diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 2b0fe65911..8c5a08b232 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -25,16 +25,16 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/features/all.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/python.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/test_reporter.yml - local: .gitlab/ci/builders/version.yml - local: .gitlab/ci/build.yml @@ -58,7 +58,7 @@ variables: SLACK_CHANNEL_OK: "#ci_gnb" SLACK_CHANNEL_FAIL: "#ci_gnb" SLACK_CHANNEL_INFO_MSG: "#ci_gnb_verbose" - AUTOREBASER_MODE: minimal + AUTOREBASER_PRS_IN_QUEUE: 1 ################################################################################ ## CI diff --git a/.gitlab/ci/build.yml b/.gitlab/ci/build.yml index be76bf4a1d..f6f2666c63 100644 --- a/.gitlab/ci/build.yml +++ b/.gitlab/ci/build.yml @@ -8,7 +8,7 @@ include: - project: softwareradiosystems/ci/srsran_project_packaging - ref: "6" + ref: "7" file: .gitlab/ci-shared/package.yml - local: .gitlab/ci/src_cache.yml @@ -79,14 +79,14 @@ variables: ################ .cache_build_set: &cache_build_set - - key: ${OS}-${COMPILER}-${BUILD_TYPE}-${AUTO_DETECT_ISA}-${ENABLE_AVX512} + - key: ${OS}-${COMPILER}-${BUILD_TYPE}-${BUILD_ARGS} paths: [ccache] policy: push .cache_build_get: &cache_build_get - - key: ${OS}-${COMPILER}-${BUILD_TYPE}-${AUTO_DETECT_ISA}-${ENABLE_AVX512} + - key: ${OS}-${COMPILER}-${BUILD_TYPE}-${BUILD_ARGS} paths: [ccache] - policy: pull + policy: pull-push ################# # Template jobs # @@ -116,6 +116,8 @@ variables: ENABLE_WERROR: "" # Empty for cmake default FORCE_DEBUG_INFO: "" # Empty for cmake default AUTO_DETECT_ISA: "" + # TEST + CTEST_TIMEOUT: 0 # CI SAVE_ARTIFACTS: "" # Empty by default KUBERNETES_CPU_REQUEST: 6 @@ -272,7 +274,11 @@ variables: CTEST_CMD="ctest -j${KUBERNETES_CPU_REQUEST} ${CTEST_SUBSET_CMD} $ctest_extra --schedule-random --output-on-failure --output-junit xunit.xml" echo "${CTEST_CMD}" echo "=============================================================================================" - $CTEST_CMD && ret=0 || ret=1 + + status_file=$(mktemp) + timeout ${CTEST_TIMEOUT} \ + bash -c "${CTEST_CMD} && echo 0 > ${status_file} || echo 1 > ${status_file}" \ + && ret=$(cat ${status_file}) || ret=124 if [[ $TEST_MODE = "coverage" ]]; then common_options="-j${KUBERNETES_CPU_REQUEST} \ @@ -301,6 +307,11 @@ variables: maxsize=$((10*1204*1024)) (( filesize > maxsize )) && echo "coverage.xml is greater than 10MB, over gitlab limit" && exit 1 fi + + if [ $ret -eq 124 ]; then + echo "The test execution exceeded the maximum allowed time !!!!" + fi + exit $ret } script: @@ -343,9 +354,9 @@ variables: BUILD_TYPE: Release ASSERT_LEVEL: PARANOID TEST_MODE: coverage - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" ENABLE_GCOV: "True" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_AVX2_TAG}"] .smoke relwithdeb: @@ -356,8 +367,8 @@ variables: BUILD_TYPE: RelWithDebInfo ASSERT_LEVEL: PARANOID TEST_MODE: default - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_AVX2_TAG}"] .smoke tsan: @@ -369,8 +380,8 @@ variables: ASSERT_LEVEL: PARANOID ENABLE_TSAN: "True" TEST_MODE: tsan - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_AVX2_TAG}"] .smoke rhel: @@ -381,8 +392,8 @@ variables: BUILD_TYPE: Release ASSERT_LEVEL: PARANOID TEST_MODE: default - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_TAG}"] .smoke archlinux: @@ -394,8 +405,8 @@ variables: BUILD_TYPE: Debug ASSERT_LEVEL: PARANOID TEST_MODE: default - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_TAG}"] .smoke dpdk: @@ -408,9 +419,9 @@ variables: ENABLE_ZEROMQ: "False" ENABLE_DPDK: "True" ASSERT_LEVEL: PARANOID - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" + AUTO_DETECT_ISA: "False" DPDK_VERSION: "23.11" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_TAG}"] .smoke valgrind: @@ -421,9 +432,8 @@ variables: BUILD_TYPE: Debug ASSERT_LEVEL: PARANOID TEST_MODE: valgrind - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "False" - BUILD_ARGS: -DEXIT_TIMEOUT=120 + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DEXIT_TIMEOUT=120 -DCMAKE_CXX_FLAGS="-march=x86-64-v3" tags: ["${AMD64_AVX2_TAG}"] .smoke avx512: @@ -434,8 +444,8 @@ variables: BUILD_TYPE: Release ASSERT_LEVEL: PARANOID TEST_MODE: default - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "True" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=x86-64-v4" tags: ["${AMD64_AVX512_TAG}"] .smoke arm: @@ -457,7 +467,8 @@ variables: BUILD_TYPE: Release ASSERT_LEVEL: PARANOID TEST_MODE: default - AUTO_DETECT_ISA: "True" + AUTO_DETECT_ISA: "False" + BUILD_ARGS: -DCMAKE_CXX_FLAGS="-march=armv8.2-a+crypto+fp16+dotprod" tags: ["${ARM64_TAG}"] # Combinations to use in schedules matrix @@ -475,7 +486,7 @@ variables: smoke relwithdeb cached: extends: .smoke relwithdeb - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -489,7 +500,7 @@ smoke relwithdeb cached: smoke tsan cached: extends: .smoke tsan stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -504,7 +515,7 @@ smoke tsan cached: smoke rhel cached: extends: .smoke rhel stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -519,7 +530,7 @@ smoke rhel cached: smoke archlinux cached: extends: .smoke archlinux stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -534,7 +545,7 @@ smoke archlinux cached: smoke dpdk cached: extends: .smoke dpdk stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -553,10 +564,10 @@ smoke dpdk cached: - lib/phy/**/* - tests/unittests/phy/**/* -smoke avx512: +smoke avx512 cached: extends: .smoke avx512 stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $ON_MR == "true" && $CI_MERGE_REQUEST_APPROVED == "true" changes: @@ -566,11 +577,14 @@ smoke avx512: <<: *instruction_set_changes when: manual allow_failure: false + cache: + - !reference [.fetch_src_cache, cache] + - *cache_build_get smoke arm cached: extends: .smoke arm stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -589,7 +603,7 @@ smoke arm cached: smoke arm neon cached: extends: .smoke arm neon stage: manual - timeout: 45 min + timeout: 1 hour rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ when: never @@ -632,6 +646,13 @@ smoke dpdk clean: rules: - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ +smoke avx512 clean: + extends: .smoke avx512 + rules: + - if: $CI_MERGE_REQUEST_LABELS =~ /no-cache/ + changes: + <<: *instruction_set_changes + smoke arm clean: extends: .smoke arm rules: @@ -652,14 +673,13 @@ intermediate commits: extends: .smoke release rules: - if: $ON_MR - timeout: 45 min + timeout: 1 hour variables: SAVE_ARTIFACTS: "True" script: - git config advice.detachedHead false - git fetch origin --depth=20 $CI_MERGE_REQUEST_TARGET_BRANCH_NAME $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - | - TEST_MODE=none for rev in $(git rev-list --reverse origin/$CI_MERGE_REQUEST_TARGET_BRANCH_NAME..origin/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME) do echo "##################################################" @@ -668,13 +688,6 @@ intermediate commits: git checkout $rev build_srsgnb done - - | - TEST_MODE=coverage - git checkout $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME - echo "##################################################" - echo "#### $CI_MERGE_REQUEST_SOURCE_BRANCH_NAME ####" - echo "##################################################" - build_srsgnb - launch_tests cache: - !reference [.fetch_src_cache, cache] @@ -683,6 +696,8 @@ intermediate commits: valgrind changed tests: extends: .smoke valgrind stage: manual + allow_failure: + exit_codes: 124 # timeout command's exit code when the time is reached rules: - if: $CI_MERGE_REQUEST_LABELS =~ /urgent/ when: never @@ -690,10 +705,10 @@ valgrind changed tests: - if: $ON_MR when: manual allow_failure: true - timeout: 1h variables: CLEAN_BUILD: "False" FINGERPRINT: "fingerprints.csv" + CTEST_TIMEOUT: 20m SAVE_ARTIFACTS: "True" script: - git config advice.detachedHead false @@ -713,6 +728,7 @@ valgrind changed tests: echo "##################################################" git checkout origin/$CI_MERGE_REQUEST_SOURCE_BRANCH_NAME build_srsgnb + - echo "This test execution has a timeout of ${CTEST_TIMEOUT}. If the execution excess that timer, the job will be marked as allowed_to_fail. This will avoid the job to have a huge duration in a MR pipeline." - launch_tests cache: - !reference [.fetch_src_cache, cache] @@ -820,11 +836,18 @@ smoke valgrind update cache: cache: - !reference [.fetch_src_cache, cache] - *cache_build_set - variables: - SAVE_ARTIFACTS: "True" # Valgrind generates extra files - artifacts: - <<: *build_artifacts - expire_in: 3 day + +smoke avx512 update cache: + extends: .smoke avx512 + rules: + - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ + when: delayed + start_in: 30 minutes + interruptible: false + retry: 2 + cache: + - !reference [.fetch_src_cache, cache] + - *cache_build_set smoke arm update cache: extends: .smoke arm @@ -881,8 +904,8 @@ package: variables: &package_variables PROJECT_NAME: srsran-project RELEASE_VERSION: "99.9" - KUBERNETES_CPU_REQUEST: 7 - KUBERNETES_CPU_LIMIT: 7 + KUBERNETES_CPU_REQUEST: 6 + KUBERNETES_CPU_LIMIT: 6 KUBERNETES_MEMORY_REQUEST: 12Gi KUBERNETES_MEMORY_LIMIT: 12Gi DEB_BUILD_OPTIONS: parallel=${KUBERNETES_CPU_LIMIT} @@ -1018,14 +1041,17 @@ rhel-8 amd64 avx2: # Basic AMD NO_ISA / AVX512 ubuntu-24.04 amd64 no isa release: - extends: .smoke release + extends: .build_and_unit rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: delayed start_in: 150 minutes interruptible: false variables: - ENABLE_GCOV: "False" + OS: ubuntu-24.04 + COMPILER: gcc + BUILD_TYPE: Release + ASSERT_LEVEL: PARANOID TEST_MODE: default AUTO_DETECT_ISA: "False" tags: ["${AMD64_TAG}"] @@ -1034,14 +1060,17 @@ ubuntu-24.04 amd64 no isa release: - COMPILER: [gcc, clang] ubuntu-24.04 amd64 avx512 release: - extends: .smoke release + extends: .build_and_unit rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: delayed start_in: 120 minutes interruptible: false variables: - ENABLE_GCOV: "False" + OS: ubuntu-24.04 + COMPILER: gcc + BUILD_TYPE: Release + ASSERT_LEVEL: PARANOID TEST_MODE: default AUTO_DETECT_ISA: "True" ENABLE_AVX512: "True" @@ -1053,14 +1082,17 @@ ubuntu-24.04 amd64 avx512 release: # Basic ARM NO_ISA / NEON ubuntu-24.04 arm no isa release: - extends: .smoke release + extends: .build_and_unit rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: delayed start_in: 150 minutes interruptible: false variables: - ENABLE_GCOV: "False" + OS: ubuntu-24.04 + COMPILER: gcc + BUILD_TYPE: Release + ASSERT_LEVEL: PARANOID TEST_MODE: default AUTO_DETECT_ISA: "False" tags: ["${ARM64_TAG}"] @@ -1069,14 +1101,17 @@ ubuntu-24.04 arm no isa release: - COMPILER: [gcc, clang] ubuntu-24.04 arm neon release: - extends: .smoke release + extends: .build_and_unit rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ when: delayed start_in: 120 minutes interruptible: false variables: - ENABLE_GCOV: "False" + OS: ubuntu-24.04 + COMPILER: gcc + BUILD_TYPE: Release + ASSERT_LEVEL: PARANOID TEST_MODE: default AUTO_DETECT_ISA: "True" tags: ["${ARM64_TAG}"] @@ -1086,7 +1121,7 @@ ubuntu-24.04 arm neon release: # Basic DPDK -smoke dpdk: +ubuntu dpdk: extends: .build_and_unit rules: - if: $CI_DESCRIPTION =~ /Nightly Build Unit Tests/ @@ -1269,7 +1304,9 @@ debian 11 amd64 avx512: BUILD_TYPE: Debug parallel: matrix: - - OS: [ubuntu-24.04, ubuntu-23.10, ubuntu-22.04] # ubuntu-20.04 disabled due to https://bugs.launchpad.net/ubuntu/+source/gcc-9/+bug/2029910 + # ubuntu-20.04 disabled due to https://bugs.launchpad.net/ubuntu/+source/gcc-9/+bug/2029910 + # ubuntu-22.04 disabled due to https://github.com/google/sanitizers/issues/1259#issuecomment-642312392 + - OS: [ubuntu-24.04, ubuntu-23.10] SANITIZER: tsan COMPILER: [gcc, clang] ENABLE_TSAN: "True" @@ -1288,7 +1325,6 @@ debian 11 amd64 avx512: SANITIZER: valgrind COMPILER: gcc TEST_MODE: valgrind - SAVE_ARTIFACTS: "True" sanitizers amd64 no isa: extends: .weekly sanitizers @@ -1310,7 +1346,9 @@ sanitizers amd64 avx512: tags: ["${AMD64_AVX512_TAG}"] parallel: matrix: - - OS: [ubuntu-24.04, ubuntu-23.10, ubuntu-22.04] # ubuntu-20.04 disabled due to https://bugs.launchpad.net/ubuntu/+source/gcc-9/+bug/2029910 + # ubuntu-20.04 disabled due to https://bugs.launchpad.net/ubuntu/+source/gcc-9/+bug/2029910 + # ubuntu-22.04 disabled due to https://github.com/google/sanitizers/issues/1259#issuecomment-642312392 + - OS: [ubuntu-24.04, ubuntu-23.10] SANITIZER: tsan COMPILER: [gcc, clang] ENABLE_TSAN: "True" @@ -1943,35 +1981,9 @@ basic avx512 dpdk: expire_in: 3 day basic avx512 dpdk withassert: - extends: .build_and_unit - rules: - - if: $CI_DESCRIPTION =~ /Nightly E2E Tests/ - when: delayed - start_in: 2 hours - retry: 2 - interruptible: false + extends: basic avx512 dpdk variables: - OS: ubuntu-24.04 - COMPILER: gcc - BUILD_TYPE: Release - TEST_MODE: none - ENABLE_UHD: "False" - ENABLE_ZEROMQ: "False" - ENABLE_DPDK: "True" - DPDK_VERSION: "23.11" - AUTO_DETECT_ISA: "True" - ENABLE_AVX512: "True" - FORCE_DEBUG_INFO: "True" ASSERT_LEVEL: NORMAL - SAVE_ARTIFACTS: "True" - KUBERNETES_CPU_REQUEST: 14 - KUBERNETES_CPU_LIMIT: 14 - KUBERNETES_MEMORY_REQUEST: 20Gi - KUBERNETES_MEMORY_LIMIT: 20Gi - tags: ["${AMD64_VIAVI_BUILDER_TAG}"] - artifacts: - <<: *build_artifacts - expire_in: 3 day ####### # Web # diff --git a/.gitlab/ci/builders.yml b/.gitlab/ci/builders.yml index 025c661021..4307ee8b06 100644 --- a/.gitlab/ci/builders.yml +++ b/.gitlab/ci/builders.yml @@ -8,13 +8,13 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/docker.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/python.yml - local: .gitlab/ci/builders/version.yml - local: .gitlab/ci/src_cache.yml @@ -58,8 +58,8 @@ ubuntu-uhd-builder: arch_name: "" uhd_version: "" GIT_STRATEGY: none - KUBERNETES_CPU_REQUEST: 3 - KUBERNETES_CPU_LIMIT: 3 + KUBERNETES_CPU_REQUEST: 4 + KUBERNETES_CPU_LIMIT: 4 KUBERNETES_MEMORY_REQUEST: 7Gi KUBERNETES_MEMORY_LIMIT: 7Gi before_script: diff --git a/.gitlab/ci/builders/archlinux/Dockerfile b/.gitlab/ci/builders/archlinux/Dockerfile index e80d25ad39..c368895b37 100644 --- a/.gitlab/ci/builders/archlinux/Dockerfile +++ b/.gitlab/ci/builders/archlinux/Dockerfile @@ -28,7 +28,7 @@ ADD builder.sh /usr/local/bin RUN chmod +x /usr/local/bin/builder.sh RUN python -m venv /usr/local/builder_tools -RUN /usr/local/builder_tools/bin/pip install "pandas <3" "psutil < 6" +RUN /usr/local/builder_tools/bin/pip install "pandas < 3" "psutil < 6" ADD changed_tests.py /usr/local/bin RUN chmod +x /usr/local/bin/changed_tests.py ADD ram_reporter.py /usr/local/bin diff --git a/.gitlab/ci/builders/builder.sh b/.gitlab/ci/builders/builder.sh index 7463a38b38..bc71303bf7 100755 --- a/.gitlab/ci/builders/builder.sh +++ b/.gitlab/ci/builders/builder.sh @@ -187,7 +187,9 @@ fi (which ccache >/dev/null) && CCACHE_CMAKE_ARGS="-DCMAKE_C_COMPILER_LAUNCHER=ccache -DCMAKE_CXX_COMPILER_LAUNCHER=ccache" || CCACHE_CMAKE_ARGS="" # Build process +ccache -z || true mkdir -p "$BUILD_FOLDER" cd "$BUILD_FOLDER" || exit cmake $CCACHE_CMAKE_ARGS "$@" .. make $MAKE_EXTRA +ccache -sv || true diff --git a/.gitlab/ci/builders/debian/Dockerfile b/.gitlab/ci/builders/debian/Dockerfile index f3d7c6129f..2ea52e09d3 100644 --- a/.gitlab/ci/builders/debian/Dockerfile +++ b/.gitlab/ci/builders/debian/Dockerfile @@ -6,7 +6,7 @@ # the distribution. # -ARG VERSION=22.04 +ARG VERSION=24.04 ARG OS_NAME=ubuntu FROM $OS_NAME:$VERSION diff --git a/.gitlab/ci/builders/ram_reporter.py b/.gitlab/ci/builders/ram_reporter.py index 807f2fb148..356117e4ce 100755 --- a/.gitlab/ci/builders/ram_reporter.py +++ b/.gitlab/ci/builders/ram_reporter.py @@ -9,19 +9,44 @@ """ Write RAM usage to a file until the process is killed """ - -import sys import time - +import os +import sys import psutil +def get_cgroup_memory_path(): + try: + with open('/proc/self/cgroup', 'r') as f: + lines = f.readlines() + for line in lines: + parts = line.strip().split(':') + if len(parts) == 3: + cgroup_path = parts[2].strip() + memory_path = f"/sys/fs/cgroup{cgroup_path}/memory.current" + if os.path.exists(memory_path): + return memory_path + return None + except Exception as e: + print(f"Error to read /proc/self/cgroup: {e}") + return None + +def get_memory_usage(memory_path): + try: + with open(memory_path, 'r') as f: + memory_usage = int(f.read().strip()) + return memory_usage + except FileNotFoundError: + print(f"The file {memory_path} not found. Make sure that you use cgroups v2.") + return None + except Exception as e: + print(f"Error to read {memory_path}: {e}") + return None def _bytes_to_gb(size_in_bytes): gb = size_in_bytes / (1024**3) return gb - -def _main(): +def write_mem_baremetal(): filename = sys.argv[1] peak_ram_usage = 0 @@ -44,6 +69,40 @@ def _main(): time.sleep(0.5) +def write_mem_kubernetes(): + memory_path = get_cgroup_memory_path() + if not memory_path: + print("Not cgroup memory path.") + return + + filename = sys.argv[1] + peak_ram_usage = 0 + + try: + with open(filename, "r", encoding="utf-8") as file: + previous_content = file.read() + previous_peak_ram_usage = float(previous_content) + peak_ram_usage = previous_peak_ram_usage + except FileNotFoundError: + peak_ram_usage = 0 + + while True: + current_ram_usage = get_memory_usage(memory_path) + if current_ram_usage is not None: + peak_ram_usage = max(peak_ram_usage, current_ram_usage) + + peak_ram_usage_gb = _bytes_to_gb(peak_ram_usage) + + with open(filename, "w", encoding="utf-8") as file: + file.write(f"{peak_ram_usage_gb:.2f}") + time.sleep(0.5) + if __name__ == "__main__": - _main() + try: + if len(sys.argv) == 3 and sys.argv[2] == "baremetal": + write_mem_baremetal() + else: + write_mem_kubernetes() + except Exception as e: + print(f"Error: {e}") \ No newline at end of file diff --git a/.gitlab/ci/docker.yml b/.gitlab/ci/docker.yml index 132ec45df0..ac51d4bc0b 100644 --- a/.gitlab/ci/docker.yml +++ b/.gitlab/ci/docker.yml @@ -8,13 +8,13 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/python.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/tools/docker.yml ################################################################################ diff --git a/.gitlab/ci/e2e.yml b/.gitlab/ci/e2e.yml index 33744ab0cc..113de81351 100644 --- a/.gitlab/ci/e2e.yml +++ b/.gitlab/ci/e2e.yml @@ -8,7 +8,7 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml variables: @@ -118,8 +118,8 @@ e2e request and config validation: KUBERNETES_CPU_LIMIT: 2 KUBERNETES_MEMORY_REQUEST: 2Gi KUBERNETES_MEMORY_LIMIT: 2Gi - KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "12G" - KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "12G" + KUBERNETES_EPHEMERAL_STORAGE_REQUEST: "20G" + KUBERNETES_EPHEMERAL_STORAGE_LIMIT: "20G" KUBECONFIG_VAR_NAME: "RETINA_NAMESPACE_KUBECONFIG" GROUP: zmq tags: @@ -304,7 +304,6 @@ amari 32UE: amari 32UE beta: extends: amari 32UE - allow_failure: true parallel: matrix: - KEYWORDS: ["reestablishment", "handover"] @@ -503,6 +502,7 @@ android n300: viavi: stage: rf extends: .e2e-run + timeout: 8h variables: GROUP: "viavi" TESTBED: "viavi" @@ -514,6 +514,17 @@ viavi: - job: "basic avx512 dpdk" artifacts: true - *retina-needs + parallel: + matrix: + - KEYWORDS: + [ + "fading and 32UE", + "fading and 1UE", + "ideal and 32UE", + "ideal and 1UE", + "birth-death and 32UE", + "birth-death and 1UE", + ] viavi-debug: stage: rf diff --git a/.gitlab/ci/e2e/.env b/.gitlab/ci/e2e/.env index b496cd4a7e..5e4eba4235 100644 --- a/.gitlab/ci/e2e/.env +++ b/.gitlab/ci/e2e/.env @@ -1,6 +1,6 @@ SRSGNB_REGISTRY_URI=registry.gitlab.com/softwareradiosystems/srsgnb RETINA_REGISTRY_PREFIX=registry.gitlab.com/softwareradiosystems/ci/retina -RETINA_VERSION=0.49.3 +RETINA_VERSION=0.49.6 UBUNTU_VERSION=24.04 AMARISOFT_VERSION=2023-09-08 SRSUE_VERSION=23.11 diff --git a/.gitlab/ci/release.yml b/.gitlab/ci/release.yml index 3aa03f937b..dbcc772cd0 100644 --- a/.gitlab/ci/release.yml +++ b/.gitlab/ci/release.yml @@ -8,10 +8,10 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/features/all.yml stages: diff --git a/.gitlab/ci/schedules.yml b/.gitlab/ci/schedules.yml index ada7e26243..0d55b486f4 100644 --- a/.gitlab/ci/schedules.yml +++ b/.gitlab/ci/schedules.yml @@ -9,72 +9,85 @@ Rebaser: cron: "*/10 6-22 * * 1-5" cron_timezone: "Europe/Madrid" - ref: dev - active: true + ref: refs/heads/dev variables: - variable_type: env_var key: CI_DESCRIPTION value: "Rebaser" + raw: false - variable_type: env_var key: NOTIFY_SLACK value: "false" + raw: false Nightly Build Unit Tests: cron: "45 23 * * 0-5" cron_timezone: "Europe/Madrid" - ref: dev + ref: refs/heads/dev variables: - variable_type: env_var key: CI_DESCRIPTION value: "Nightly Build Unit Tests" + raw: false - variable_type: env_var key: NOTIFY_SLACK value: "true" + raw: false - variable_type: env_var key: SLACK_CHANNEL_OK value: "#ci_gnb" + raw: false Nightly E2E Tests: cron: "00 23 * * 0-5" cron_timezone: "Europe/Madrid" - ref: dev + ref: refs/heads/dev variables: - variable_type: env_var key: CI_DESCRIPTION value: "Nightly E2E Tests" + raw: false - variable_type: env_var key: NOTIFY_SLACK value: "true" + raw: false - variable_type: env_var key: SLACK_CHANNEL_OK value: "#ci_gnb" + raw: false Weekly: cron: "00 13 * * 6" cron_timezone: "Europe/Madrid" - ref: dev + ref: refs/heads/dev variables: - variable_type: env_var key: CI_DESCRIPTION value: "Weekly" + raw: false - variable_type: env_var key: NOTIFY_SLACK value: "true" + raw: false - variable_type: env_var key: SLACK_CHANNEL_OK value: "#ci_gnb" + raw: false Alternative OSs: cron: "00 10 * * 6" cron_timezone: "Europe/Madrid" - ref: dev + ref: refs/heads/dev variables: - variable_type: env_var key: CI_DESCRIPTION value: "Alternative OSs" + raw: false - variable_type: env_var key: NOTIFY_SLACK value: "true" + raw: false - variable_type: env_var key: SLACK_CHANNEL_OK value: "#ci_gnb" + raw: false diff --git a/.gitlab/ci/trx.yml b/.gitlab/ci/trx.yml index 3ee7c4e939..a2cd862521 100644 --- a/.gitlab/ci/trx.yml +++ b/.gitlab/ci/trx.yml @@ -8,7 +8,7 @@ include: - project: softwareradiosystems/ci/tools - ref: "17" + ref: "18" file: .gitlab/ci-shared/setup/all.yml - local: .gitlab/ci/build.yml @@ -58,7 +58,7 @@ build trx driver: rm -Rf build mkdir build cd build - cmake -DENABLE_TRX_DRIVER=True -DTRX_DRIVER_DIR=${CI_PROJECT_DIR}/amarisoft/trx_uhd-linux -DENABLE_EXPORT=True -DENABLE_UHD=False -DENABLE_ZEROMQ=True -DAUTO_DETECT_ISA=True -DENABLE_AVX512=False .. + cmake -DENABLE_TRX_DRIVER=True -DTRX_DRIVER_DIR=${CI_PROJECT_DIR}/amarisoft/trx_uhd-linux -DENABLE_EXPORT=True -DENABLE_UHD=False -DENABLE_ZEROMQ=True -DAUTO_DETECT_ISA=False -DCMAKE_CXX_FLAGS="-march=x86-64-v3" .. make -j${KUBERNETES_CPU_REQUEST} trx_srsran_test } - | diff --git a/CMakeLists.txt b/CMakeLists.txt index 221fc47b80..a717cb82ad 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -300,7 +300,6 @@ if (AUTO_DETECT_ISA) if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") set(GCC_ARCH armv8-a CACHE STRING "GCC compile for specific architecture.") message(STATUS "Detected aarch64 processor") - add_definitions(-DHAVE_NEON) set(HAVE_NEON True CACHE BOOL "NEON Instruction set is supported") else (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") set(GCC_ARCH native CACHE STRING "GCC compile for specific architecture.") @@ -312,23 +311,18 @@ if (AUTO_DETECT_ISA) ADD_CXX_COMPILER_FLAG_IF_AVAILABLE("-march=${GCC_ARCH}" HAVE_MARCH) if (HAVE_AVX2) - add_definitions(-DHAVE_AVX2) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx2") endif (HAVE_AVX2) if (HAVE_AVX) - add_definitions(-DHAVE_AVX) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx") endif (HAVE_AVX) if (HAVE_SSE) - add_definitions(-DHAVE_SSE) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse4.1") endif (HAVE_SSE) if (HAVE_FMA) - add_definitions(-DHAVE_FMA) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfma") endif (HAVE_FMA) if (HAVE_AVX512) - add_definitions(-DHAVE_AVX512) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mavx512f -mavx512cd -mavx512bw -mavx512dq") endif (HAVE_AVX512) else (AUTO_DETECT_ISA) diff --git a/COPYRIGHT b/COPYRIGHT index 85bc423ca7..55d0aec68a 100644 --- a/COPYRIGHT +++ b/COPYRIGHT @@ -40,11 +40,6 @@ Copyright: 2017-2023 University of Cincinnati, developed by Henry Schreiner License: BSD-3-clause -Files: external/variant/variant.hpp -Copyright: 2015-2017 Michael Park -License: Boost Software License, Version 1.0 - - Files: external/rigtorp/MPMCQueue.hpp Copyright: 2018 Erik Rigtorp License: MIT diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt index f532fc36ea..5262b3c920 100644 --- a/apps/CMakeLists.txt +++ b/apps/CMakeLists.txt @@ -19,6 +19,8 @@ # add_subdirectory(examples) +add_subdirectory(cu) add_subdirectory(gnb) +add_subdirectory(du) add_subdirectory(units) add_subdirectory(services) diff --git a/apps/cu/CMakeLists.txt b/apps/cu/CMakeLists.txt new file mode 100644 index 0000000000..e5da7b156e --- /dev/null +++ b/apps/cu/CMakeLists.txt @@ -0,0 +1,48 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(srscu + cu.cpp + cu_appconfig_cli11_schema.cpp + cu_worker_manager.cpp +) + +install(TARGETS srscu + RUNTIME) + +target_link_libraries(srscu + srsran_app_services + srsran_support + srsran_version + srsran_build_info + srsran_cu_cp_app_unit + srsran_cu_up_app_unit + srsran_ngap + srsran_f1c_gateway + srsran_e1_gateway + srsgnb_app_f1u_cu_up_split_connector + srsran_pcap + ngap_asn1 + ) + +add_backward(srscu) + +target_include_directories(srscu PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/external) + diff --git a/apps/cu/cu.cpp b/apps/cu/cu.cpp new file mode 100644 index 0000000000..44bd3e0666 --- /dev/null +++ b/apps/cu/cu.cpp @@ -0,0 +1,488 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/cu_up/cu_up.h" +#include "srsran/cu_up/cu_up_factory.h" +#include "srsran/f1ap/gateways/f1c_network_server_factory.h" +#include "srsran/f1u/cu_up/split_connector/f1u_split_connector.h" +#include "srsran/gateways/udp_network_gateway.h" +#include "srsran/gtpu/gtpu_config.h" +#include "srsran/gtpu/gtpu_demux_factory.h" +#include "srsran/gtpu/ngu_gateway.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/support/backtrace.h" +#include "srsran/support/build_info/build_info.h" +#include "srsran/support/config_parsers.h" +#include "srsran/support/cpu_features.h" +#include "srsran/support/error_handling.h" +#include "srsran/support/event_tracing.h" +#include "srsran/support/io/io_broker.h" +#include "srsran/support/io/io_broker_factory.h" +#include "srsran/support/io/io_timer_source.h" +#include "srsran/support/signal_handler.h" +#include "srsran/support/sysinfo.h" +#include "srsran/support/timers.h" +#include "srsran/support/version/version.h" + +#include "apps/cu/cu_appconfig_cli11_schema.h" +#include "apps/cu/cu_worker_manager.h" +#include "apps/services/console_helper.h" +#include "apps/services/metrics_log_helper.h" +#include "apps/units/cu_cp/cu_cp_builder.h" +#include "apps/units/cu_cp/cu_cp_logger_registrator.h" +#include "apps/units/cu_cp/cu_cp_unit_config.h" +#include "apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h" +#include "apps/units/cu_cp/cu_cp_unit_config_validator.h" +#include "apps/units/cu_cp/cu_cp_unit_logger_config.h" +#include "apps/units/cu_up/cu_up_builder.h" +#include "apps/units/cu_up/cu_up_logger_registrator.h" +#include "apps/units/cu_up/cu_up_unit_config.h" +#include "apps/units/cu_up/cu_up_unit_config_cli11_schema.h" +#include "apps/units/cu_up/cu_up_unit_config_translators.h" +#include "apps/units/cu_up/cu_up_unit_config_validator.h" + +// TODO remove apps/gnb/*.h +#include "apps/gnb/adapters/e2_gateway_remote_connector.h" +#include "apps/gnb/gnb_appconfig_translators.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" + +#include "apps/units/cu_up/cu_up_wrapper.h" +#include "cu_appconfig.h" + +#include +#include + +using namespace srsran; + +/// \file +/// \brief Application of a Central Unit (CU) with combined CU control-plane (CU-CP) and CU user-plane (CU-UP). +/// +/// This application runs a CU without the E1 connection between the CU-CP and CU-UP going over a real SCTP +/// connection. However, its does expose the F1, N2 and N3 interface to the DU, AMF and UPF over the standard +/// UDP/SCTP ports. +/// +/// The app serves as an example for an all-integrated CU. +/// +/// \cond + +static std::string config_file; + +static std::atomic is_running = {true}; +const int MAX_CONFIG_FILES(10); + +static void populate_cli11_generic_args(CLI::App& app) +{ + fmt::memory_buffer buffer; + format_to(buffer, "srsRAN 5G CU version {} ({})", get_version(), get_build_hash()); + app.set_version_flag("-v,--version", srsran::to_c_str(buffer)); + app.set_config("-c,", config_file, "Read config from file", false)->expected(1, MAX_CONFIG_FILES); +} + +static void local_signal_handler() +{ + is_running = false; +} + +static void initialize_log(const std::string& filename) +{ + srslog::sink* log_sink = (filename == "stdout") ? srslog::create_stdout_sink() : srslog::create_file_sink(filename); + if (log_sink == nullptr) { + report_error("Could not create application main log sink.\n"); + } + srslog::set_default_sink(*log_sink); + srslog::init(); +} + +static void register_app_logs(const log_appconfig& log_cfg, + const cu_cp_unit_logger_config& cu_cp_loggers, + const cu_up_unit_logger_config& cu_up_loggers) +{ + // Set log-level of app and all non-layer specific components to app level. + for (const auto& id : {"CU", "ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { + auto& logger = srslog::fetch_basic_logger(id, false); + logger.set_level(srslog::str_to_basic_level(log_cfg.lib_level)); + logger.set_hex_dump_max_size(log_cfg.hex_max_size); + } + + auto& config_logger = srslog::fetch_basic_logger("CONFIG", false); + config_logger.set_level(srslog::str_to_basic_level(log_cfg.config_level)); + config_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + auto& metrics_logger = srslog::fetch_basic_logger("METRICS", false); + metrics_logger.set_level(srslog::str_to_basic_level(log_cfg.metrics_level)); + metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + auto& e2ap_logger = srslog::fetch_basic_logger("E2AP", false); + e2ap_logger.set_level(srslog::str_to_basic_level(log_cfg.e2ap_level)); + e2ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + // Register units logs. + register_cu_cp_loggers(cu_cp_loggers); + register_cu_up_loggers(cu_up_loggers); +} + +// Temporary helper to create CU-UP. +// TODO remove +std::unique_ptr app_build_cu_up(const cu_up_unit_config& unit_cfg, + cu_worker_manager& workers, + const std::string& f1u_bind_addr, + srs_cu_up::e1_connection_client& e1_conn_client, + f1u_cu_up_gateway& f1u_gateway, + dlt_pcap& gtpu_pcap, + timer_manager& timers, + io_broker& io_brk); + +int main(int argc, char** argv) +{ + // Set signal handler. + register_signal_handler(local_signal_handler); + + // Enable backtrace. + enable_backtrace(); + + // Setup and configure config parsing. + CLI::App app("srsCU application"); + app.config_formatter(create_yaml_config_parser()); + app.allow_config_extras(CLI::config_extras_mode::error); + // Fill the generic application arguments to parse. + populate_cli11_generic_args(app); + + // Configure CLI11 with the CU application configuration schema. + cu_appconfig cu_cfg; + configure_cli11_with_cu_appconfig_schema(app, cu_cfg); + + cu_cp_unit_config cu_cp_config; + configure_cli11_with_cu_cp_unit_config_schema(app, cu_cp_config); + + cu_up_unit_config cu_up_config; + configure_cli11_with_cu_up_unit_config_schema(app, cu_up_config); + + // Set the callback for the app calling all the autoderivation functions. + app.callback([&app, &cu_cp_config]() { + // Create the PLMN and TAC list from the cells. + // TODO remove hard-coded value + std::vector plmns; + std::vector tacs; + plmns.emplace_back("00101"); + tacs.emplace_back(7); + + autoderive_cu_cp_parameters_after_parsing(app, cu_cp_config, std::move(plmns), std::move(tacs)); + }); + + // Parse arguments. + CLI11_PARSE(app, argc, argv); + + // Check the modified configuration. + if (!validate_cu_cp_unit_config(cu_cp_config) || !validate_cu_up_unit_config(cu_up_config)) { + report_error("Invalid configuration detected.\n"); + } + + // Set up logging. + initialize_log(cu_cfg.log_cfg.filename); + register_app_logs(cu_cfg.log_cfg, cu_cp_config.loggers, cu_up_config.loggers); + + // Log input configuration. + srslog::basic_logger& config_logger = srslog::fetch_basic_logger("CONFIG"); + if (config_logger.debug.enabled()) { + config_logger.debug("Input configuration (all values): \n{}", app.config_to_str(true, false)); + } else { + config_logger.info("Input configuration (only non-default values): \n{}", app.config_to_str(false, false)); + } + + srslog::basic_logger& cu_logger = srslog::fetch_basic_logger("CU"); + if (not cu_cfg.log_cfg.tracing_filename.empty()) { + cu_logger.info("Opening event tracer..."); + open_trace_file(cu_cfg.log_cfg.tracing_filename); + cu_logger.info("Event tracer opened successfully"); + } + + // configure cgroups + // TODO + + // Setup size of byte buffer pool. + // TODO byte_buffer_pool + + // Log build info + cu_logger.info("Built in {} mode using {}", get_build_mode(), get_build_info()); + + // Log CPU architecture. + // TODO + + // Check and log included CPU features and check support by current CPU + if (cpu_supports_included_features()) { + cu_logger.debug("Required CPU features: {}", get_cpu_feature_info()); + } else { + // Quit here until we complete selection of the best matching implementation for the current CPU at runtime. + cu_logger.error("The CPU does not support the required CPU features that were configured during compile time: {}", + get_cpu_feature_info()); + report_error("The CPU does not support the required CPU features that were configured during compile time: {}\n", + get_cpu_feature_info()); + } + + // Check some common causes of performance issues and print a warning if required. + check_cpu_governor(cu_logger); + check_drm_kms_polling(cu_logger); + + // Create worker manager. + cu_worker_manager workers{cu_cfg, cu_up_config.gtpu_queue_size}; + + // Create layer specific PCAPs. + // TODO: + // 1. modules::...create_pcap does not use the custom cu_worker. + // 2. modules::flexible_du... for creating F1AP pcap. + // Initializing PCAPs direclty. + std::unique_ptr ngap_p = create_null_dlt_pcap(); + std::vector> cu_up_pcaps(2); + cu_up_pcaps[0] = create_null_dlt_pcap(); + cu_up_pcaps[1] = create_null_dlt_pcap(); + std::unique_ptr f1ap_p = create_null_dlt_pcap(); + std::unique_ptr e2ap_p = + cu_cfg.cu_cp_pcap_cfg.e2ap.enabled + ? create_e2ap_pcap(cu_cfg.cu_cp_pcap_cfg.e2ap.filename, workers.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + + // Create IO broker. + const auto& low_prio_cpu_mask = cu_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; + io_broker_config io_broker_cfg(low_prio_cpu_mask); + std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll, io_broker_cfg); + + // Create F1-C GW (TODO cleanup port and PPID args with factory) + sctp_network_gateway_config f1c_sctp_cfg = {}; + f1c_sctp_cfg.bind_address = cu_cp_config.f1ap_config.f1c_bind_address; + f1c_sctp_cfg.bind_port = 38471; + f1c_sctp_cfg.ppid = F1AP_PPID; + f1c_cu_sctp_gateway_config f1c_server_cfg({f1c_sctp_cfg, *epoll_broker, *ngap_p}); + std::unique_ptr cu_f1c_gw = srsran::create_f1c_gateway_server(f1c_server_cfg); + + // Create F1-U GW (TODO factory and cleanup). + gtpu_demux_creation_request cu_f1u_gtpu_msg = {}; + cu_f1u_gtpu_msg.cfg.warn_on_drop = true; + cu_f1u_gtpu_msg.gtpu_pcap = cu_up_pcaps[1].get(); // FIXME use right enum + std::unique_ptr cu_f1u_gtpu_demux = create_gtpu_demux(cu_f1u_gtpu_msg); + udp_network_gateway_config cu_f1u_gw_config = {}; + cu_f1u_gw_config.bind_address = cu_cfg.f1u_cfg.f1u_bind_addr; + cu_f1u_gw_config.bind_port = GTPU_PORT; + cu_f1u_gw_config.reuse_addr = true; + std::unique_ptr cu_f1u_gw = + srs_cu_up::create_udp_ngu_gateway(cu_f1u_gw_config, *epoll_broker, *workers.cu_up_io_ul_exec); + std::unique_ptr cu_f1u_conn = + std::make_unique(cu_f1u_gw.get(), cu_f1u_gtpu_demux.get(), *cu_up_pcaps[1].get()); + + // Create E1AP local connector + std::unique_ptr e1_gw = create_e1_local_connector(e1_local_connector_config{*cu_up_pcaps[0]}); + + // Create manager of timers for CU-CP and CU-UP, which will be + // driven by the system timer slot ticks. + // TODO revisit how to use the system timer timer source. + timer_manager app_timers{256}; + timer_manager* cu_timers = &app_timers; + + // Create time source that ticks the timers + io_timer_source time_source{app_timers, *epoll_broker, std::chrono::milliseconds{1}}; + + // Set up the JSON log channel used by metrics. + // TODO metrics. Do we have any CU-CP or CU-UP JSON metrics? + + // Create NGAP Gateway. + // TODO had to include gnb + std::unique_ptr ngap_adapter; + { + using no_core_mode_t = srs_cu_cp::n2_connection_client_config::no_core; + using network_mode_t = srs_cu_cp::n2_connection_client_config::network; + using ngap_mode_t = std::variant; + + // TODO generate network config in helper function, not in apps/gnb + srsran::sctp_network_connector_config n2_nw_cfg; + n2_nw_cfg.connection_name = "AMF"; + n2_nw_cfg.connect_address = cu_cp_config.amf_cfg.ip_addr; + n2_nw_cfg.connect_port = cu_cp_config.amf_cfg.port; + if (cu_cp_config.amf_cfg.n2_bind_addr == "auto") { + n2_nw_cfg.bind_address = cu_cp_config.amf_cfg.bind_addr; + } else { + n2_nw_cfg.bind_address = cu_cp_config.amf_cfg.n2_bind_addr; + } + n2_nw_cfg.bind_interface = cu_cp_config.amf_cfg.n2_bind_interface; + n2_nw_cfg.ppid = NGAP_PPID; + + ngap_adapter = srs_cu_cp::create_n2_connection_client(srs_cu_cp::n2_connection_client_config{ + *ngap_p, + cu_cp_config.amf_cfg.no_core ? ngap_mode_t{no_core_mode_t{}} + : ngap_mode_t{network_mode_t{*epoll_broker, n2_nw_cfg}}}); + } + + // E2AP configuration. + // Create E2AP GW remote connector. + // TODO This seems to be used in the DU only? + + // Create CU-CP config. + cu_cp_build_dependencies cu_cp_dependencies; + cu_cp_dependencies.cu_cp_executor = workers.cu_cp_exec; + cu_cp_dependencies.cu_cp_e2_exec = workers.cu_cp_e2_exec; + cu_cp_dependencies.ngap_notifier = ngap_adapter.get(); + cu_cp_dependencies.timers = cu_timers; + + // create CU-CP. + std::unique_ptr cu_cp_obj = build_cu_cp(cu_cp_config, cu_cp_dependencies); + + // TODO: Remove JSON sink and refactor console_helper to not require it upon construction + // Set up the JSON log channel used by metrics. + srslog::sink& json_sink = srslog::fetch_udp_sink("127.0.9.9", 61234, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(false); + + // Create console helper object for commands and metrics printing. + console_helper console(*epoll_broker, json_channel, cu_cp_obj->get_command_handler()); + console.on_app_starting(); + + // Create metrics log helper. + metrics_log_helper metrics_logger(srslog::fetch_basic_logger("METRICS")); + + // Connect NGAP adpter to CU-CP to pass NGAP messages. + ngap_adapter->connect_cu_cp(cu_cp_obj->get_ng_handler().get_ngap_message_handler(), + cu_cp_obj->get_ng_handler().get_ngap_event_handler()); + + // Connect E1AP to CU-CP. + e1_gw->attach_cu_cp(cu_cp_obj->get_e1_handler()); + + // Connect F1-C to CU-CP. + cu_f1c_gw->attach_cu_cp(cu_cp_obj->get_f1c_handler()); + + // start CU-CP + cu_logger.info("Starting CU-CP..."); + cu_cp_obj->start(); + cu_logger.info("CU-CP started successfully"); + + // Check connection to AMF + if (not cu_cp_obj->get_ng_handler().amf_is_connected()) { + report_error("CU-CP failed to connect to AMF"); + } + + // Create and start CU-UP + // TODO right now build_cu_up() depends on the worker_manager, but the worker manager + // depends on multiple configurations of the DU/RU. As such, temporarly we avoid the + // function and create things direclty here. + std::unique_ptr cu_up_obj = app_build_cu_up(cu_up_config, + workers, + cu_cfg.f1u_cfg.f1u_bind_addr, + *e1_gw, + *cu_f1u_conn->get_f1u_cu_up_gateway(), + *cu_up_pcaps[1].get(), + *cu_timers, + *epoll_broker); + cu_up_obj->start(); + + // Move all the DLT PCAPs to a container. + std::vector> dlt_pcaps = std::move(cu_up_pcaps); + dlt_pcaps.push_back(std::move(f1ap_p)); + dlt_pcaps.push_back(std::move(ngap_p)); + dlt_pcaps.push_back(std::move(e2ap_p)); + + // Start processing. + console.on_app_running(); + + while (is_running) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + // Console helper print stop + console.on_app_stopping(); + + // Stop CU-UP activity. + cu_up_obj->stop(); + + // Stop CU-CP activity. + cu_cp_obj->stop(); + + // Close network connections + cu_logger.info("Closing network connections..."); + ngap_adapter->disconnect(); + cu_logger.info("Network connections closed successfully"); + + // Close PCAPs + cu_logger.info("Closing PCAP files..."); + for (auto& pcap : dlt_pcaps) { + pcap->close(); + } + cu_logger.info("PCAP files successfully closed."); + + // Stop workers + cu_logger.info("Stopping executors..."); + workers.stop(); + cu_logger.info("Executors closed successfully."); + + srslog::flush(); + + if (not cu_cfg.log_cfg.tracing_filename.empty()) { + cu_logger.info("Closing event tracer..."); + close_trace_file(); + cu_logger.info("Event tracer closed successfully"); + } + + // Clean cgroups + // TODO required for CU? + + return 0; +} + +// Temporary helper to create CU-UP. +// TODO remove +std::unique_ptr app_build_cu_up(const cu_up_unit_config& unit_cfg, + cu_worker_manager& workers, + const std::string& f1u_bind_address, + srs_cu_up::e1_connection_client& e1_conn_client, + f1u_cu_up_gateway& f1u_gateway, + dlt_pcap& gtpu_pcap, + timer_manager& timers, + io_broker& io_brk) +{ + srs_cu_up::cu_up_configuration config = generate_cu_up_config(unit_cfg); + config.ctrl_executor = workers.cu_up_ctrl_exec; + config.cu_up_e2_exec = workers.cu_up_e2_exec; + config.ue_exec_pool = workers.cu_up_exec_mapper.get(); + config.io_ul_executor = workers.cu_up_io_ul_exec; // Optionally select separate exec for UL IO + config.e1ap.e1_conn_client = &e1_conn_client; + config.f1u_gateway = &f1u_gateway; + config.gtpu_pcap = >pu_pcap; + config.timers = &timers; + config.qos = generate_cu_up_qos_config(unit_cfg); + + config.net_cfg.f1u_bind_addr = f1u_bind_address; // TODO remove this parameter and make sure that the CU-UP gets the + // bind address directly from the gateway. + + // Create NG-U gateway. + std::unique_ptr ngu_gw; + if (not unit_cfg.upf_cfg.no_core) { + udp_network_gateway_config ngu_gw_config = {}; + ngu_gw_config.bind_address = config.net_cfg.n3_bind_addr; + ngu_gw_config.bind_port = config.net_cfg.n3_bind_port; + ngu_gw_config.bind_interface = config.net_cfg.n3_bind_interface; + ngu_gw_config.rx_max_mmsg = config.net_cfg.n3_rx_max_mmsg; + ngu_gw = srs_cu_up::create_udp_ngu_gateway(ngu_gw_config, io_brk, *workers.cu_up_io_ul_exec); + } else { + ngu_gw = srs_cu_up::create_no_core_ngu_gateway(); + } + config.ngu_gw = ngu_gw.get(); + + return std::make_unique(std::move(ngu_gw), create_cu_up(config)); +} +/// \endcond diff --git a/apps/cu/cu_appconfig.h b/apps/cu/cu_appconfig.h new file mode 100644 index 0000000000..1e8673243b --- /dev/null +++ b/apps/cu/cu_appconfig.h @@ -0,0 +1,133 @@ +/* + * + * 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 "apps/services/os_sched_affinity_manager.h" +#include "srsran/support/executors/unique_thread.h" +#include +#include + +namespace srsran { + +/// Configuration of logging functionalities. +struct log_appconfig { + /// Path to log file or "stdout" to print to console. + std::string filename = "/tmp/cu.log"; + /// Default log level for all layers. + std::string all_level = "warning"; + /// Generic log level assigned to library components without layer-specific level. + std::string lib_level = "warning"; + std::string e2ap_level = "warning"; + std::string config_level = "none"; + std::string metrics_level = "none"; + /// Maximum number of bytes to write when dumping hex arrays. + int hex_max_size = 0; + /// Set to a valid file path to enable tracing and write the trace to the file. + std::string tracing_filename; +}; + +/// Configuration of packet captures. +struct cu_cp_pcap_appconfig { + struct { + std::string filename = "/tmp/cu_ngap.pcap"; + bool enabled = false; + } ngap; + struct { + std::string filename = "/tmp/cu_e1ap.pcap"; + bool enabled = false; + } e1ap; + struct { + std::string filename = "/tmp/cu_f1ap.pcap"; + bool enabled = false; + } f1ap; + struct { + std::string filename = "/tmp/cu_e2ap.pcap"; + bool enabled = false; + } e2ap; + struct { + std::string filename = "/tmp/cu_gtpu.pcap"; + bool enabled = false; + } gtpu; +}; + +struct cu_up_pcap_appconfig { + struct { + std::string filename = "/tmp/cu_gtpu.pcap"; + bool enabled = false; + } gtpu; +}; + +/// CPU affinities configuration for the gNB app. +struct cpu_affinities_appconfig { + /// CPUs isolation. + std::optional isolated_cpus; + /// Low priority workers CPU affinity mask. + os_sched_affinity_config low_priority_cpu_cfg = {sched_affinity_mask_types::low_priority, + {}, + sched_affinity_mask_policy::mask}; +}; + +/// Non real time thread configuration for the gNB. +struct non_rt_threads_appconfig { + /// Number of non real time threads for processing of CP and UP data in the upper layers + unsigned nof_non_rt_threads = 4; +}; + +/// Expert threads configuration of the CU app. +struct expert_threads_appconfig { + /// Non real time thread configuration of the gNB app. + non_rt_threads_appconfig non_rt_threads; +}; + +/// Expert configuration of the gNB app. +struct expert_execution_appconfig { + /// gNB CPU affinities. + cpu_affinities_appconfig affinities; + /// Expert thread configuration of the gNB app. + expert_threads_appconfig threads; +}; + +struct cu_up_f1u_appconfig { + std::string f1u_bind_addr = "127.0.10.1"; // Bind address used by the F1-U interface + int udp_rx_max_msgs = 256; // Max number of UDP packets received by a single syscall on the F1-U interface. +}; + +/// Monolithic gnb application configuration. +struct cu_appconfig { + /// Logging configuration. + log_appconfig log_cfg; + + /// PCAP configuration. + cu_cp_pcap_appconfig cu_cp_pcap_cfg; + cu_up_pcap_appconfig cu_up_pcap_cfg; + + /// F1-U split configuration. + cu_up_f1u_appconfig f1u_cfg; + + /// Expert configuration. + expert_execution_appconfig expert_execution_cfg; + + /// TODO fill in the rest of the configuration +}; + +} // namespace srsran diff --git a/apps/cu/cu_appconfig_cli11_schema.cpp b/apps/cu/cu_appconfig_cli11_schema.cpp new file mode 100644 index 0000000000..28e1e64b2c --- /dev/null +++ b/apps/cu/cu_appconfig_cli11_schema.cpp @@ -0,0 +1,143 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "cu_appconfig_cli11_schema.h" +#include "cu_appconfig.h" +#include "srsran/support/cli11_utils.h" +#include "CLI/CLI11.hpp" + +using namespace srsran; + +// TODO this is common between DU and CU. +static void configure_cli11_log_args(CLI::App& app, log_appconfig& log_params) +{ + auto level_check = [](const std::string& value) -> std::string { + if (value == "info" || value == "debug" || value == "warning" || value == "error") { + return {}; + } + return "Log level value not supported. Accepted values [info,debug,warning,error]"; + }; + + auto metric_level_check = [](const std::string& value) -> std::string { + if (value == "none" || value == "info" || value == "debug") { + return {}; + } + return "Log level value not supported. Accepted values [none,info,debug]"; + }; + + app.add_option("--filename", log_params.filename, "Log file output path")->capture_default_str(); + app.add_option( + "--all_level", log_params.all_level, "Default log level for PHY, MAC, RLC, PDCP, RRC, SDAP, NGAP and GTPU") + ->capture_default_str() + ->check(level_check); + app.add_option("--lib_level", log_params.lib_level, "Generic log level")->capture_default_str()->check(level_check); + app.add_option("--config_level", log_params.config_level, "Config log level") + ->capture_default_str() + ->check(metric_level_check); + app.add_option("--metrics_level", log_params.metrics_level, "Metrics log level") + ->capture_default_str() + ->check(metric_level_check); + app.add_option( + "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") + ->capture_default_str() + ->check(CLI::Range(0, 1024)); + app.add_option("--tracing_filename", log_params.tracing_filename, "Set to a valid file path to enable tracing") + ->always_capture_default(); + + // Post-parsing callback. This allows us to set the log level to "all" level, if no level is provided. + app.callback([&]() { + // Do nothing when all_level is not defined or it is defined as warning. + if (app.count("--all_level") == 0 || log_params.all_level == "warning") { + return; + } + + const auto options = app.get_options(); + for (auto* option : options) { + // Skip all_level option and unrelated options to log level. + if (option->check_name("--all_level") || option->get_name().find("level") == std::string::npos) { + continue; + } + + // Do nothing if option is present. + if (option->count()) { + continue; + } + + // Config and metrics loggers have only subset of levels. + if (option->check_name("--config_level") || option->check_name("--metrics_level")) { + if (log_params.all_level == "error") { + option->default_val("none"); + continue; + } + } + + option->default_val(log_params.all_level); + } + }); +} + +// TODO this is common between DU and CU. +static void configure_cli11_cu_cp_pcap_args(CLI::App& app, cu_cp_pcap_appconfig& pcap_params) +{ + add_option(app, "--ngap_filename", pcap_params.ngap.filename, "NGAP PCAP file output path")->capture_default_str(); + add_option(app, "--ngap_enable", pcap_params.ngap.enabled, "Enable NGAP packet capture")->always_capture_default(); + add_option(app, "--e1ap_filename", pcap_params.e1ap.filename, "E1AP PCAP file output path")->capture_default_str(); + add_option(app, "--e1ap_enable", pcap_params.e1ap.enabled, "Enable E1AP packet capture")->always_capture_default(); + add_option(app, "--f1ap_filename", pcap_params.f1ap.filename, "F1AP PCAP file output path")->capture_default_str(); + add_option(app, "--f1ap_enable", pcap_params.f1ap.enabled, "Enable F1AP packet capture")->always_capture_default(); + add_option(app, "--e2ap_filename", pcap_params.e2ap.filename, "E2AP PCAP file output path")->capture_default_str(); + add_option(app, "--e2ap_enable", pcap_params.e2ap.enabled, "Enable E2AP packet capture")->always_capture_default(); +} + +static void configure_cli11_cu_up_pcap_args(CLI::App& app, cu_up_pcap_appconfig& pcap_params) +{ + add_option(app, "--gtpu_filename", pcap_params.gtpu.filename, "GTP-U PCAP file output path")->capture_default_str(); + add_option(app, "--gtpu_enable", pcap_params.gtpu.enabled, "Enable GTP-U packet capture")->always_capture_default(); +} + +static void configure_cli11_f1u_args(CLI::App& app, cu_up_f1u_appconfig& f1u_cfg) +{ + add_option(app, + "--f1u_bind_addr", + f1u_cfg.f1u_bind_addr, + "Default local IP address interfaces bind to, unless a specific bind address is specified") + ->check(CLI::ValidIPV4); + add_option(app, "--udp_max_rx_msgs", f1u_cfg.udp_rx_max_msgs, "Maximum amount of messages RX in a single syscall"); +} + +void srsran::configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_parsed_cfg) +{ + cu_appconfig& cu_cfg = cu_parsed_cfg; + + // Logging section. + CLI::App* log_subcmd = app.add_subcommand("log", "Logging configuration")->configurable(); + configure_cli11_log_args(*log_subcmd, cu_cfg.log_cfg); + + // PCAP section. + CLI::App* pcap_subcmd = app.add_subcommand("pcap", "PCAP configuration")->configurable(); + configure_cli11_cu_up_pcap_args(*pcap_subcmd, cu_cfg.cu_up_pcap_cfg); + configure_cli11_cu_cp_pcap_args(*pcap_subcmd, cu_cfg.cu_cp_pcap_cfg); + + // F1-U section. + CLI::App* f1u_subcmd = add_subcommand(app, "f1u", "F1-U parameters")->configurable(); + configure_cli11_f1u_args(*f1u_subcmd, cu_parsed_cfg.f1u_cfg); +} diff --git a/apps/cu/cu_appconfig_cli11_schema.h b/apps/cu/cu_appconfig_cli11_schema.h new file mode 100644 index 0000000000..307090bb4b --- /dev/null +++ b/apps/cu/cu_appconfig_cli11_schema.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "CLI/CLI11.hpp" + +namespace srsran { + +struct cu_appconfig; + +/// Configures the given CLI11 application with the gNB application configuration schema. +void configure_cli11_with_cu_appconfig_schema(CLI::App& app, cu_appconfig& cu_parsed_cfg); + +} // namespace srsran diff --git a/apps/cu/cu_worker_manager.cpp b/apps/cu/cu_worker_manager.cpp new file mode 100644 index 0000000000..fc9c7f19f1 --- /dev/null +++ b/apps/cu/cu_worker_manager.cpp @@ -0,0 +1,196 @@ +/* + * + * 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_worker_manager.h" +#include "srsran/support/event_tracing.h" + +using namespace srsran; + +static const uint32_t task_worker_queue_size = 2048; + +cu_worker_manager::cu_worker_manager(const cu_appconfig& appcfg, unsigned gtpu_queue_size) : + low_prio_affinity_mng({appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg}) +{ + create_low_prio_executors(appcfg, gtpu_queue_size); + associate_low_prio_executors(); +} + +void cu_worker_manager::stop() +{ + exec_mng.stop(); +} + +void cu_worker_manager::create_worker_pool(const std::string& name, + unsigned nof_workers, + unsigned queue_size, + const std::vector& execs, + os_thread_realtime_priority prio, + span cpu_masks) +{ + using namespace execution_config_helper; + + concurrent_queue_policy queue_policy = concurrent_queue_policy::locking_mpmc; + + const worker_pool pool{name, + nof_workers, + {{queue_policy, queue_size}}, + execs, + std::chrono::microseconds{queue_policy == concurrent_queue_policy::locking_mpmc ? 0 : 10}, + prio, + std::vector{cpu_masks.begin(), cpu_masks.end()}}; + if (not exec_mng.add_execution_context(create_execution_context(pool))) { + report_fatal_error("Failed to instantiate {} execution context", pool.name); + } +} + +void cu_worker_manager::create_prio_worker(const std::string& name, + unsigned queue_size, + const std::vector& execs, + const os_sched_affinity_bitmask& mask, + os_thread_realtime_priority prio) +{ + using namespace execution_config_helper; + + const single_worker worker_desc{ + name, {concurrent_queue_policy::locking_mpsc, queue_size}, execs, std::nullopt, prio, mask}; + if (not exec_mng.add_execution_context(create_execution_context(worker_desc))) { + report_fatal_error("Failed to instantiate {} execution context", worker_desc.name); + } +} + +void append_pcap_strands(std::vector& strand_list, + const cu_cp_pcap_appconfig& cp_pcaps, + const cu_up_pcap_appconfig& up_pcaps) +{ + using namespace execution_config_helper; + + // Default configuration for each pcap writer strand. + strand base_strand_cfg{{{"pcap_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; + + // These layers have very low throughput, so no point in instantiating more than one strand. + // This means that there is no parallelization in pcap writing across these layers. + if (cp_pcaps.e2ap.enabled or cp_pcaps.f1ap.enabled or cp_pcaps.ngap.enabled or cp_pcaps.e1ap.enabled) { + strand_list.emplace_back(base_strand_cfg); + } + + if (up_pcaps.gtpu.enabled) { + base_strand_cfg.queues[0].name = "gtpu_pcap_exec"; + strand_list.emplace_back(base_strand_cfg); + } +} + +execution_config_helper::worker_pool cu_worker_manager::create_low_prio_workers(const cu_appconfig& appcfg) +{ + using namespace execution_config_helper; + + // Configure non-RT worker pool. + worker_pool non_rt_pool{ + "non_rt_pool", + appcfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads, + {{concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, // two task priority levels. + {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}, + // Left empty, is filled later. + {}, + std::chrono::microseconds{100}, + os_thread_realtime_priority::no_realtime(), + std::vector{appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask}}; + + return non_rt_pool; +} + +void cu_worker_manager::create_low_prio_executors(const cu_appconfig& appcfg, unsigned gtpu_queue_size) +{ + using namespace execution_config_helper; + // TODO: split executor creation and association to workers + worker_pool non_rt_pool = create_low_prio_workers(appcfg); + + // Associate executors to the worker pool. + // 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); + // 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, + {}, // define CU-UP strands below. + task_worker_queue_size}); + + std::vector& low_prio_strands = non_rt_pool.executors[0].strands; + std::vector& high_prio_strands = non_rt_pool.executors[1].strands; + std::vector& cu_up_strands = non_rt_pool.executors[2].strands; + + // Configuration of strands for PCAP writing. These strands will use the low priority executor. + append_pcap_strands(low_prio_strands, appcfg.cu_cp_pcap_cfg, appcfg.cu_up_pcap_cfg); + + // 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}, + {"ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}; + high_prio_strands.push_back(cp_strand); + + // Configuration of strands for user plane handling (CU-UP and DU-low user plane). Given that the CU-UP doesn't + // currently support multithreading, these strands will point to a strand that interfaces with the non-RT thread pool. + // Each UE strand will have three queues, one for timer management and configuration, one for DL data plane and one + // for UL data plane. + cu_up_strands.push_back( + strand{{{"cu_up_ctrl_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, + {"cu_up_io_ul_exec", concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}}); + for (unsigned i = 0; i != nof_cu_up_ue_strands; ++i) { + cu_up_strands.push_back( + strand{{{fmt::format("ue_up_ctrl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, + {fmt::format("ue_up_ul_exec#{}", i), + concurrent_queue_policy::lockfree_mpmc, + gtpu_queue_size}, // TODO: Consider separate param for size of UL queue if needed. + {fmt::format("ue_up_dl_exec#{}", i), concurrent_queue_policy::lockfree_mpmc, gtpu_queue_size}}}); + } + + // Create non-RT worker pool. + if (not exec_mng.add_execution_context(create_execution_context(non_rt_pool))) { + report_fatal_error("Failed to instantiate {} execution context", non_rt_pool.name); + } +} + +void cu_worker_manager::associate_low_prio_executors() +{ + using namespace execution_config_helper; + const auto& exec_map = exec_mng.executors(); + + // Update executor pointer mapping + cu_cp_exec = exec_map.at("ctrl_exec"); + cu_cp_e2_exec = exec_map.at("ctrl_exec"); + metrics_hub_exec = exec_map.at("ctrl_exec"); + cu_up_ctrl_exec = exec_map.at("cu_up_ctrl_exec"); + cu_up_io_ul_exec = exec_map.at("cu_up_io_ul_exec"); + cu_up_e2_exec = exec_map.at("cu_up_ctrl_exec"); + + // Create CU-UP execution mapper object. + std::vector ue_up_dl_execs(nof_cu_up_ue_strands, nullptr); + std::vector ue_up_ul_execs(nof_cu_up_ue_strands, nullptr); + std::vector ue_up_ctrl_execs(nof_cu_up_ue_strands, nullptr); + for (unsigned i = 0; i != nof_cu_up_ue_strands; ++i) { + ue_up_dl_execs[i] = exec_map.at(fmt::format("ue_up_dl_exec#{}", i)); + ue_up_ul_execs[i] = exec_map.at(fmt::format("ue_up_ul_exec#{}", i)); + ue_up_ctrl_execs[i] = exec_map.at(fmt::format("ue_up_ctrl_exec#{}", i)); + } + cu_up_exec_mapper = srs_cu_up::make_cu_up_executor_pool( + *exec_map.at("cu_up_ctrl_exec"), ue_up_dl_execs, ue_up_ul_execs, ue_up_ctrl_execs, *exec_map.at("low_prio_exec")); +} diff --git a/apps/cu/cu_worker_manager.h b/apps/cu/cu_worker_manager.h new file mode 100644 index 0000000000..d24456178b --- /dev/null +++ b/apps/cu/cu_worker_manager.h @@ -0,0 +1,99 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "apps/services/os_sched_affinity_manager.h" +#include "cu_appconfig.h" +#include "srsran/cu_up/cu_up_executor_pool.h" +#include "srsran/support/executors/task_execution_manager.h" +#include "srsran/support/executors/task_executor.h" + +namespace srsran { + +/// Manages the workers of the app. +struct cu_worker_manager { + cu_worker_manager(const cu_appconfig& appcfg, unsigned gtpu_queue_size); + + void stop(); + + /// cu-cp ctrl exec points to general ctrl_worker + /// cu-up ue exec points to the general ue_worker + /// + /// The handler side is responsible for executor dispatching: + /// - ngap::handle_message calls cu-cp ctrl exec + /// - f1ap_cu::handle_message calls cu-cp ctrl exec + /// - e1ap_cu_cp::handle_message calls cu-cp ctrl exec + /// - e1ap_cu_up::handle_message calls cu-up ue exec + + task_executor* cu_cp_exec = nullptr; + task_executor* cu_up_ctrl_exec = nullptr; ///< CU-UP executor for control + task_executor* cu_up_io_ul_exec = nullptr; ///< CU-UP executor for UL socket transmission + task_executor* cu_cp_e2_exec = nullptr; + task_executor* cu_up_e2_exec = nullptr; + task_executor* metrics_hub_exec = nullptr; + + std::unique_ptr cu_up_exec_mapper; + + // Gets the DU-low downlink executors. + void get_du_low_dl_executors(std::vector& executors, unsigned sector_id) const; + + /// Get executor based on the name. + task_executor* find_executor(const std::string& name) const + { + auto it = exec_mng.executors().find(name); + return it != exec_mng.executors().end() ? it->second : nullptr; + } + task_executor& get_executor(const std::string& name) const { return *exec_mng.executors().at(name); } + +private: + static const unsigned nof_cu_up_ue_strands = 16; + + /// Manager of execution contexts and respective executors instantiated by the application. + task_execution_manager exec_mng; + + os_sched_affinity_manager low_prio_affinity_mng; + + /// CPU affinity bitmask manager per cell. + std::vector affinity_mng; + + /// Helper method to create workers with non zero priority. + void create_prio_worker(const std::string& name, + unsigned queue_size, + const std::vector& execs, + const os_sched_affinity_bitmask& mask, + os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime()); + + /// Helper method to create worker pool. + void create_worker_pool(const std::string& name, + unsigned nof_workers, + unsigned queue_size, + const std::vector& execs, + os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime(), + span cpu_masks = {}); + + execution_config_helper::worker_pool create_low_prio_workers(const cu_appconfig& appcfg); + void create_low_prio_executors(const cu_appconfig& appcfg, unsigned gtpu_queue_size); + void associate_low_prio_executors(); +}; + +} // namespace srsran diff --git a/apps/du/CMakeLists.txt b/apps/du/CMakeLists.txt new file mode 100644 index 0000000000..17bbd66e57 --- /dev/null +++ b/apps/du/CMakeLists.txt @@ -0,0 +1,55 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_executable(srsdu + du.cpp + du_appconfig_cli11_schema.cpp + du_appconfig_validators.cpp + du_appconfig_translators.cpp + ../gnb/adapters/e2_gateway_remote_connector.cpp # TODO: Delete +) + +install(TARGETS srsdu + RUNTIME) + +target_link_libraries(srsdu + srsran_app_services + srsgnb_app_f1u_du_split_connector + srsran_cu_cp + srsran_network + srsran_e2 + e2ap_asn1 + srsran_pcap + srsran_support + srsran_version + srsran_build_info + srsran_flexible_du_dynamic + srsran_f1c_gateway + srsran_cu_up # TODO: Delete +) + +if (DPDK_FOUND) + add_definitions(-DDPDK_FOUND) + target_link_libraries(srsdu hal_dpdk) +endif (DPDK_FOUND) + +add_backward(srsdu) + +target_include_directories(srsdu PRIVATE ${CMAKE_SOURCE_DIR} ${CMAKE_SOURCE_DIR}/external) diff --git a/apps/du/adapters/f1_gateways.h b/apps/du/adapters/f1_gateways.h new file mode 100644 index 0000000000..ce383dc306 --- /dev/null +++ b/apps/du/adapters/f1_gateways.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/f1ap/gateways/f1c_network_client_factory.h" + +namespace srsran { + +/// Instantiates an F1-C DU client. +std::unique_ptr create_f1c_client_gateway(const std::string& cu_cp_addr, + const std::string& bind_addr, + io_broker& broker, + dlt_pcap& f1ap_pcap) +{ + sctp_network_connector_config f1c_sctp{}; + f1c_sctp.connection_name = "F1-C"; + f1c_sctp.connect_address = cu_cp_addr; + f1c_sctp.connect_port = 38471; + f1c_sctp.ppid = F1AP_PPID; + f1c_sctp.bind_address = bind_addr; + return create_f1c_gateway_client(f1c_du_sctp_gateway_config{f1c_sctp, broker, f1ap_pcap}); +} + +} // namespace srsran diff --git a/apps/du/du.cpp b/apps/du/du.cpp new file mode 100644 index 0000000000..f0b0c57e48 --- /dev/null +++ b/apps/du/du.cpp @@ -0,0 +1,374 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/gateways/sctp_network_gateway_factory.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/pcap/mac_pcap.h" +#include "srsran/support/build_info/build_info.h" +#include "srsran/support/cpu_features.h" +#include "srsran/support/event_tracing.h" +#include "srsran/support/signal_handler.h" +#include "srsran/support/version/version.h" + +#include "srsran/f1u/du/split_connector/f1u_split_connector.h" +#include "srsran/gtpu/gtpu_demux_factory.h" + +#include "srsran/support/io/io_broker_factory.h" + +#include "adapters/f1_gateways.h" +#include "srsran/support/backtrace.h" +#include "srsran/support/config_parsers.h" + +#include "du_appconfig.h" +#include "du_appconfig_cli11_schema.h" +#include "du_appconfig_translators.h" +#include "du_appconfig_validators.h" + +#include "apps/services/worker_manager.h" + +#include "apps/services/console_helper.h" +#include "apps/services/metrics_log_helper.h" +#include "apps/services/rlc_metrics_plotter_json.h" + +#include "apps/units/flexible_du/split_dynamic/dynamic_du_factory.h" + +#include "apps/gnb/adapters/e2_gateway_remote_connector.h" +#include "apps/services/e2_metric_connector_manager.h" +#include "srsran/support/sysinfo.h" + +#include + +#include "../units/flexible_du/du_high/pcap_factory.h" +#include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.h" +#include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.h" +#include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h" + +#ifdef DPDK_FOUND +#include "srsran/hal/dpdk/dpdk_eal_factory.h" +#endif + +using namespace srsran; + +/// \file +/// \brief Application of a distributed unit (DU) that is split from the CU-CP and CU-UP via the F1 interface. +/// +/// \cond + +static std::string config_file; + +static std::atomic is_running = {true}; +const int MAX_CONFIG_FILES(10); + +static void populate_cli11_generic_args(CLI::App& app) +{ + fmt::memory_buffer buffer; + format_to(buffer, "srsRAN 5G DU version {} ({})", get_version(), get_build_hash()); + app.set_version_flag("-v,--version", srsran::to_c_str(buffer)); + app.set_config("-c,", config_file, "Read config from file", false)->expected(1, MAX_CONFIG_FILES); +} + +static void local_signal_handler() +{ + is_running = false; +} + +static void initialize_log(const std::string& filename) +{ + srslog::sink* log_sink = (filename == "stdout") ? srslog::create_stdout_sink() : srslog::create_file_sink(filename); + if (log_sink == nullptr) { + report_error("Could not create application main log sink.\n"); + } + srslog::set_default_sink(*log_sink); + srslog::init(); +} + +static void register_app_logs(const log_appconfig& log_cfg, const dynamic_du_unit_config& du_loggers) +{ + // Set log-level of app and all non-layer specific components to app level. + for (const auto& id : {"GNB", "ALL", "SCTP-GW", "IO-EPOLL", "UDP-GW", "PCAP"}) { + auto& logger = srslog::fetch_basic_logger(id, false); + logger.set_level(srslog::str_to_basic_level(log_cfg.lib_level)); + logger.set_hex_dump_max_size(log_cfg.hex_max_size); + } + + auto& config_logger = srslog::fetch_basic_logger("CONFIG", false); + config_logger.set_level(srslog::str_to_basic_level(log_cfg.config_level)); + config_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + auto& metrics_logger = srslog::fetch_basic_logger("METRICS", false); + metrics_logger.set_level(srslog::str_to_basic_level(log_cfg.metrics_level)); + metrics_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + auto& e2ap_logger = srslog::fetch_basic_logger("E2AP", false); + e2ap_logger.set_level(srslog::str_to_basic_level(log_cfg.e2ap_level)); + e2ap_logger.set_hex_dump_max_size(log_cfg.hex_max_size); + + // Register units logs. + register_dynamic_du_loggers(du_loggers); +} + +// TODO: Remove. +class null_command_handler : public srs_cu_cp::cu_cp_command_handler, public srs_cu_cp::cu_cp_mobility_command_handler +{ +public: + srs_cu_cp::cu_cp_mobility_command_handler& get_mobility_command_handler() override { return *this; } + void trigger_handover(pci_t source_pci, rnti_t rnti, pci_t target_pci) override {} +}; + +int main(int argc, char** argv) +{ + // Set signal handler. + register_signal_handler(local_signal_handler); + + // Enable backtrace. + enable_backtrace(); + + // Setup and configure config parsing. + CLI::App app("srsDU application"); + app.config_formatter(create_yaml_config_parser()); + app.allow_config_extras(CLI::config_extras_mode::error); + // Fill the generic application arguments to parse. + populate_cli11_generic_args(app); + + du_appconfig du_cfg; + // Configure CLI11 with the DU application configuration schema. + configure_cli11_with_du_appconfig_schema(app, du_cfg); + + dynamic_du_unit_config du_unit_cfg; + configure_cli11_with_dynamic_du_unit_config_schema(app, du_unit_cfg); + + // Set the callback for the app calling all the autoderivation functions. + app.callback([&app, &du_cfg, &du_unit_cfg]() { + autoderive_du_parameters_after_parsing(app, du_cfg); + autoderive_dynamic_du_parameters_after_parsing(app, du_unit_cfg); + }); + + // Parse arguments. + CLI11_PARSE(app, argc, argv); + + // Check the modified configuration. + if (!validate_appconfig(du_cfg) || + !validate_dynamic_du_unit_config(du_unit_cfg, + (du_cfg.expert_execution_cfg.affinities.isolated_cpus) + ? du_cfg.expert_execution_cfg.affinities.isolated_cpus.value() + : os_sched_affinity_bitmask::available_cpus())) { + report_error("Invalid configuration detected.\n"); + } + + // Set up logging. + initialize_log(du_cfg.log_cfg.filename); + register_app_logs(du_cfg.log_cfg, du_unit_cfg); + + // Log input configuration. + srslog::basic_logger& config_logger = srslog::fetch_basic_logger("CONFIG"); + if (config_logger.debug.enabled()) { + config_logger.debug("Input configuration (all values): \n{}", app.config_to_str(true, false)); + } else { + config_logger.info("Input configuration (only non-default values): \n{}", app.config_to_str(false, false)); + } + + srslog::basic_logger& du_logger = srslog::fetch_basic_logger("DU"); + if (not du_cfg.log_cfg.tracing_filename.empty()) { + du_logger.info("Opening event tracer..."); + open_trace_file(du_cfg.log_cfg.tracing_filename); + du_logger.info("Event tracer opened successfully"); + } + + if (du_cfg.expert_execution_cfg.affinities.isolated_cpus) { + if (!configure_cgroups(*du_cfg.expert_execution_cfg.affinities.isolated_cpus)) { + report_error("Failed to isolate specified CPUs"); + } + } + +#ifdef DPDK_FOUND + std::unique_ptr eal; + if (du_cfg.hal_config) { + // Prepend the application name in argv[0] as it is expected by EAL. + eal = dpdk::create_dpdk_eal(std::string(argv[0]) + " " + du_cfg.hal_config->eal_args, + srslog::fetch_basic_logger("EAL", false)); + } +#endif + + // Setup size of byte buffer pool. + init_byte_buffer_segment_pool(du_cfg.buffer_pool_config.nof_segments, du_cfg.buffer_pool_config.segment_size); + + // Log build info + du_logger.info("Built in {} mode using {}", get_build_mode(), get_build_info()); + + // Log CPU architecture. + cpu_architecture_info::get().print_cpu_info(du_logger); + + // Check and log included CPU features and check support by current CPU + if (cpu_supports_included_features()) { + du_logger.debug("Required CPU features: {}", get_cpu_feature_info()); + } else { + // Quit here until we complete selection of the best matching implementation for the current CPU at runtime. + du_logger.error("The CPU does not support the required CPU features that were configured during compile time: {}", + get_cpu_feature_info()); + report_error("The CPU does not support the required CPU features that were configured during compile time: {}\n", + get_cpu_feature_info()); + } + + // Check some common causes of performance issues and print a warning if required. + check_cpu_governor(du_logger); + check_drm_kms_polling(du_logger); + + worker_manager workers{du_unit_cfg, du_cfg.expert_execution_cfg, du_cfg.pcap_cfg, du_cfg.f1u_cfg.pdu_queue_size}; + + // Set layer-specific pcap options. + const auto& low_prio_cpu_mask = du_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; + + // Create IO broker. + io_broker_config io_broker_cfg(low_prio_cpu_mask); + std::unique_ptr epoll_broker = create_io_broker(io_broker_type::epoll, io_broker_cfg); + + std::unique_ptr f1ap_p = + modules::flexible_du::create_dlt_pcap(du_unit_cfg.du_high_cfg.config.pcaps, workers); + std::unique_ptr mac_p = + modules::flexible_du::create_mac_pcap(du_unit_cfg.du_high_cfg.config.pcaps, workers); + std::unique_ptr rlc_p = + modules::flexible_du::create_rlc_pcap(du_unit_cfg.du_high_cfg.config.pcaps, workers); + // TODO: Remove GTPU pcap + std::unique_ptr gtpu_p = + modules::flexible_du::create_dlt_pcap(du_unit_cfg.du_high_cfg.config.pcaps, workers); + std::unique_ptr e2ap_p = + du_cfg.pcap_cfg.e2ap.enabled ? create_e2ap_pcap(du_cfg.pcap_cfg.e2ap.filename, workers.get_executor("pcap_exec")) + : create_null_dlt_pcap(); + + // Instantiate F1-C client gateway. + std::unique_ptr f1c_gw = + create_f1c_client_gateway(du_cfg.f1c_cfg.cu_cp_address, du_cfg.f1c_cfg.bind_address, *epoll_broker, *f1ap_p); + + // Create manager of timers for DU, which will be driven by the PHY slot ticks. + timer_manager app_timers{256}; + + // Create F1-U connector + // TODO: Simplify this and use factory. + gtpu_demux_creation_request du_f1u_gtpu_msg = {}; + du_f1u_gtpu_msg.cfg.warn_on_drop = true; + du_f1u_gtpu_msg.gtpu_pcap = gtpu_p.get(); + std::unique_ptr du_f1u_gtpu_demux = create_gtpu_demux(du_f1u_gtpu_msg); + udp_network_gateway_config du_f1u_gw_config = {}; + du_f1u_gw_config.bind_address = du_cfg.f1u_cfg.bind_address; + du_f1u_gw_config.bind_port = GTPU_PORT; + du_f1u_gw_config.reuse_addr = true; + std::unique_ptr du_f1u_gw = srs_cu_up::create_udp_ngu_gateway( + du_f1u_gw_config, + *epoll_broker, + workers.get_du_high_executor_mapper(0).ue_mapper().mac_ul_pdu_executor(to_du_ue_index(0))); + std::unique_ptr du_f1u_conn = + std::make_unique(du_f1u_gw.get(), du_f1u_gtpu_demux.get(), *gtpu_p); + + // Set up the JSON log channel used by metrics. + srslog::sink& json_sink = + srslog::fetch_udp_sink(du_cfg.metrics_cfg.addr, du_cfg.metrics_cfg.port, srslog::create_json_formatter()); + srslog::log_channel& json_channel = srslog::fetch_log_channel("JSON_channel", json_sink, {}); + json_channel.set_enabled(du_cfg.metrics_cfg.enable_json_metrics); + + // Set up RLC JSON log channel used by metrics. + srslog::log_channel& rlc_json_channel = srslog::fetch_log_channel("JSON_RLC_channel", json_sink, {}); + rlc_json_channel.set_enabled(du_unit_cfg.du_high_cfg.config.metrics.rlc.json_enabled); + rlc_metrics_plotter_json rlc_json_plotter(rlc_json_channel); + + std::unique_ptr hub = std::make_unique(*workers.metrics_hub_exec); + e2_metric_connector_manager e2_metric_connectors(du_unit_cfg.du_high_cfg.config.cells_cfg.size()); + + // E2AP configuration. + srsran::sctp_network_connector_config e2_du_nw_config = generate_e2ap_nw_config(du_cfg, E2_DU_PPID); + + // Create E2AP GW remote connector. + e2_gateway_remote_connector e2_gw{*epoll_broker, e2_du_nw_config, *e2ap_p}; + + // Create console helper object for commands and metrics printing. + // TODO: Remove cu_cp handler dependency. + null_command_handler cmd_handler; + console_helper console(*epoll_broker, json_channel, cmd_handler, du_cfg.metrics_cfg.autostart_stdout_metrics); + console.on_app_starting(); + + // Create metrics log helper. + metrics_log_helper metrics_logger(srslog::fetch_basic_logger("METRICS")); + + // Instantiate one DU. + std::unique_ptr du_inst = create_du(du_unit_cfg, + workers, + *f1c_gw, + *du_f1u_conn, + app_timers, + *mac_p, + *rlc_p, + console, + metrics_logger, + e2_gw, + e2_metric_connectors, + rlc_json_plotter, + *hub); + + // Move all the DLT PCAPs to a container. + std::vector> dlt_pcaps; + dlt_pcaps.push_back(std::move(f1ap_p)); + dlt_pcaps.push_back(std::move(e2ap_p)); + + // Start processing. + du_inst->start(); + console.on_app_running(); + + while (is_running) { + std::this_thread::sleep_for(std::chrono::milliseconds(100)); + } + + console.on_app_stopping(); + + // Stop DU activity. + du_inst->stop(); + + if (du_cfg.e2_cfg.enable_du_e2) { + du_logger.info("Closing E2 network connections..."); + e2_gw.close(); + du_logger.info("E2 Network connections closed successfully"); + } + + du_logger.info("Closing PCAP files..."); + mac_p->close(); + rlc_p->close(); + for (auto& pcap : dlt_pcaps) { + pcap->close(); + } + du_logger.info("PCAP files successfully closed."); + + du_logger.info("Stopping executors..."); + workers.stop(); + du_logger.info("Executors closed successfully."); + + srslog::flush(); + + if (not du_cfg.log_cfg.tracing_filename.empty()) { + du_logger.info("Closing event tracer..."); + close_trace_file(); + du_logger.info("Event tracer closed successfully"); + } + + if (du_cfg.expert_execution_cfg.affinities.isolated_cpus) { + cleanup_cgroups(); + } + + return 0; +} diff --git a/apps/du/du_appconfig.h b/apps/du/du_appconfig.h new file mode 100644 index 0000000000..e5482c55b0 --- /dev/null +++ b/apps/du/du_appconfig.h @@ -0,0 +1,72 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "../gnb/gnb_appconfig.h" // TODO: Remove +#include "apps/services/os_sched_affinity_manager.h" +#include "srsran/adt/byte_buffer.h" +#include "srsran/support/executors/unique_thread.h" +#include + +namespace srsran { +namespace srs_du { + +/// Configuration of the F1-C interface of the DU. +struct f1c_appconfig { + /// CU-CP F1-C address the DU will connect to. + std::string cu_cp_address = "127.0.10.1"; + /// DU F1-C bind address. + std::string bind_address = "127.0.10.2"; +}; + +struct f1u_appconfig { + unsigned pdu_queue_size = 2048; + /// IP address to bind the F1-U interface to. + std::string bind_address = "127.0.10.2"; +}; + +} // namespace srs_du + +/// DU application configuration. +struct du_appconfig { + /// Logging configuration. + log_appconfig log_cfg; + /// PCAP configuration. + pcap_appconfig pcap_cfg; + /// Metrics configuration. + metrics_appconfig metrics_cfg; + /// E2 configuration. + e2_appconfig e2_cfg; + /// F1-C configuration. + srs_du::f1c_appconfig f1c_cfg; + /// F1-U configuration. + srs_du::f1u_appconfig f1u_cfg; + /// Buffer pool configuration. + buffer_pool_appconfig buffer_pool_config; + /// Expert configuration. + expert_execution_appconfig expert_execution_cfg; + /// HAL configuration. + std::optional hal_config; +}; + +} // namespace srsran diff --git a/apps/du/du_appconfig_cli11_schema.cpp b/apps/du/du_appconfig_cli11_schema.cpp new file mode 100644 index 0000000000..1ec74ab3c2 --- /dev/null +++ b/apps/du/du_appconfig_cli11_schema.cpp @@ -0,0 +1,76 @@ +/* + * + * 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 "du_appconfig_cli11_schema.h" +#include "du_appconfig.h" + +using namespace srsran; + +static void configure_cli11_f1c_args(CLI::App& app, srs_du::f1c_appconfig& f1c_params) +{ + app.add_option("--cu_cp_addr", f1c_params.cu_cp_address, "CU-CP F1-C address to connect to")->capture_default_str(); + app.add_option( + "--bind_addr", f1c_params.bind_address, "DU F1-C bind address. If left empty, implicit bind is performed") + ->capture_default_str(); +} + +static void configure_cli11_f1u_args(CLI::App& app, srs_du::f1u_appconfig& f1u_params) +{ + app.add_option("--queue_size", f1u_params.pdu_queue_size, "F1-U PDU queue size")->capture_default_str(); + app.add_option( + "--bind_addr", f1u_params.bind_address, "DU F1-U bind address. If left empty, implicit bind is performed") + ->capture_default_str(); +} + +void srsran::configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfig& parsed_cfg) +{ + // F1-C section. + CLI::App* f1c_subcmd = app.add_subcommand("f1c", "F1-C interface configuration")->configurable(); + configure_cli11_f1c_args(*f1c_subcmd, parsed_cfg.f1c_cfg); + + // F1-U section. + CLI::App* f1u_subcmd = app.add_subcommand("f1u", "F1-U interface configuration")->configurable(); + configure_cli11_f1u_args(*f1u_subcmd, parsed_cfg.f1u_cfg); + + // Logging section. + // TODO + + // PCAP section. + // TODO + + // Metrics section. + // TODO + + // E2 section. + // TODO + + // Buffer pool section. + // TODO + + // Expert section. + // TODO + + // HAL section. + // TODO +} + +void srsran::autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& parsed_cfg) {} diff --git a/include/srsran/adt/any.h b/apps/du/du_appconfig_cli11_schema.h similarity index 52% rename from include/srsran/adt/any.h rename to apps/du/du_appconfig_cli11_schema.h index b740140def..64fa01ca41 100644 --- a/include/srsran/adt/any.h +++ b/apps/du/du_appconfig_cli11_schema.h @@ -22,43 +22,16 @@ #pragma once -#define ANY_IMPL_NO_EXCEPTIONS -#include "any/any.hpp" +#include "CLI/CLI11.hpp" namespace srsran { -using bad_any_cast = linb::bad_any_cast; +struct du_appconfig; -using any = linb::any; +/// Configures the given CLI11 application with the DU application configuration schema. +void configure_cli11_with_du_appconfig_schema(CLI::App& app, du_appconfig& parsed_cfg); -template -inline ValueType any_cast(const any& operand) -{ - return linb::any_cast(operand); -} - -template -inline ValueType any_cast(any& operand) -{ - return linb::any_cast(operand); -} - -template -inline ValueType any_cast(any&& operand) -{ - return linb::any_cast(std::move(operand)); -} - -template -inline const ValueType* any_cast(const any* operand) noexcept -{ - return linb::any_cast(operand); -} - -template -inline ValueType* any_cast(any* operand) noexcept -{ - return linb::any_cast(operand); -} +/// Auto derive DU parameters after the parsing. +void autoderive_du_parameters_after_parsing(CLI::App& app, du_appconfig& parsed_cfg); } // namespace srsran diff --git a/apps/du/du_appconfig_translators.cpp b/apps/du/du_appconfig_translators.cpp new file mode 100644 index 0000000000..096fef0f5a --- /dev/null +++ b/apps/du/du_appconfig_translators.cpp @@ -0,0 +1,56 @@ +/* + * + * 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 "du_appconfig_translators.h" +#include "du_appconfig.h" + +using namespace srsran; +using namespace std::chrono_literals; + +// TODO: refactor. +srsran::sctp_network_connector_config srsran::generate_e2ap_nw_config(const du_appconfig& config, int ppid) +{ + srsran::sctp_network_connector_config out_cfg; + out_cfg.connection_name = "NearRT-RIC"; + out_cfg.connect_address = config.e2_cfg.ip_addr; + out_cfg.connect_port = config.e2_cfg.port; + out_cfg.bind_address = config.e2_cfg.bind_addr; + out_cfg.ppid = ppid; + + if (config.e2_cfg.sctp_rto_initial >= 0) { + out_cfg.rto_initial = config.e2_cfg.sctp_rto_initial; + } + if (config.e2_cfg.sctp_rto_min >= 0) { + out_cfg.rto_min = config.e2_cfg.sctp_rto_min; + } + if (config.e2_cfg.sctp_rto_max >= 0) { + out_cfg.rto_max = config.e2_cfg.sctp_rto_max; + } + if (config.e2_cfg.sctp_init_max_attempts >= 0) { + out_cfg.init_max_attempts = config.e2_cfg.sctp_init_max_attempts; + } + if (config.e2_cfg.sctp_max_init_timeo >= 0) { + out_cfg.max_init_timeo = config.e2_cfg.sctp_max_init_timeo; + } + + return out_cfg; +} diff --git a/apps/du/du_appconfig_translators.h b/apps/du/du_appconfig_translators.h new file mode 100644 index 0000000000..24447d13ea --- /dev/null +++ b/apps/du/du_appconfig_translators.h @@ -0,0 +1,35 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/e2/e2ap_configuration.h" +#include "srsran/gateways/sctp_network_gateway.h" + +namespace srsran { + +struct du_appconfig; + +/// Converts and returns the given gnb application configuration to a E2AP Network Gateway configuration. +sctp_network_connector_config generate_e2ap_nw_config(const du_appconfig& config, int ppid); + +} // namespace srsran \ No newline at end of file diff --git a/apps/du/du_appconfig_validators.cpp b/apps/du/du_appconfig_validators.cpp new file mode 100644 index 0000000000..0dc0744e0b --- /dev/null +++ b/apps/du/du_appconfig_validators.cpp @@ -0,0 +1,35 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "du_appconfig_validators.h" + +using namespace srsran; + +bool srsran::validate_appconfig(const du_appconfig& config) +{ + if (config.f1c_cfg.cu_cp_address.empty()) { + fmt::print("CU-CP F1-C address is mandatory\n"); + return false; + } + + return true; +} diff --git a/apps/du/du_appconfig_validators.h b/apps/du/du_appconfig_validators.h new file mode 100644 index 0000000000..b7458a69f6 --- /dev/null +++ b/apps/du/du_appconfig_validators.h @@ -0,0 +1,32 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "du_appconfig.h" + +namespace srsran { + +/// Validates the given DU application configuration. Returns true on success, false otherwise. +bool validate_appconfig(const du_appconfig& config); + +} // namespace srsran \ No newline at end of file diff --git a/apps/examples/du/CMakeLists.txt b/apps/examples/du/CMakeLists.txt index bb75b04058..edf535def3 100644 --- a/apps/examples/du/CMakeLists.txt +++ b/apps/examples/du/CMakeLists.txt @@ -27,8 +27,8 @@ if (ZEROMQ_FOUND OR UHD_FOUND) target_link_libraries(du_example srsran_du_high srsran_gateway - mac_fapi_adaptor_factory - phy_fapi_adaptor_factory + srsran_mac_fapi_adaptor + srsran_phy_fapi_adaptor srsran_fapi srsran_phy_support srsran_upper_phy diff --git a/apps/gnb/CMakeLists.txt b/apps/gnb/CMakeLists.txt index d5d8032077..c4b4bb38fa 100644 --- a/apps/gnb/CMakeLists.txt +++ b/apps/gnb/CMakeLists.txt @@ -23,9 +23,7 @@ add_executable(gnb gnb_appconfig_cli11_schema.cpp gnb_appconfig_validators.cpp gnb_appconfig_translators.cpp - adapters/e1ap_gateway_local_connector.cpp adapters/e2_gateway_remote_connector.cpp - adapters/ngap_adapter.cpp ) install(TARGETS gnb @@ -45,8 +43,9 @@ target_link_libraries(gnb srsran_support srsran_version srsran_build_info - srsran_flexible_du_ru_dynamic + srsran_flexible_du_dynamic srsran_f1c_gateway + srsran_e1_gateway srsran_cu_cp_app_unit srsran_cu_up_app_unit ) diff --git a/apps/gnb/adapters/e1ap_gateway_local_connector.cpp b/apps/gnb/adapters/e1ap_gateway_local_connector.cpp deleted file mode 100644 index 2fa3b943ec..0000000000 --- a/apps/gnb/adapters/e1ap_gateway_local_connector.cpp +++ /dev/null @@ -1,101 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "e1ap_gateway_local_connector.h" -#include "srsran/e1ap/common/e1ap_message.h" -#include "srsran/pcap/dlt_pcap.h" -#include "srsran/support/error_handling.h" - -using namespace srsran; -using namespace srs_cu_cp; - -namespace { - -/// Decorator for e1ap_message_notifier that writes the forwarded PDU to a pcap file. -class e1ap_pdu_pcap_notifier final : public e1ap_message_notifier -{ -public: - e1ap_pdu_pcap_notifier(std::unique_ptr decorated_, - dlt_pcap& pcap_writer_, - srslog::basic_logger& logger_) : - logger(logger_), pcap_writer(pcap_writer_), decorated(std::move(decorated_)) - { - } - - void on_new_message(const e1ap_message& msg) override - { - logger.debug("Received a PDU of type {}", msg.pdu.type().to_string()); - - if (pcap_writer.is_write_enabled()) { - byte_buffer buf; - asn1::bit_ref bref(buf); - if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) { - logger.error("Failed to pack PDU"); - } else { - pcap_writer.push_pdu(std::move(buf)); - } - } - - // Forward message to decorated class. - decorated->on_new_message(msg); - } - -private: - srslog::basic_logger& logger; - dlt_pcap& pcap_writer; - std::unique_ptr decorated; -}; - -} // namespace - -e1ap_gateway_local_connector::e1ap_gateway_local_connector(dlt_pcap& e1ap_pcap_writer_) : - e1ap_pcap_writer(e1ap_pcap_writer_) -{ -} - -void e1ap_gateway_local_connector::attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_cp_cu_up_mng_) -{ - cu_cp_cu_up_mng = &cu_cp_cu_up_mng_; -} - -std::unique_ptr e1ap_gateway_local_connector::handle_cu_up_connection_request( - std::unique_ptr cu_up_rx_pdu_notifier) -{ - report_fatal_error_if_not(cu_cp_cu_up_mng != nullptr, "CU-CP has not been attached to E1AP gateway."); - - // Decorate CU-UP RX notifier with pcap and logging. - if (e1ap_pcap_writer.is_write_enabled()) { - cu_up_rx_pdu_notifier = std::make_unique( - std::move(cu_up_rx_pdu_notifier), e1ap_pcap_writer, srslog::fetch_basic_logger("CU-UP-E1")); - } - - // Create direct connection between CU-CP and CU-UP notifier. - auto cu_cp_rx_pdu_notifier = cu_cp_cu_up_mng->handle_new_cu_up_connection(std::move(cu_up_rx_pdu_notifier)); - - // Decorate CU-CP RX notifier with pcap and logging. - if (e1ap_pcap_writer.is_write_enabled()) { - cu_cp_rx_pdu_notifier = std::make_unique( - std::move(cu_cp_rx_pdu_notifier), e1ap_pcap_writer, srslog::fetch_basic_logger("CU-CP-E1")); - } - - return cu_cp_rx_pdu_notifier; -} diff --git a/apps/gnb/gnb.cpp b/apps/gnb/gnb.cpp index 780a901a3e..63a070a1ab 100644 --- a/apps/gnb/gnb.cpp +++ b/apps/gnb/gnb.cpp @@ -36,10 +36,10 @@ #include "srsran/cu_up/cu_up_factory.h" #include "srsran/f1u/local_connector/f1u_local_connector.h" -#include "adapters/ngap_adapter.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" #include "srsran/support/io/io_broker_factory.h" -#include "adapters/e1ap_gateway_local_connector.h" +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" #include "srsran/f1ap/gateways/f1c_local_connector_factory.h" #include "srsran/gtpu/ngu_gateway.h" #include "srsran/support/backtrace.h" @@ -59,11 +59,6 @@ #include "apps/units/flexible_du/split_dynamic/dynamic_du_factory.h" -#include "srsran/ru/ru_adapters.h" -#include "srsran/ru/ru_dummy_factory.h" -#include "srsran/ru/ru_generic_factory.h" -#include "srsran/ru/ru_ofh_factory.h" - #include "apps/gnb/adapters/e2_gateway_remote_connector.h" #include "apps/services/e2_metric_connector_manager.h" #include "srsran/support/sysinfo.h" @@ -80,16 +75,12 @@ #include "../units/cu_cp/pcap_factory.h" #include "../units/cu_up/pcap_factory.h" #include "../units/flexible_du/du_high/pcap_factory.h" +#include "apps/units/cu_cp/cu_cp_builder.h" #include "apps/units/cu_up/cu_up_builder.h" #include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.h" #include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.h" #include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h" -#include "srsran/du/du_high_wrapper.h" -#include "srsran/du/du_wrapper.h" -#include "srsran/du_low/du_low.h" -#include "srsran/du_low/du_low_wrapper.h" - #ifdef DPDK_FOUND #include "srsran/hal/dpdk/dpdk_eal_factory.h" #endif @@ -124,72 +115,6 @@ static void local_signal_handler() is_running = false; } -/// Resolves the generic Radio Unit dependencies and adds them to the configuration. -static void configure_ru_generic_executors_and_notifiers(ru_generic_configuration& config, - const std::string& phy_log_level, - worker_manager& workers, - ru_uplink_plane_rx_symbol_notifier& symbol_notifier, - ru_timing_notifier& timing_notifier) -{ - config.rf_logger = &srslog::fetch_basic_logger("RF"); - config.radio_exec = workers.radio_exec; - config.statistics_printer_executor = workers.ru_printer_exec; - config.timing_notifier = &timing_notifier; - config.symbol_notifier = &symbol_notifier; - - for (unsigned i = 0, e = config.lower_phy_config.size(); i != e; ++i) { - lower_phy_configuration& low_phy_cfg = config.lower_phy_config[i]; - low_phy_cfg.logger = &srslog::fetch_basic_logger("PHY"); - low_phy_cfg.tx_task_executor = workers.lower_phy_tx_exec[i]; - low_phy_cfg.rx_task_executor = workers.lower_phy_rx_exec[i]; - low_phy_cfg.dl_task_executor = workers.lower_phy_dl_exec[i]; - low_phy_cfg.ul_task_executor = workers.lower_phy_ul_exec[i]; - low_phy_cfg.prach_async_executor = workers.lower_prach_exec[i]; - - low_phy_cfg.logger->set_level(srslog::str_to_basic_level(phy_log_level)); - } -} - -/// Resolves the Open Fronthaul Radio Unit dependencies and adds them to the configuration. -static void configure_ru_ofh_executors_and_notifiers(ru_ofh_configuration& config, - ru_ofh_dependencies& dependencies, - const log_appconfig& log_cfg, - worker_manager& workers, - ru_uplink_plane_rx_symbol_notifier& symbol_notifier, - ru_timing_notifier& timing_notifier, - ru_error_notifier& error_notifier) -{ - dependencies.logger = &srslog::fetch_basic_logger("OFH"); - dependencies.rt_timing_executor = workers.ru_timing_exec; - dependencies.timing_notifier = &timing_notifier; - dependencies.rx_symbol_notifier = &symbol_notifier; - dependencies.error_notifier = &error_notifier; - - // Configure sector. - for (unsigned i = 0, e = config.sector_configs.size(); i != e; ++i) { - dependencies.sector_dependencies.emplace_back(); - ru_ofh_sector_dependencies& sector_deps = dependencies.sector_dependencies.back(); - sector_deps.logger = dependencies.logger; - sector_deps.receiver_executor = workers.ru_rx_exec[i]; - sector_deps.transmitter_executor = workers.ru_tx_exec[i]; - sector_deps.downlink_executor = workers.ru_dl_exec[i]; - } -} - -/// Resolves the Dummy Radio Unit dependencies and adds them to the configuration. -static void configure_ru_dummy_executors_and_notifiers(ru_dummy_configuration& config, - ru_dummy_dependencies& dependencies, - const log_appconfig& log_cfg, - worker_manager& workers, - ru_uplink_plane_rx_symbol_notifier& symbol_notifier, - ru_timing_notifier& timing_notifier) -{ - dependencies.logger = &srslog::fetch_basic_logger("RU"); - dependencies.executor = workers.radio_exec; - dependencies.timing_notifier = &timing_notifier; - dependencies.symbol_notifier = &symbol_notifier; -} - static void initialize_log(const std::string& filename) { srslog::sink* log_sink = (filename == "stdout") ? srslog::create_stdout_sink() : srslog::create_file_sink(filename); @@ -259,9 +184,19 @@ int main(int argc, char** argv) configure_cli11_with_dynamic_du_unit_config_schema(app, du_unit_cfg); // Set the callback for the app calling all the autoderivation functions. - app.callback([&app, &gnb_cfg, &du_unit_cfg]() { + app.callback([&app, &gnb_cfg, &du_unit_cfg, &cu_cp_config]() { autoderive_gnb_parameters_after_parsing(app, gnb_cfg); autoderive_dynamic_du_parameters_after_parsing(app, du_unit_cfg); + + // Create the PLMN and TAC list from the cells. + std::vector plmns; + std::vector tacs; + for (const auto& cell : du_unit_cfg.du_high_cfg.config.cells_cfg) { + plmns.push_back(cell.cell.plmn); + tacs.push_back(cell.cell.tac); + } + + autoderive_cu_cp_parameters_after_parsing(app, cu_cp_config, std::move(plmns), std::move(tacs)); }); // Parse arguments. @@ -273,7 +208,8 @@ int main(int argc, char** argv) !validate_dynamic_du_unit_config(du_unit_cfg, (gnb_cfg.expert_execution_cfg.affinities.isolated_cpus) ? gnb_cfg.expert_execution_cfg.affinities.isolated_cpus.value() - : os_sched_affinity_bitmask::available_cpus())) { + : os_sched_affinity_bitmask::available_cpus()) || + !validate_plmn_and_tacs(du_unit_cfg.du_high_cfg.config, cu_cp_config)) { report_error("Invalid configuration detected.\n"); } @@ -335,7 +271,7 @@ int main(int argc, char** argv) check_cpu_governor(gnb_logger); check_drm_kms_polling(gnb_logger); - worker_manager workers{gnb_cfg, du_unit_cfg, cu_up_config.gtpu_queue_size}; + worker_manager workers{du_unit_cfg, gnb_cfg.expert_execution_cfg, gnb_cfg.pcap_cfg, cu_up_config.gtpu_queue_size}; // Set layer-specific pcap options. const auto& low_prio_cpu_mask = gnb_cfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask; @@ -357,7 +293,8 @@ int main(int argc, char** argv) : create_null_dlt_pcap(); std::unique_ptr f1c_gw = create_f1c_local_connector(f1c_local_connector_config{*f1ap_p}); - e1ap_gateway_local_connector e1ap_gw{*cu_up_pcaps[modules::cu_up::to_value(modules::cu_up::pcap_type::E1_AP)]}; + std::unique_ptr e1_gw = create_e1_local_connector( + e1_local_connector_config{*cu_up_pcaps[modules::cu_up::to_value(modules::cu_up::pcap_type::E1_AP)]}); // Create manager of timers for DU, CU-CP and CU-UP, which will be driven by the PHY slot ticks. timer_manager app_timers{256}; @@ -387,13 +324,13 @@ int main(int argc, char** argv) e2_metric_connector_manager e2_metric_connectors(du_unit_cfg.du_high_cfg.config.cells_cfg.size()); // Create NGAP Gateway. - std::unique_ptr ngap_adapter; + std::unique_ptr n2_client; { - using no_core_mode_t = srs_cu_cp::ngap_gateway_params::no_core; - using network_mode_t = srs_cu_cp::ngap_gateway_params::network; - using ngap_mode_t = variant; + using no_core_mode_t = srs_cu_cp::n2_connection_client_config::no_core; + using network_mode_t = srs_cu_cp::n2_connection_client_config::network; + using ngap_mode_t = std::variant; - ngap_adapter = srs_cu_cp::create_ngap_gateway(srs_cu_cp::ngap_gateway_params{ + n2_client = srs_cu_cp::create_n2_connection_client(srs_cu_cp::n2_connection_client_config{ *ngap_p, cu_cp_config.amf_cfg.no_core ? ngap_mode_t{no_core_mode_t{}} @@ -407,14 +344,14 @@ int main(int argc, char** argv) e2_gateway_remote_connector e2_gw{*epoll_broker, e2_du_nw_config, *e2ap_p}; // Create CU-CP config. - srs_cu_cp::cu_cp_configuration cu_cp_cfg = generate_cu_cp_config(du_unit_cfg.du_high_cfg.config, cu_cp_config); - cu_cp_cfg.cu_cp_executor = workers.cu_cp_exec; - cu_cp_cfg.cu_cp_e2_exec = workers.cu_cp_e2_exec; - cu_cp_cfg.ngap_notifier = ngap_adapter.get(); - cu_cp_cfg.timers = cu_timers; + cu_cp_build_dependencies cu_cp_dependencies; + cu_cp_dependencies.cu_cp_executor = workers.cu_cp_exec; + cu_cp_dependencies.cu_cp_e2_exec = workers.cu_cp_e2_exec; + cu_cp_dependencies.ngap_notifier = n2_client.get(); + cu_cp_dependencies.timers = cu_timers; // create CU-CP. - std::unique_ptr cu_cp_obj = create_cu_cp(cu_cp_cfg); + std::unique_ptr cu_cp_obj = build_cu_cp(cu_cp_config, cu_cp_dependencies); // Create console helper object for commands and metrics printing. console_helper console( @@ -425,11 +362,11 @@ int main(int argc, char** argv) metrics_log_helper metrics_logger(srslog::fetch_basic_logger("METRICS")); // Connect NGAP adpter to CU-CP to pass NGAP messages. - ngap_adapter->connect_cu_cp(cu_cp_obj->get_ng_handler().get_ngap_message_handler(), - cu_cp_obj->get_ng_handler().get_ngap_event_handler()); + n2_client->connect_cu_cp(cu_cp_obj->get_ng_handler().get_ngap_message_handler(), + cu_cp_obj->get_ng_handler().get_ngap_event_handler()); // Connect E1AP to CU-CP. - e1ap_gw.attach_cu_cp(cu_cp_obj->get_e1_handler()); + e1_gw->attach_cu_cp(cu_cp_obj->get_e1_handler()); // Connect F1-C to CU-CP. f1c_gw->attach_cu_cp(cu_cp_obj->get_f1c_handler()); @@ -447,95 +384,27 @@ int main(int argc, char** argv) std::unique_ptr cu_up_obj = build_cu_up(cu_up_config, workers, - e1ap_gw, + *e1_gw, *f1u_conn->get_f1u_cu_up_gateway(), *cu_up_pcaps[modules::cu_up::to_value(modules::cu_up::pcap_type::GTPU)].get(), *cu_timers, *epoll_broker); cu_up_obj->start(); - std::vector du_cells = generate_du_cell_config(du_unit_cfg.du_high_cfg.config); - - // Radio Unit instantiation block. - ru_configuration ru_cfg = generate_ru_config(gnb_cfg, du_cells, du_unit_cfg); - - upper_ru_ul_adapter ru_ul_adapt(du_unit_cfg.du_high_cfg.config.cells_cfg.size()); - upper_ru_timing_adapter ru_timing_adapt(du_unit_cfg.du_high_cfg.config.cells_cfg.size()); - upper_ru_error_adapter ru_error_adapt(du_unit_cfg.du_high_cfg.config.cells_cfg.size()); - - std::unique_ptr ru_object; - if (variant_holds_alternative(ru_cfg.config)) { - ru_ofh_dependencies ru_dependencies; - configure_ru_ofh_executors_and_notifiers(variant_get(ru_cfg.config), - ru_dependencies, - gnb_cfg.log_cfg, - workers, - ru_ul_adapt, - ru_timing_adapt, - ru_error_adapt); - - ru_object = create_ofh_ru(variant_get(ru_cfg.config), std::move(ru_dependencies)); - } else if (variant_holds_alternative(ru_cfg.config)) { - configure_ru_generic_executors_and_notifiers(variant_get(ru_cfg.config), - du_unit_cfg.du_low_cfg.loggers.phy_level, - workers, - ru_ul_adapt, - ru_timing_adapt); - - ru_object = create_generic_ru(variant_get(ru_cfg.config)); - - // Set the generic RU controller for the GNB console. - console.set_ru_controller(ru_object->get_controller()); - } else { - ru_dummy_dependencies ru_dependencies; - configure_ru_dummy_executors_and_notifiers(variant_get(ru_cfg.config), - ru_dependencies, - gnb_cfg.log_cfg, - workers, - ru_ul_adapt, - ru_timing_adapt); - - ru_object = create_dummy_ru(variant_get(ru_cfg.config), ru_dependencies); - } - report_error_if_not(ru_object, "Unable to create Radio Unit."); - gnb_logger.info("Radio Unit created successfully"); - - upper_ru_dl_rg_adapter ru_dl_rg_adapt; - upper_ru_ul_request_adapter ru_ul_request_adapt; - ru_dl_rg_adapt.connect(ru_object->get_downlink_plane_handler()); - ru_ul_request_adapt.connect(ru_object->get_uplink_plane_handler()); - - // Instantiate one DU per cell. - std::vector> du_inst = make_gnb_dus(gnb_cfg, - du_unit_cfg, - du_cells, - workers, - ru_dl_rg_adapt, - ru_ul_request_adapt, - *f1c_gw, - *f1u_conn->get_f1u_du_gateway(), - app_timers, - *mac_p, - *rlc_p, - console, - metrics_logger, - e2_gw, - e2_metric_connectors, - rlc_json_plotter, - *hub); - - for (unsigned sector_id = 0, sector_end = du_inst.size(); sector_id != sector_end; ++sector_id) { - auto& du = du_inst[sector_id]; - auto& upper = du->get_du_low_wrapper().get_du_low().get_upper_phy(0); - - // Make connections between DU and RU. - ru_ul_adapt.map_handler(sector_id, upper.get_rx_symbol_handler()); - ru_timing_adapt.map_handler(sector_id, upper.get_timing_handler()); - ru_error_adapt.map_handler(sector_id, upper.get_error_handler()); - - // Start DU execution. - du->start(); - } + // Instantiate one DU. + std::unique_ptr du_inst = create_du(du_unit_cfg, + workers, + *f1c_gw, + *f1u_conn->get_f1u_du_gateway(), + app_timers, + *mac_p, + *rlc_p, + console, + metrics_logger, + e2_gw, + e2_metric_connectors, + rlc_json_plotter, + *hub); // Move all the DLT PCAPs to a container. std::vector> dlt_pcaps = std::move(cu_up_pcaps); @@ -544,10 +413,7 @@ int main(int argc, char** argv) dlt_pcaps.push_back(std::move(e2ap_p)); // Start processing. - gnb_logger.info("Starting Radio Unit..."); - ru_object->get_controller().start(); - gnb_logger.info("Radio Unit started successfully"); - + du_inst->start(); console.on_app_running(); while (is_running) { @@ -556,14 +422,8 @@ int main(int argc, char** argv) console.on_app_stopping(); - gnb_logger.info("Stopping Radio Unit..."); - ru_object->get_controller().stop(); - gnb_logger.info("Radio Unit notify_stop successfully"); - // Stop DU activity. - for (auto& du : du_inst) { - du->stop(); - } + du_inst->stop(); // Stop CU-UP activity. cu_up_obj->stop(); @@ -572,7 +432,7 @@ int main(int argc, char** argv) cu_cp_obj->stop(); gnb_logger.info("Closing network connections..."); - ngap_adapter->disconnect(); + n2_client->disconnect(); gnb_logger.info("Network connections closed successfully"); if (gnb_cfg.e2_cfg.enable_du_e2) { diff --git a/apps/gnb/gnb_appconfig_translators.cpp b/apps/gnb/gnb_appconfig_translators.cpp index 9b9df5b220..56a6dd668d 100644 --- a/apps/gnb/gnb_appconfig_translators.cpp +++ b/apps/gnb/gnb_appconfig_translators.cpp @@ -22,193 +22,12 @@ #include "gnb_appconfig_translators.h" #include "apps/units/cu_cp/cu_cp_unit_config.h" -#include "apps/units/cu_up/cu_up_unit_config.h" -#include "apps/units/flexible_du/split_dynamic/dynamic_du_unit_config.h" #include "gnb_appconfig.h" -#include "srsran/cu_cp/cu_cp_configuration_helpers.h" -#include "srsran/cu_up/cu_up_configuration_helpers.h" -#include "srsran/du/du_cell_config_helpers.h" -#include "srsran/du/du_cell_config_validation.h" -#include "srsran/du/du_update_config_helpers.h" -#include "srsran/e2/e2ap_configuration.h" -#include "srsran/e2/e2ap_configuration_helpers.h" -#include "srsran/ran/prach/prach_configuration.h" -#include "srsran/ran/prach/prach_helper.h" #include "srsran/ran/subcarrier_spacing.h" -#include "srsran/rlc/rlc_srb_config_factory.h" -#include "srsran/scheduler/config/cell_config_builder_params.h" -#include "srsran/scheduler/config/csi_helper.h" -#include "srsran/scheduler/config/scheduler_expert_config_validator.h" -#include "srsran/scheduler/config/serving_cell_config_factory.h" -#include "srsran/support/math_utils.h" -#include -#include using namespace srsran; using namespace std::chrono_literals; -/// Static configuration that the gnb supports. -static constexpr cyclic_prefix cp = cyclic_prefix::NORMAL; - -static std::map generate_cu_cp_qos_config(const cu_cp_unit_config& config) -{ - std::map out_cfg = {}; - if (config.qos_cfg.empty()) { - out_cfg = config_helpers::make_default_cu_cp_qos_config_list(); - return out_cfg; - } - - for (const auto& qos : config.qos_cfg) { - if (out_cfg.find(qos.five_qi) != out_cfg.end()) { - report_error("Duplicate 5QI configuration: {}\n", qos.five_qi); - } - // Convert PDCP config - pdcp_config& out_pdcp = out_cfg[qos.five_qi].pdcp; - - // RB type - out_pdcp.rb_type = pdcp_rb_type::drb; - - // RLC mode - rlc_mode mode = {}; - if (!from_string(mode, qos.rlc.mode)) { - report_error("Invalid RLC mode: {}, mode={}\n", qos.five_qi, qos.rlc.mode); - } - if (mode == rlc_mode::um_bidir || mode == rlc_mode::um_unidir_ul || mode == rlc_mode::um_unidir_dl) { - out_pdcp.rlc_mode = pdcp_rlc_mode::um; - } else if (mode == rlc_mode::am) { - out_pdcp.rlc_mode = pdcp_rlc_mode::am; - } else { - report_error("Invalid RLC mode: {}, mode={}\n", qos.five_qi, qos.rlc.mode); - } - - // Integrity Protection required - out_pdcp.integrity_protection_required = qos.pdcp.integrity_protection_required; - - // Ciphering required - out_pdcp.ciphering_required = true; - - // > Tx - // >> SN size - if (!pdcp_sn_size_from_uint(out_pdcp.tx.sn_size, qos.pdcp.tx.sn_field_length)) { - report_error("Invalid PDCP TX SN: {}, SN={}\n", qos.five_qi, qos.pdcp.tx.sn_field_length); - } - - // >> discard timer - out_pdcp.tx.discard_timer = pdcp_discard_timer{}; - if (!pdcp_discard_timer_from_int(out_pdcp.tx.discard_timer.value(), qos.pdcp.tx.discard_timer)) { - report_error("Invalid PDCP discard timer. 5QI {} discard_timer {}\n", qos.five_qi, qos.pdcp.tx.discard_timer); - } - - // >> status report required - out_pdcp.tx.status_report_required = qos.pdcp.tx.status_report_required; - - // > Rx - // >> SN size - if (!pdcp_sn_size_from_uint(out_pdcp.rx.sn_size, qos.pdcp.rx.sn_field_length)) { - report_error("Invalid PDCP RX SN: {}, SN={}\n", qos.five_qi, qos.pdcp.rx.sn_field_length); - } - - // >> out of order delivery - out_pdcp.rx.out_of_order_delivery = qos.pdcp.rx.out_of_order_delivery; - - // >> t-Reordering - if (!pdcp_t_reordering_from_int(out_pdcp.rx.t_reordering, qos.pdcp.rx.t_reordering)) { - report_error("Invalid PDCP t-Reordering. {} t-Reordering {}\n", qos.five_qi, qos.pdcp.rx.t_reordering); - } - } - return out_cfg; -} - -static security::preferred_integrity_algorithms -generate_preferred_integrity_algorithms_list(const cu_cp_unit_config& config) -{ - // String splitter helper - auto split = [](const std::string& s, char delim) -> std::vector { - std::vector result; - std::stringstream ss(s); - for (std::string item; getline(ss, item, delim);) { - result.push_back(item); - } - return result; - }; - - // > Remove spaces, convert to lower case and split on comma - std::string nia_preference_list = config.security_config.nia_preference_list; - nia_preference_list.erase(std::remove_if(nia_preference_list.begin(), nia_preference_list.end(), ::isspace), - nia_preference_list.end()); - std::transform(nia_preference_list.begin(), - nia_preference_list.end(), - nia_preference_list.begin(), - [](unsigned char c) { return std::tolower(c); }); - std::vector nea_v = split(nia_preference_list, ','); - - security::preferred_integrity_algorithms algo_list = {}; - int idx = 0; - for (const std::string& nea : nea_v) { - if (nea == "nia0") { - algo_list[idx] = security::integrity_algorithm::nia0; - } else if (nea == "nia1") { - algo_list[idx] = security::integrity_algorithm::nia1; - } else if (nea == "nia2") { - algo_list[idx] = security::integrity_algorithm::nia2; - } else if (nea == "nia3") { - algo_list[idx] = security::integrity_algorithm::nia3; - } - idx++; - } - return algo_list; -} - -static security::preferred_ciphering_algorithms -generate_preferred_ciphering_algorithms_list(const cu_cp_unit_config& config) -{ - // String splitter helper - auto split = [](const std::string& s, char delim) -> std::vector { - std::vector result; - std::stringstream ss(s); - for (std::string item; getline(ss, item, delim);) { - result.push_back(item); - } - return result; - }; - - // > Remove spaces, convert to lower case and split on comma - std::string nea_preference_list = config.security_config.nea_preference_list; - nea_preference_list.erase(std::remove_if(nea_preference_list.begin(), nea_preference_list.end(), ::isspace), - nea_preference_list.end()); - std::transform(nea_preference_list.begin(), - nea_preference_list.end(), - nea_preference_list.begin(), - [](unsigned char c) { return std::tolower(c); }); - std::vector nea_v = split(nea_preference_list, ','); - - security::preferred_ciphering_algorithms algo_list = {}; - int idx = 0; - for (const std::string& nea : nea_v) { - if (nea == "nea0") { - algo_list[idx] = security::ciphering_algorithm::nea0; - } else if (nea == "nea1") { - algo_list[idx] = security::ciphering_algorithm::nea1; - } else if (nea == "nea2") { - algo_list[idx] = security::ciphering_algorithm::nea2; - } else if (nea == "nea3") { - algo_list[idx] = security::ciphering_algorithm::nea3; - } - idx++; - } - return algo_list; -} - -srs_cu_cp::rrc_ssb_mtc srsran::generate_rrc_ssb_mtc(unsigned period, unsigned offset, unsigned duration) -{ - srs_cu_cp::rrc_ssb_mtc ssb_mtc; - ssb_mtc.periodicity_and_offset.periodicity = (srs_cu_cp::rrc_periodicity_and_offset::periodicity_t)period; - ssb_mtc.periodicity_and_offset.offset = offset; - ssb_mtc.dur = duration; - - return ssb_mtc; -} - srsran::sctp_network_connector_config srsran::generate_ngap_nw_config(const cu_cp_unit_amf_config& config) { srsran::sctp_network_connector_config out_cfg; @@ -243,1540 +62,6 @@ srsran::sctp_network_connector_config srsran::generate_ngap_nw_config(const cu_c return out_cfg; } -srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const du_high_unit_config& config, - const cu_cp_unit_config& cu_cfg) -{ - srs_cu_cp::cu_cp_configuration out_cfg = config_helpers::make_default_cu_cp_config(); - out_cfg.max_nof_dus = cu_cfg.max_nof_dus; - out_cfg.max_nof_cu_ups = cu_cfg.max_nof_cu_ups; - - out_cfg.ngap_config.gnb_id = cu_cfg.gnb_id; - out_cfg.ngap_config.ran_node_name = cu_cfg.ran_node_name; - out_cfg.ngap_config.slice_configurations = cu_cfg.slice_cfg; - - // :TODO: What happens when there are multiple cells? - const du_high_unit_base_cell_config& cell = config.cells_cfg.front().cell; - out_cfg.ngap_config.plmn = cell.plmn; - out_cfg.ngap_config.tac = cell.tac; - - out_cfg.rrc_config.gnb_id = cu_cfg.gnb_id; - out_cfg.rrc_config.force_reestablishment_fallback = cu_cfg.rrc_config.force_reestablishment_fallback; - out_cfg.rrc_config.rrc_procedure_timeout_ms = cu_cfg.rrc_config.rrc_procedure_timeout_ms; - out_cfg.rrc_config.int_algo_pref_list = generate_preferred_integrity_algorithms_list(cu_cfg); - out_cfg.rrc_config.enc_algo_pref_list = generate_preferred_ciphering_algorithms_list(cu_cfg); - out_cfg.rrc_config.drb_config = generate_cu_cp_qos_config(cu_cfg); - - if (!from_string(out_cfg.default_security_indication.integrity_protection_ind, - cu_cfg.security_config.integrity_protection)) { - report_error("Invalid value for integrity_protection={}\n", cu_cfg.security_config.integrity_protection); - } - if (!from_string(out_cfg.default_security_indication.confidentiality_protection_ind, - cu_cfg.security_config.confidentiality_protection)) { - report_error("Invalid value for confidentiality_protection={}\n", - cu_cfg.security_config.confidentiality_protection); - } - - out_cfg.ue_config.inactivity_timer = std::chrono::seconds{cu_cfg.inactivity_timer}; - out_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; - out_cfg.ngap_config.pdu_session_setup_timeout = std::chrono::seconds{cu_cfg.pdu_session_setup_timeout}; - out_cfg.statistics_report_period = std::chrono::seconds{cu_cfg.metrics.cu_cp_statistics_report_period}; - - out_cfg.mobility_config.mobility_manager_config.trigger_handover_from_measurements = - cu_cfg.mobility_config.trigger_handover_from_measurements; - - // F1AP-CU config. - out_cfg.f1ap_config.ue_context_setup_timeout = std::chrono::milliseconds{cu_cfg.f1ap_config.ue_context_setup_timeout}; - out_cfg.f1ap_config.json_log_enabled = cu_cfg.loggers.f1ap_json_enabled; - - // Convert appconfig's cell list into cell manager type. - for (const auto& app_cfg_item : cu_cfg.mobility_config.cells) { - srs_cu_cp::cell_meas_config meas_cfg_item; - meas_cfg_item.serving_cell_cfg.nci = app_cfg_item.nr_cell_id; - if (app_cfg_item.periodic_report_cfg_id.has_value()) { - meas_cfg_item.periodic_report_cfg_id = - srs_cu_cp::uint_to_report_cfg_id(app_cfg_item.periodic_report_cfg_id.value()); - } - - if (app_cfg_item.gnb_id_bit_length.has_value()) { - meas_cfg_item.serving_cell_cfg.gnb_id = - config_helpers::get_gnb_id(app_cfg_item.nr_cell_id, app_cfg_item.gnb_id_bit_length.value()); - } - meas_cfg_item.serving_cell_cfg.pci = app_cfg_item.pci; - meas_cfg_item.serving_cell_cfg.band = app_cfg_item.band; - meas_cfg_item.serving_cell_cfg.ssb_arfcn = app_cfg_item.ssb_arfcn; - if (app_cfg_item.ssb_scs.has_value()) { - meas_cfg_item.serving_cell_cfg.ssb_scs.emplace() = - to_subcarrier_spacing(std::to_string(app_cfg_item.ssb_scs.value())); - } - if (app_cfg_item.ssb_duration.has_value() && app_cfg_item.ssb_offset.has_value() && - app_cfg_item.ssb_period.has_value()) { - // Add MTC config. - meas_cfg_item.serving_cell_cfg.ssb_mtc.emplace() = generate_rrc_ssb_mtc( - app_cfg_item.ssb_period.value(), app_cfg_item.ssb_offset.value(), app_cfg_item.ssb_duration.value()); - } - - for (const auto& ncell : app_cfg_item.ncells) { - srs_cu_cp::neighbor_cell_meas_config ncell_meas_cfg; - ncell_meas_cfg.nci = ncell.nr_cell_id; - for (const auto& report_id : ncell.report_cfg_ids) { - ncell_meas_cfg.report_cfg_ids.push_back(srs_cu_cp::uint_to_report_cfg_id(report_id)); - } - - meas_cfg_item.ncells.push_back(ncell_meas_cfg); - } - - // Store config. - out_cfg.mobility_config.meas_manager_config.cells[meas_cfg_item.serving_cell_cfg.nci] = meas_cfg_item; - } - - // Convert report config. - for (const auto& report_cfg_item : cu_cfg.mobility_config.report_configs) { - srs_cu_cp::rrc_report_cfg_nr report_cfg; - - if (report_cfg_item.report_type == "periodical") { - srs_cu_cp::rrc_periodical_report_cfg periodical; - - periodical.rs_type = srs_cu_cp::rrc_nr_rs_type::ssb; - if (report_cfg_item.report_interval_ms.has_value()) { - periodical.report_interv = report_cfg_item.report_interval_ms.value(); - } else { - periodical.report_interv = 1024; - } - periodical.report_amount = -1; - periodical.report_quant_cell.rsrp = true; - periodical.report_quant_cell.rsrq = true; - periodical.report_quant_cell.sinr = true; - periodical.max_report_cells = 4; - - srs_cu_cp::rrc_meas_report_quant report_quant_rs_idxes; - report_quant_rs_idxes.rsrp = true; - report_quant_rs_idxes.rsrq = true; - report_quant_rs_idxes.sinr = true; - periodical.report_quant_rs_idxes = report_quant_rs_idxes; - - periodical.max_nrof_rs_idxes_to_report = 4; - periodical.include_beam_meass = true; - periodical.use_allowed_cell_list = false; - - report_cfg.periodical = periodical; - } else { - srs_cu_cp::rrc_event_trigger_cfg event_trigger_cfg; - - // event id - // A3 event config is currently the only supported event. - auto& event_a3 = event_trigger_cfg.event_id.event_a3.emplace(); - - if (report_cfg_item.a3_report_type.empty() or !report_cfg_item.a3_offset_db.has_value() or - !report_cfg_item.a3_hysteresis_db.has_value()) { - report_error("Invalid measurement report configuration.\n"); - } - - if (report_cfg_item.a3_report_type == "rsrp") { - event_a3.a3_offset.rsrp = report_cfg_item.a3_offset_db.value(); - } else if (report_cfg_item.a3_report_type == "rsrq") { - event_a3.a3_offset.rsrq = report_cfg_item.a3_offset_db.value(); - } else if (report_cfg_item.a3_report_type == "sinr") { - event_a3.a3_offset.sinr = report_cfg_item.a3_offset_db.value(); - } - - event_a3.report_on_leave = false; - - event_a3.hysteresis = report_cfg_item.a3_hysteresis_db.value(); - event_a3.time_to_trigger = report_cfg_item.a3_time_to_trigger_ms.value(); - - event_a3.use_allowed_cell_list = false; - - event_trigger_cfg.rs_type = srs_cu_cp::rrc_nr_rs_type::ssb; - if (report_cfg_item.report_interval_ms.has_value()) { - event_trigger_cfg.report_interv = report_cfg_item.report_interval_ms.value(); - } else { - event_trigger_cfg.report_interv = 1024; - } - event_trigger_cfg.report_amount = -1; - event_trigger_cfg.report_quant_cell.rsrp = true; - event_trigger_cfg.report_quant_cell.rsrq = true; - event_trigger_cfg.report_quant_cell.sinr = true; - event_trigger_cfg.max_report_cells = 4; - - srs_cu_cp::rrc_meas_report_quant report_quant_rs_idxes; - report_quant_rs_idxes.rsrp = true; - report_quant_rs_idxes.rsrq = true; - report_quant_rs_idxes.sinr = true; - event_trigger_cfg.report_quant_rs_idxes = report_quant_rs_idxes; - - report_cfg.event_triggered = event_trigger_cfg; - } - - // Store config. - out_cfg.mobility_config.meas_manager_config - .report_config_ids[srs_cu_cp::uint_to_report_cfg_id(report_cfg_item.report_cfg_id)] = report_cfg; - } - - if (!config_helpers::is_valid_configuration(out_cfg)) { - report_error("Invalid CU-CP configuration.\n"); - } - - return out_cfg; -} - -static pcch_config generate_pcch_config(const du_high_unit_base_cell_config& cell) -{ - pcch_config cfg{}; - switch (cell.paging_cfg.default_paging_cycle) { - case 32: - cfg.default_paging_cycle = paging_cycle::rf32; - break; - case 64: - cfg.default_paging_cycle = paging_cycle::rf64; - break; - case 128: - cfg.default_paging_cycle = paging_cycle::rf128; - break; - case 256: - cfg.default_paging_cycle = paging_cycle::rf256; - break; - default: - report_error( - "Invalid default paging cycle={} for cell with pci={}\n", cell.paging_cfg.default_paging_cycle, cell.pci); - } - cfg.nof_pf = cell.paging_cfg.nof_pf; - if (cell.paging_cfg.pf_offset > (static_cast(cfg.nof_pf) - 1)) { - report_error("Invalid paging frame offset value={} for cell with pci={}. Value must be less than or equal to {}\n", - cell.paging_cfg.pf_offset, - cell.pci, - (static_cast(cfg.nof_pf) - 1)); - } - cfg.paging_frame_offset = cell.paging_cfg.pf_offset; - switch (cell.paging_cfg.nof_po_per_pf) { - case 1: - cfg.ns = pcch_config::nof_po_per_pf::one; - break; - case 2: - cfg.ns = pcch_config::nof_po_per_pf::two; - break; - case 4: - cfg.ns = pcch_config::nof_po_per_pf::four; - break; - default: - report_error("Invalid number of paging occasions per paging frame={} for cell with pci={}\n", - cell.paging_cfg.nof_po_per_pf, - cell.pci); - } - - return cfg; -} - -static unsigned get_nof_rbs(const du_high_unit_base_cell_config& cell_cfg) -{ - return band_helper::get_n_rbs_from_bw( - cell_cfg.channel_bw_mhz, cell_cfg.common_scs, band_helper::get_freq_range(*cell_cfg.band)); -} - -static tdd_ul_dl_config_common generate_tdd_pattern(subcarrier_spacing scs, const du_high_unit_tdd_ul_dl_config& config) -{ - tdd_ul_dl_config_common out; - out.ref_scs = scs; - - out.pattern1.dl_ul_tx_period_nof_slots = config.pattern1.dl_ul_period_slots; - out.pattern1.nof_dl_slots = config.pattern1.nof_dl_slots; - out.pattern1.nof_dl_symbols = config.pattern1.nof_dl_symbols; - out.pattern1.nof_ul_slots = config.pattern1.nof_ul_slots; - out.pattern1.nof_ul_symbols = config.pattern1.nof_ul_symbols; - - if (config.pattern2.has_value()) { - out.pattern2.emplace(); - out.pattern2->dl_ul_tx_period_nof_slots = config.pattern2->dl_ul_period_slots; - out.pattern2->nof_dl_slots = config.pattern2->nof_dl_slots; - out.pattern2->nof_dl_symbols = config.pattern2->nof_dl_symbols; - out.pattern2->nof_ul_slots = config.pattern2->nof_ul_slots; - out.pattern2->nof_ul_symbols = config.pattern2->nof_ul_symbols; - } - return out; -} - -static void fill_csi_resources(serving_cell_config& out_cell, const du_high_unit_base_cell_config& cell_cfg) -{ - const auto& csi_cfg = cell_cfg.csi_cfg; - - csi_helper::csi_builder_params csi_params{}; - csi_params.pci = cell_cfg.pci; - csi_params.nof_rbs = get_nof_rbs(cell_cfg); - csi_params.nof_ports = cell_cfg.nof_antennas_dl; - csi_params.csi_rs_period = static_cast(csi_cfg.csi_rs_period_msec * - get_nof_slots_per_subframe(cell_cfg.common_scs)); - if (cell_cfg.tdd_ul_dl_cfg.has_value()) { - if (not csi_helper::derive_valid_csi_rs_slot_offsets( - csi_params, - csi_cfg.meas_csi_slot_offset, - csi_cfg.tracking_csi_slot_offset, - csi_cfg.zp_csi_slot_offset, - generate_tdd_pattern(cell_cfg.common_scs, *cell_cfg.tdd_ul_dl_cfg))) { - report_error("Unable to derive valid CSI-RS slot offsets and period for cell with pci={}\n", cell_cfg.pci); - } - } else { - csi_params.meas_csi_slot_offset = csi_cfg.meas_csi_slot_offset.has_value() ? *csi_cfg.meas_csi_slot_offset : 2; - csi_params.zp_csi_slot_offset = csi_cfg.zp_csi_slot_offset.has_value() ? *csi_cfg.zp_csi_slot_offset : 2; - csi_params.tracking_csi_slot_offset = - csi_cfg.tracking_csi_slot_offset.has_value() ? *csi_cfg.tracking_csi_slot_offset : 12; - } - - // Generate basic csiMeasConfig. - out_cell.csi_meas_cfg = csi_helper::make_csi_meas_config(csi_params); - - // Set power control offset for all nzp-CSI-RS resources. - for (auto& nzp_csi_res : out_cell.csi_meas_cfg->nzp_csi_rs_res_list) { - nzp_csi_res.pwr_ctrl_offset = cell_cfg.csi_cfg.pwr_ctrl_offset; - } - - // Set CQI table according to the MCS table used for PDSCH. - switch (cell_cfg.pdsch_cfg.mcs_table) { - case pdsch_mcs_table::qam64: - out_cell.csi_meas_cfg->csi_report_cfg_list[0].cqi_table = cqi_table_t::table1; - break; - case pdsch_mcs_table::qam256: - out_cell.csi_meas_cfg->csi_report_cfg_list[0].cqi_table = cqi_table_t::table2; - break; - case pdsch_mcs_table::qam64LowSe: - out_cell.csi_meas_cfg->csi_report_cfg_list[0].cqi_table = cqi_table_t::table3; - break; - default: - report_error("Invalid MCS table={} for cell with pci={}\n", cell_cfg.pdsch_cfg.mcs_table, cell_cfg.pci); - } - - // Generate zp-CSI-RS resources. - out_cell.init_dl_bwp.pdsch_cfg->zp_csi_rs_res_list = csi_helper::make_periodic_zp_csi_rs_resource_list(csi_params); - out_cell.init_dl_bwp.pdsch_cfg->p_zp_csi_rs_res = csi_helper::make_periodic_zp_csi_rs_resource_set(csi_params); -} - -static sib2_info create_sib2_info() -{ - sib2_info sib2; - sib2.q_hyst_db = 3; - sib2.q_rx_lev_min = -70; - sib2.s_intra_search_p = 31; - sib2.t_reselection_nr = 1; - sib2.cell_reselection_priority = 6; - sib2.thresh_serving_low_p = 0; - return sib2; -} - -static sib19_info create_sib19_info(const du_high_unit_config& config) -{ - sib19_info sib19; - sib19.cell_specific_koffset = config.ntn_cfg.value().cell_specific_koffset; - sib19.ephemeris_info = config.ntn_cfg.value().ephemeris_info; - - // These ephemeris parameters are all scaled in accordance with NIMA TR 8350.2. - if (variant_holds_alternative(sib19.ephemeris_info.value())) { - variant_get(sib19.ephemeris_info.value()).position_x /= 1.3; - variant_get(sib19.ephemeris_info.value()).position_y /= 1.3; - variant_get(sib19.ephemeris_info.value()).position_z /= 1.3; - variant_get(sib19.ephemeris_info.value()).velocity_vx /= 0.06; - variant_get(sib19.ephemeris_info.value()).velocity_vy /= 0.06; - variant_get(sib19.ephemeris_info.value()).velocity_vz /= 0.06; - } else if (variant_holds_alternative(sib19.ephemeris_info.value())) { - variant_get(sib19.ephemeris_info.value()).semi_major_axis -= 6500000; - variant_get(sib19.ephemeris_info.value()).semi_major_axis /= 0.004249; - variant_get(sib19.ephemeris_info.value()).eccentricity /= 0.00000001431; - variant_get(sib19.ephemeris_info.value()).periapsis /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).longitude /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).inclination /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).mean_anomaly /= 0.00000002341; - } - if (config.ntn_cfg.value().distance_threshold.has_value()) { - sib19.distance_thres = config.ntn_cfg.value().distance_threshold.value(); - } - if (config.ntn_cfg.value().epoch_time.has_value()) { - sib19.epoch_time = config.ntn_cfg.value().epoch_time.value(); - } - if (config.ntn_cfg.value().k_mac.has_value()) { - sib19.k_mac = config.ntn_cfg.value().k_mac.value(); - } - if (config.ntn_cfg.value().ta_info.has_value()) { - sib19.ta_info = config.ntn_cfg.value().ta_info.value(); - } - if (config.ntn_cfg.value().reference_location.has_value()) { - sib19.ref_location = config.ntn_cfg.value().reference_location.value(); - } - return sib19; -} - -std::vector srsran::generate_du_cell_config(const du_high_unit_config& config) -{ - srslog::basic_logger& logger = srslog::fetch_basic_logger("GNB", false); - - 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; - param.pci = base_cell.pci; - param.scs_common = base_cell.common_scs; - param.channel_bw_mhz = base_cell.channel_bw_mhz; - param.dl_arfcn = base_cell.dl_arfcn; - param.band = *base_cell.band; - // Enable CSI-RS if the PDSCH mcs is dynamic (min_ue_mcs != max_ue_mcs). - param.csi_rs_enabled = base_cell.csi_cfg.csi_rs_enabled; - param.nof_dl_ports = base_cell.nof_antennas_dl; - param.search_space0_index = base_cell.pdcch_cfg.common.ss0_index; - param.min_k1 = base_cell.pucch_cfg.min_k1; - param.min_k2 = base_cell.pusch_cfg.min_k2; - param.coreset0_index = base_cell.pdcch_cfg.common.coreset0_index; - // Set maximum CORESET#0 duration to 1 OFDM symbol for BW > 50Mhz to spread CORESET RBs across the BW. This results - // in one extra symbol to be used for PDSCH. - if (base_cell.pdcch_cfg.common.max_coreset0_duration.has_value()) { - param.max_coreset0_duration = base_cell.pdcch_cfg.common.max_coreset0_duration.value(); - } else if (param.channel_bw_mhz > bs_channel_bandwidth_fr1::MHz50) { - param.max_coreset0_duration = 1; - } - const unsigned nof_crbs = band_helper::get_n_rbs_from_bw( - base_cell.channel_bw_mhz, param.scs_common, band_helper::get_freq_range(*param.band)); - - std::optional ssb_freq_loc; - if (base_cell.pdcch_cfg.common.coreset0_index.has_value()) { - ssb_freq_loc = - band_helper::get_ssb_coreset0_freq_location_for_cset0_idx(base_cell.dl_arfcn, - *param.band, - nof_crbs, - base_cell.common_scs, - base_cell.common_scs, - param.search_space0_index, - base_cell.pdcch_cfg.common.coreset0_index.value()); - } else { - ssb_freq_loc = band_helper::get_ssb_coreset0_freq_location(base_cell.dl_arfcn, - *param.band, - nof_crbs, - base_cell.common_scs, - base_cell.common_scs, - param.search_space0_index, - param.max_coreset0_duration); - } - - if (!ssb_freq_loc.has_value()) { - report_error("Unable to derive a valid SSB pointA and k_SSB for cell id ({}).\n", base_cell.pci); - } - - param.offset_to_point_a = (*ssb_freq_loc).offset_to_point_A; - param.k_ssb = (*ssb_freq_loc).k_ssb; - param.coreset0_index = (*ssb_freq_loc).coreset0_idx; - - // Set TDD pattern. - if (band_helper::get_duplex_mode(*param.band) == duplex_mode::TDD) { - param.tdd_ul_dl_cfg_common.emplace(generate_tdd_pattern(param.scs_common, cell.cell.tdd_ul_dl_cfg.value())); - } - - // Create the configuration. - out_cfg.push_back(config_helpers::make_default_du_cell_config(param)); - du_cell_config& out_cell = out_cfg.back(); - - // Set the rest of the parameters. - out_cell.nr_cgi.plmn = base_cell.plmn; - out_cell.nr_cgi.nci = config_helpers::make_nr_cell_identity(config.gnb_id, cell_id); - out_cell.tac = base_cell.tac; - out_cell.searchspace0_idx = param.search_space0_index; - - // Cell selection parameters. - out_cell.cell_sel_info.q_rx_lev_min = base_cell.q_rx_lev_min; - out_cell.cell_sel_info.q_qual_min = base_cell.q_qual_min; - - // SSB config. - out_cell.ssb_cfg.ssb_period = (ssb_periodicity)base_cell.ssb_cfg.ssb_period_msec; - out_cell.ssb_cfg.ssb_block_power = base_cell.ssb_cfg.ssb_block_power; - out_cell.ssb_cfg.pss_to_sss_epre = base_cell.ssb_cfg.pss_to_sss_epre; - - // SI message config. - if (not base_cell.sib_cfg.si_sched_info.empty()) { - out_cell.si_config.emplace(); - out_cell.si_config->si_window_len_slots = base_cell.sib_cfg.si_window_len_slots; - out_cell.si_config->si_sched_info.resize(base_cell.sib_cfg.si_sched_info.size()); - std::vector sibs_included; - for (unsigned i = 0; i != base_cell.sib_cfg.si_sched_info.size(); ++i) { - auto& out_si = out_cell.si_config->si_sched_info[i]; - out_si.si_period_radio_frames = base_cell.sib_cfg.si_sched_info[i].si_period_rf; - out_si.sib_mapping_info.resize(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size()); - for (unsigned j = 0; j != base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size(); ++j) { - sibs_included.push_back(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info[j]); - out_si.sib_mapping_info[j] = static_cast(sibs_included.back()); - } - } - for (const uint8_t sib_id : sibs_included) { - sib_info item; - switch (sib_id) { - case 2: { - item = create_sib2_info(); - } break; - case 19: { - if (config.ntn_cfg.has_value()) { - item = create_sib19_info(config); - } else { - report_error("SIB19 is not configured, NTN fields required\n"); - } - } break; - default: - report_error("SIB{} not supported\n", sib_id); - } - out_cell.si_config->sibs.push_back(item); - } - // Enable otherSI search space. - out_cell.dl_cfg_common.init_dl_bwp.pdcch_common.other_si_search_space_id = to_search_space_id(1); - } - - // UE timers and constants config. - out_cell.ue_timers_and_constants.t300 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t300); - out_cell.ue_timers_and_constants.t301 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t301); - out_cell.ue_timers_and_constants.t310 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t310); - out_cell.ue_timers_and_constants.n310 = base_cell.sib_cfg.ue_timers_and_constants.n310; - out_cell.ue_timers_and_constants.t311 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t311); - out_cell.ue_timers_and_constants.n311 = base_cell.sib_cfg.ue_timers_and_constants.n311; - out_cell.ue_timers_and_constants.t319 = std::chrono::milliseconds(base_cell.sib_cfg.ue_timers_and_constants.t319); - - // Carrier config. - out_cell.dl_carrier.nof_ant = base_cell.nof_antennas_dl; - out_cell.ul_carrier.nof_ant = base_cell.nof_antennas_ul; - - // UL common config. - if (base_cell.ul_common_cfg.p_max.has_value()) { - out_cell.ul_cfg_common.freq_info_ul.p_max = base_cell.ul_common_cfg.p_max.value(); - } - - // DL common config. - out_cell.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location = dc_offset_helper::pack( - base_cell.pdsch_cfg.dc_offset, out_cell.dl_cfg_common.freq_info_dl.scs_carrier_list.back().carrier_bandwidth); - - // PRACH config. - rach_config_common& rach_cfg = *out_cell.ul_cfg_common.init_ul_bwp.rach_cfg_common; - rach_cfg.rach_cfg_generic.prach_config_index = base_cell.prach_cfg.prach_config_index.value(); - rach_cfg.rach_cfg_generic.preamble_trans_max = base_cell.prach_cfg.preamble_trans_max; - rach_cfg.rach_cfg_generic.power_ramping_step_db = base_cell.prach_cfg.power_ramping_step_db; - const bool is_long_prach = - is_long_preamble(prach_configuration_get(band_helper::get_freq_range(param.band.value()), - band_helper::get_duplex_mode(param.band.value()), - rach_cfg.rach_cfg_generic.prach_config_index) - .format); - // \c is_prach_root_seq_index_l839 and msg1_scs are derived parameters, that depend on the PRACH format. They are - // originally computed in the base_cell struct, but since we overwrite the PRACH prach_config_index (which - // determines the PRACH format), we need to recompute both \c is_prach_root_seq_index_l839 and \c msg1_scs. - rach_cfg.is_prach_root_seq_index_l839 = is_long_prach; - rach_cfg.msg1_scs = is_long_prach ? subcarrier_spacing::invalid : base_cell.common_scs; - rach_cfg.prach_root_seq_index = base_cell.prach_cfg.prach_root_sequence_index; - rach_cfg.rach_cfg_generic.zero_correlation_zone_config = base_cell.prach_cfg.zero_correlation_zone; - rach_cfg.rach_cfg_generic.preamble_rx_target_pw = base_cell.prach_cfg.preamble_rx_target_pw; - // \c msg1_frequency_start for RACH is one of the parameters that can either be set manually, or need to be - // recomputed at the end of the manual configuration, as a results of other user parameters passed by the user. - bool update_msg1_frequency_start = not base_cell.prach_cfg.prach_frequency_start.has_value(); - if (not update_msg1_frequency_start) { - // Set manually. - rach_cfg.rach_cfg_generic.msg1_frequency_start = base_cell.prach_cfg.prach_frequency_start.value(); - } - rach_cfg.total_nof_ra_preambles = base_cell.prach_cfg.total_nof_ra_preambles; - rach_cfg.nof_ssb_per_ro = base_cell.prach_cfg.nof_ssb_per_ro; - rach_cfg.nof_cb_preambles_per_ssb = base_cell.prach_cfg.nof_cb_preambles_per_ssb; - - // PhysicalCellGroup Config parameters. - if (base_cell.pcg_cfg.p_nr_fr1.has_value()) { - out_cell.pcg_params.p_nr_fr1 = base_cell.pcg_cfg.p_nr_fr1.value(); - } - - // MAC Cell Group Config parameters. - out_cell.mcg_params.periodic_timer = to_periodic_bsr_timer(base_cell.mcg_cfg.bsr_cfg.periodic_bsr_timer); - out_cell.mcg_params.retx_timer = to_retx_bsr_timer(base_cell.mcg_cfg.bsr_cfg.retx_bsr_timer); - if (base_cell.mcg_cfg.bsr_cfg.lc_sr_delay_timer.has_value()) { - out_cell.mcg_params.lc_sr_delay_timer = to_lc_sr_delay_timer(base_cell.mcg_cfg.bsr_cfg.lc_sr_delay_timer.value()); - } - out_cell.mcg_params.max_tx = to_sr_max_tx(base_cell.mcg_cfg.sr_cfg.sr_trans_max); - out_cell.mcg_params.phr_prohib_timer = to_phr_prohibit_timer(base_cell.mcg_cfg.phr_cfg.phr_prohib_timer); - if (base_cell.mcg_cfg.sr_cfg.sr_prohibit_timer.has_value()) { - out_cell.mcg_params.sr_prohibit_timer.emplace( - to_sr_prohib_timer(base_cell.mcg_cfg.sr_cfg.sr_prohibit_timer.value())); - } - - // PCCH-Config. - out_cell.dl_cfg_common.init_dl_bwp.pdcch_common.paging_search_space_id = - to_search_space_id(base_cell.paging_cfg.paging_search_space_id); - out_cell.dl_cfg_common.pcch_cfg = generate_pcch_config(base_cell); - - // Parameters for PUSCH-ConfigCommon. - if (not out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.has_value()) { - out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.emplace(); - } - out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().msg3_delta_preamble = - base_cell.pusch_cfg.msg3_delta_preamble; - out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().p0_nominal_with_grant = - base_cell.pusch_cfg.p0_nominal_with_grant; - out_cell.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().msg3_delta_power = base_cell.pusch_cfg.msg3_delta_power; - - if (config.ntn_cfg.has_value()) { - out_cell.ntn_cs_koffset = config.ntn_cfg.value().cell_specific_koffset; - } - // Parameters for PUCCH-ConfigCommon. - if (not out_cell.ul_cfg_common.init_ul_bwp.pucch_cfg_common.has_value()) { - out_cell.ul_cfg_common.init_ul_bwp.pucch_cfg_common.emplace(); - } - out_cell.ul_cfg_common.init_ul_bwp.pucch_cfg_common.value().p0_nominal = base_cell.pucch_cfg.p0_nominal; - - // Common PDCCH config. - search_space_configuration& ss1_cfg = out_cell.dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces.back(); - ss1_cfg.set_non_ss0_nof_candidates(base_cell.pdcch_cfg.common.ss1_n_candidates); - - // UE-dedicated PDCCH config. - freq_resource_bitmap freq_resources(pdcch_constants::MAX_NOF_FREQ_RESOURCES); - unsigned cset1_start_crb = 0; - if (base_cell.pdcch_cfg.dedicated.coreset1_rb_start.has_value()) { - cset1_start_crb = base_cell.pdcch_cfg.dedicated.coreset1_rb_start.value(); - } - unsigned cset1_l_crb = nof_crbs - cset1_start_crb; - if (base_cell.pdcch_cfg.dedicated.coreset1_l_crb.has_value()) { - cset1_l_crb = base_cell.pdcch_cfg.dedicated.coreset1_l_crb.value(); - } - const unsigned coreset1_nof_resources = cset1_l_crb / pdcch_constants::NOF_RB_PER_FREQ_RESOURCE; - freq_resources.fill(cset1_start_crb / pdcch_constants::NOF_RB_PER_FREQ_RESOURCE, - cset1_start_crb / pdcch_constants::NOF_RB_PER_FREQ_RESOURCE + coreset1_nof_resources, - true); - - search_space_configuration& ss2_cfg = out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdcch_cfg->search_spaces[0]; - coreset_configuration& cset1_cfg = out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdcch_cfg->coresets[0]; - cset1_cfg.set_freq_domain_resources(freq_resources); - if (base_cell.pdcch_cfg.dedicated.coreset1_duration.has_value()) { - cset1_cfg.duration = base_cell.pdcch_cfg.dedicated.coreset1_duration.value(); - } else { - cset1_cfg.duration = out_cell.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->duration; - } - const std::array auto_compute_ss2_n_candidates_cfg = {0, 0, 0, 0, 0}; - if (base_cell.pdcch_cfg.dedicated.ss2_n_candidates != auto_compute_ss2_n_candidates_cfg) { - ss2_cfg.set_non_ss0_nof_candidates(base_cell.pdcch_cfg.dedicated.ss2_n_candidates); - } else if (base_cell.pdcch_cfg.dedicated.ss2_type != search_space_configuration::type_t::common) { - ss2_cfg.set_non_ss0_nof_candidates({0, - config_helpers::compute_max_nof_candidates(aggregation_level::n2, cset1_cfg), - config_helpers::compute_max_nof_candidates(aggregation_level::n4, cset1_cfg), - 0, - 0}); - } - - if (base_cell.pdcch_cfg.dedicated.ss2_type == search_space_configuration::type_t::common) { - ss2_cfg.set_non_ss0_monitored_dci_formats(search_space_configuration::common_dci_format{.f0_0_and_f1_0 = true}); - // TODO: Handle this implementation defined restriction of max. 4 PDCCH candidates in validator. - if (base_cell.pdcch_cfg.dedicated.ss2_n_candidates == auto_compute_ss2_n_candidates_cfg) { - ss2_cfg.set_non_ss0_nof_candidates( - {0, - std::min(static_cast(4U), - config_helpers::compute_max_nof_candidates(aggregation_level::n2, cset1_cfg)), - std::min(static_cast(4U), - config_helpers::compute_max_nof_candidates(aggregation_level::n4, cset1_cfg)), - 0, - 0}); - } - } else if (not base_cell.pdcch_cfg.dedicated.dci_format_0_1_and_1_1) { - search_space_configuration& ss_cfg = out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdcch_cfg->search_spaces[0]; - ss_cfg.set_non_ss0_monitored_dci_formats(search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0); - } - - // PDSCH-Config - Update PDSCH time domain resource allocations based on partial slot and/or dedicated PDCCH - // configuration. - out_cell.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list = - config_helpers::make_pdsch_time_domain_resource(param.search_space0_index, - out_cell.dl_cfg_common.init_dl_bwp.pdcch_common, - out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdcch_cfg, - band_helper::get_duplex_mode(param.band.value()) == - duplex_mode::TDD - ? out_cell.tdd_ul_dl_cfg_common.value() - : std::optional{}); - - out_cell.ue_ded_serv_cell_cfg.pdsch_serv_cell_cfg->nof_harq_proc = - (pdsch_serving_cell_config::nof_harq_proc_for_pdsch)config.cells_cfg.front().cell.pdsch_cfg.nof_harqs; - // Set DL MCS table. - out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdsch_cfg->mcs_table = base_cell.pdsch_cfg.mcs_table; - // Set DMRS additional position. - out_cell.ue_ded_serv_cell_cfg.init_dl_bwp.pdsch_cfg->pdsch_mapping_type_a_dmrs->additional_positions = - uint_to_dmrs_additional_positions(base_cell.pdsch_cfg.dmrs_add_pos); - - // Parameters for csiMeasConfig. - if (param.csi_rs_enabled) { - fill_csi_resources(out_cell.ue_ded_serv_cell_cfg, base_cell); - } - - // 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_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; - - // Parameters for PUSCH-Config. - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.emplace(); - } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.emplace(); - } - // Set DMRS additional position. - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs->additional_positions = - uint_to_dmrs_additional_positions(base_cell.pusch_cfg.dmrs_add_pos); - // Set UL MCS table. - out_cell.ue_ded_serv_cell_cfg.ul_config->init_ul_bwp.pusch_cfg->mcs_table = base_cell.pusch_cfg.mcs_table; - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.emplace(); - } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.emplace(); - } - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg->emplace(); - } - if (not variant_holds_alternative( - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value())) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.reset(); - out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg->emplace(); - } - beta_offsets& b_offsets = - variant_get(out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()); - b_offsets.beta_offset_ack_idx_1 = base_cell.pusch_cfg.b_offset_ack_idx_1; - b_offsets.beta_offset_ack_idx_2 = base_cell.pusch_cfg.b_offset_ack_idx_2; - b_offsets.beta_offset_ack_idx_3 = base_cell.pusch_cfg.b_offset_ack_idx_3; - b_offsets.beta_offset_csi_p1_idx_1 = base_cell.pusch_cfg.b_offset_csi_p1_idx_1; - b_offsets.beta_offset_csi_p1_idx_2 = base_cell.pusch_cfg.b_offset_csi_p1_idx_2; - b_offsets.beta_offset_csi_p2_idx_1 = base_cell.pusch_cfg.b_offset_csi_p2_idx_1; - b_offsets.beta_offset_csi_p2_idx_2 = base_cell.pusch_cfg.b_offset_csi_p2_idx_2; - - // Parameters for PUCCH-Config. - if (not out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.has_value()) { - out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.emplace(); - } - auto& sr_cng = out_cell.ue_ded_serv_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.value().sr_res_list; - if (sr_cng.empty()) { - sr_cng.emplace_back(scheduling_request_resource_config{}); - } - sr_cng.front().period = static_cast(get_nof_slots_per_subframe(base_cell.common_scs) * - base_cell.pucch_cfg.sr_period_msec); - - // If any dependent parameter needs to be updated, this is the place. - config_helpers::compute_nof_sr_csi_pucch_res(du_pucch_cfg, - base_cell.ul_common_cfg.max_pucchs_per_slot, - base_cell.pucch_cfg.sr_period_msec, - base_cell.csi_cfg.csi_rs_enabled - ? std::optional{base_cell.csi_cfg.csi_rs_period_msec} - : std::nullopt); - if (update_msg1_frequency_start) { - rach_cfg.rach_cfg_generic.msg1_frequency_start = config_helpers::compute_prach_frequency_start( - du_pucch_cfg, out_cell.ul_cfg_common.init_ul_bwp.generic_params.crbs.length(), is_long_prach); - } - - logger.info( - "SSB derived parameters for cell: {}, band: {}, dl_arfcn:{}, crbs: {} scs:{}, ssb_scs:{}:\n\t - SSB offset " - "pointA:{} \n\t - k_SSB:{} \n\t - SSB arfcn:{} \n\t - Coreset index:{} \n\t - Searchspace index:{}", - base_cell.pci, - *param.band, - base_cell.dl_arfcn, - nof_crbs, - to_string(base_cell.common_scs), - to_string(out_cfg.back().ssb_cfg.scs), - (*ssb_freq_loc).offset_to_point_A.to_uint(), - (*ssb_freq_loc).k_ssb.to_uint(), - (*ssb_freq_loc).ssb_arfcn, - (*ssb_freq_loc).coreset0_idx, - (*ssb_freq_loc).searchspace0_idx); - - error_type error = is_du_cell_config_valid(out_cfg.back()); - if (!error) { - report_error("Invalid configuration DU cell detected.\n> {}\n", error.error()); - } - ++cell_id; - } - - return out_cfg; -} - -static rlc_am_config generate_du_rlc_am_config(const du_high_unit_rlc_am_config& in_cfg) -{ - rlc_am_config out_rlc; - // AM Config - //< TX SN - if (!from_number(out_rlc.tx.sn_field_length, in_cfg.tx.sn_field_length)) { - report_error("Invalid RLC AM TX SN: SN={}\n", in_cfg.tx.sn_field_length); - } - out_rlc.tx.t_poll_retx = in_cfg.tx.t_poll_retx; - out_rlc.tx.max_retx_thresh = in_cfg.tx.max_retx_thresh; - out_rlc.tx.poll_pdu = in_cfg.tx.poll_pdu; - out_rlc.tx.poll_byte = in_cfg.tx.poll_byte; - out_rlc.tx.max_window = in_cfg.tx.max_window; - out_rlc.tx.queue_size = in_cfg.tx.queue_size; - //< RX SN - if (!from_number(out_rlc.rx.sn_field_length, in_cfg.rx.sn_field_length)) { - report_error("Invalid RLC AM RX SN: SN={}\n", in_cfg.rx.sn_field_length); - } - out_rlc.rx.t_reassembly = in_cfg.rx.t_reassembly; - out_rlc.rx.t_status_prohibit = in_cfg.rx.t_status_prohibit; - if (in_cfg.rx.max_sn_per_status != 0) { - out_rlc.rx.max_sn_per_status = in_cfg.rx.max_sn_per_status; - } - return out_rlc; -} - -static mac_lc_config generate_mac_lc_config(const du_high_unit_mac_lc_config& in_cfg) -{ - mac_lc_config out_mac; - - out_mac.priority = in_cfg.priority; - out_mac.lcg_id = uint_to_lcg_id(in_cfg.lc_group_id); - out_mac.pbr = to_prioritized_bit_rate(in_cfg.prioritized_bit_rate_kBps); - out_mac.bsd = to_bucket_size_duration(in_cfg.bucket_size_duration_ms); - out_mac.lc_sr_mask = false; - out_mac.lc_sr_delay_applied = false; - out_mac.sr_id = uint_to_sched_req_id(0); - return out_mac; -} - -std::map srsran::generate_du_qos_config(const du_high_unit_config& config) -{ - std::map out_cfg = {}; - if (config.qos_cfg.empty()) { - out_cfg = config_helpers::make_default_du_qos_config_list(config.warn_on_drop, config.metrics.rlc.report_period); - return out_cfg; - } - - for (const auto& qos : config.qos_cfg) { - if (out_cfg.find(qos.five_qi) != out_cfg.end()) { - report_error("Duplicate 5QI configuration: {}\n", qos.five_qi); - } - // Convert RLC config - auto& out_rlc = out_cfg[qos.five_qi].rlc; - if (!from_string(out_rlc.mode, qos.rlc.mode)) { - report_error("Invalid RLC mode: {}, mode={}\n", qos.five_qi, qos.rlc.mode); - } - - if (out_rlc.mode == rlc_mode::um_bidir) { - // UM Config - //< RX SN - if (!from_number(out_rlc.um.rx.sn_field_length, qos.rlc.um.rx.sn_field_length)) { - report_error("Invalid RLC UM RX SN: {}, SN={}\n", qos.five_qi, qos.rlc.um.rx.sn_field_length); - } - //< RX t-reassembly - out_rlc.um.rx.t_reassembly = qos.rlc.um.rx.t_reassembly; - //< TX SN - if (!from_number(out_rlc.um.tx.sn_field_length, qos.rlc.um.tx.sn_field_length)) { - report_error("Invalid RLC UM TX SN: {}, SN={}\n", qos.five_qi, qos.rlc.um.tx.sn_field_length); - } - out_rlc.um.tx.queue_size = qos.rlc.um.tx.queue_size; - } else if (out_rlc.mode == rlc_mode::am) { - // AM Config - out_rlc.am = generate_du_rlc_am_config(qos.rlc.am); - } - out_rlc.metrics_period = std::chrono::milliseconds(config.metrics.rlc.report_period); - - // Convert F1-U config - auto& out_f1u = out_cfg[qos.five_qi].f1u; - //< t-Notify - out_f1u.t_notify = qos.f1u_du.t_notify; - out_f1u.warn_on_drop = config.warn_on_drop; - - // Convert MAC config - out_cfg[qos.five_qi].mac = generate_mac_lc_config(qos.mac); - } - return out_cfg; -} - -std::map srsran::generate_du_srb_config(const du_high_unit_config& config) -{ - std::map srb_cfg; - - // SRB1 - srb_cfg.insert(std::make_pair(srb_id_t::srb1, du_srb_config{})); - if (config.srb_cfg.find(srb_id_t::srb1) != config.srb_cfg.end()) { - auto& out_rlc = srb_cfg[srb_id_t::srb1].rlc; - out_rlc.mode = rlc_mode::am; - out_rlc.am = generate_du_rlc_am_config(config.srb_cfg.at(srb_id_t::srb1).rlc); - } else { - srb_cfg.at(srb_id_t::srb1).rlc = make_default_srb_rlc_config(); - } - srb_cfg.at(srb_id_t::srb1).mac = make_default_srb_mac_lc_config(LCID_SRB1); - - // SRB2 - srb_cfg.insert(std::make_pair(srb_id_t::srb2, du_srb_config{})); - if (config.srb_cfg.find(srb_id_t::srb2) != config.srb_cfg.end()) { - auto& out_rlc = srb_cfg[srb_id_t::srb2].rlc; - out_rlc.mode = rlc_mode::am; - out_rlc.am = generate_du_rlc_am_config(config.srb_cfg.at(srb_id_t::srb2).rlc); - } else { - srb_cfg.at(srb_id_t::srb2).rlc = make_default_srb_rlc_config(); - } - srb_cfg.at(srb_id_t::srb2).mac = make_default_srb_mac_lc_config(LCID_SRB2); - - // SRB3 - srb_cfg.insert(std::make_pair(srb_id_t::srb3, du_srb_config{})); - if (config.srb_cfg.find(srb_id_t::srb3) != config.srb_cfg.end()) { - auto& out_rlc = srb_cfg[srb_id_t::srb3].rlc; - out_rlc.mode = rlc_mode::am; - out_rlc.am = generate_du_rlc_am_config(config.srb_cfg.at(srb_id_t::srb3).rlc); - } else { - srb_cfg.at(srb_id_t::srb3).rlc = make_default_srb_rlc_config(); - } - srb_cfg.at(srb_id_t::srb3).mac = make_default_srb_mac_lc_config(LCID_SRB3); - - if (config.ntn_cfg.has_value()) { - ntn_augment_rlc_parameters(config.ntn_cfg.value(), srb_cfg); - } - return srb_cfg; -} - -/// Fills the given low PHY configuration from the given gnb configuration. -static void generate_low_phy_config(lower_phy_configuration& out_cfg, - const du_high_unit_cell_config& config, - const ru_sdr_unit_config& ru_cfg, - unsigned max_processing_delay_slot) -{ - const du_high_unit_base_cell_config& cell_cfg = config.cell; - out_cfg.scs = cell_cfg.common_scs; - out_cfg.cp = cp; - out_cfg.dft_window_offset = 0.5F; - out_cfg.max_processing_delay_slots = max_processing_delay_slot; - - out_cfg.srate = sampling_rate::from_MHz(ru_cfg.srate_MHz); - - out_cfg.ta_offset = band_helper::get_ta_offset(*cell_cfg.band); - if (ru_cfg.time_alignment_calibration.has_value()) { - // Selects the user specific value. - out_cfg.time_alignment_calibration = ru_cfg.time_alignment_calibration.value(); - } else { - // Selects a default parameter that ensures a valid time alignment in the MSG1 (PRACH). - out_cfg.time_alignment_calibration = 0; - } - - // Select RX buffer size policy. - if (ru_cfg.device_driver == "zmq") { - out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::half_slot; - } else if (ru_cfg.expert_execution_cfg.threads.execution_profile == lower_phy_thread_profile::single) { - // For single executor, the same executor processes uplink and downlink. In this case, the processing is blocked - // by the signal reception. The buffers must be smaller than a slot duration considering the downlink baseband - // samples must arrive to the baseband device before the transmission time passes. - out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; - } else { - out_cfg.baseband_rx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; - } - - // Select TX buffer size policy. - if (ru_cfg.expert_cfg.dl_buffer_size_policy == "auto") { - if (ru_cfg.device_driver == "zmq") { - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::half_slot; - } else if (ru_cfg.expert_execution_cfg.threads.execution_profile == lower_phy_thread_profile::single) { - // For single executor, the same executor processes uplink and downlink. In this case, the processing is blocked - // by the signal reception. The buffers must be smaller than a slot duration considering the downlink baseband - // samples must arrive to the baseband device before the transmission time passes. - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::single_packet; - } else { - out_cfg.baseband_tx_buffer_size_policy = lower_phy_baseband_buffer_size_policy::slot; - } - } else { - // Manual selection of the TX buffer size policy. - out_cfg.baseband_tx_buffer_size_policy = to_buffer_size_policy(ru_cfg.expert_cfg.dl_buffer_size_policy); - } - - // Get lower PHY system time throttling. - out_cfg.system_time_throttling = ru_cfg.expert_cfg.lphy_dl_throttling; - - const unsigned bandwidth_sc = - NOF_SUBCARRIERS_PER_RB * - band_helper::get_n_rbs_from_bw(cell_cfg.channel_bw_mhz, cell_cfg.common_scs, frequency_range::FR1); - - // Apply gain back-off to account for the PAPR of the signal and the DFT power normalization. - out_cfg.amplitude_config.input_gain_dB = - -convert_power_to_dB(static_cast(bandwidth_sc)) - ru_cfg.amplitude_cfg.gain_backoff_dB; - - // If clipping is enabled, the amplitude controller will clip the IQ components when their amplitude comes within - // 0.1 dB of the radio full scale value. - out_cfg.amplitude_config.ceiling_dBFS = ru_cfg.amplitude_cfg.power_ceiling_dBFS; - - out_cfg.amplitude_config.enable_clipping = ru_cfg.amplitude_cfg.enable_clipping; - - // Set the full scale amplitude reference to 1. - out_cfg.amplitude_config.full_scale_lin = 1.0F; - - lower_phy_sector_description sector_config; - sector_config.bandwidth_rb = - band_helper::get_n_rbs_from_bw(cell_cfg.channel_bw_mhz, cell_cfg.common_scs, frequency_range::FR1); - sector_config.dl_freq_hz = band_helper::nr_arfcn_to_freq(cell_cfg.dl_arfcn); - sector_config.ul_freq_hz = - band_helper::nr_arfcn_to_freq(band_helper::get_ul_arfcn_from_dl_arfcn(cell_cfg.dl_arfcn, cell_cfg.band)); - sector_config.nof_rx_ports = cell_cfg.nof_antennas_ul; - sector_config.nof_tx_ports = cell_cfg.nof_antennas_dl; - out_cfg.sectors.push_back(sector_config); - - if (!is_valid_lower_phy_config(out_cfg)) { - report_error("Invalid lower PHY configuration.\n"); - } -} - -/// Slice the given string by the ',' limiter, and returns a vector with each position containing one slice of the -/// string. -static std::vector split_rf_driver_args(const std::string& driver_args) -{ - std::stringstream ss(driver_args); - std::vector result; - - while (ss.good()) { - std::string str; - getline(ss, str, ','); - if (!str.empty()) { - result.push_back(str); - } - } - - return result; -} - -/// Finds the ZMQ ports within the given driver arguments. Returns a vector that contains with the ZMQ transmission or -/// reception ports. -static std::vector extract_zmq_ports(const std::string& driver_args, const std::string& port_id) -{ - std::vector ports; - - const std::vector& splitted_args = split_rf_driver_args(driver_args); - for (const auto& arg : splitted_args) { - auto I = arg.find(port_id); - - if (I == std::string::npos) { - continue; - } - - I = arg.find("="); - ports.push_back(arg.substr(++I)); - } - - return ports; -} - -static double calibrate_center_freq_Hz(double center_freq_Hz, double freq_offset_Hz, double calibration_ppm) -{ - return (center_freq_Hz + freq_offset_Hz) * (1.0 + calibration_ppm * 1e-6); -} - -static void generate_radio_config(radio_configuration::radio& out_cfg, - const ru_sdr_unit_config& ru_cfg, - const du_high_unit_config& du_cfg) -{ - out_cfg.args = ru_cfg.device_arguments; - out_cfg.log_level = ru_cfg.loggers.radio_level; - out_cfg.sampling_rate_hz = ru_cfg.srate_MHz * 1e6; - out_cfg.otw_format = radio_configuration::to_otw_format(ru_cfg.otw_format); - out_cfg.clock.clock = radio_configuration::to_clock_source(ru_cfg.clock_source); - out_cfg.clock.sync = radio_configuration::to_clock_source(ru_cfg.synch_source); - out_cfg.tx_mode = radio_configuration::to_transmission_mode(ru_cfg.expert_cfg.transmission_mode); - out_cfg.power_ramping_us = ru_cfg.expert_cfg.power_ramping_time_us; - - const std::vector& zmq_tx_addr = extract_zmq_ports(ru_cfg.device_arguments, "tx_port"); - const std::vector& zmq_rx_addr = extract_zmq_ports(ru_cfg.device_arguments, "rx_port"); - - // For each sector... - for (unsigned sector_id = 0; sector_id != du_cfg.cells_cfg.size(); ++sector_id) { - // Select cell configuration. - const du_high_unit_base_cell_config& cell = du_cfg.cells_cfg[sector_id].cell; - - // Each cell is mapped to a different stream. - radio_configuration::stream tx_stream_config; - radio_configuration::stream rx_stream_config; - - // Deduce center frequencies. - const double cell_tx_freq_Hz = band_helper::nr_arfcn_to_freq(cell.dl_arfcn); - const double cell_rx_freq_Hz = - band_helper::nr_arfcn_to_freq(band_helper::get_ul_arfcn_from_dl_arfcn(cell.dl_arfcn, cell.band)); - - // Correct actual RF center frequencies considering offset and PPM calibration. - double center_tx_freq_cal_Hz = - calibrate_center_freq_Hz(cell_tx_freq_Hz, ru_cfg.center_freq_offset_Hz, ru_cfg.calibrate_clock_ppm); - double center_rx_freq_cal_Hz = - calibrate_center_freq_Hz(cell_rx_freq_Hz, ru_cfg.center_freq_offset_Hz, ru_cfg.calibrate_clock_ppm); - - // Calculate actual LO frequencies considering LO frequency offset and the frequency correction. - double lo_tx_freq_cal_Hz = calibrate_center_freq_Hz( - cell_tx_freq_Hz + ru_cfg.lo_offset_MHz * 1e6, ru_cfg.center_freq_offset_Hz, ru_cfg.calibrate_clock_ppm); - double lo_rx_freq_cal_Hz = calibrate_center_freq_Hz( - cell_rx_freq_Hz + ru_cfg.lo_offset_MHz * 1e6, ru_cfg.center_freq_offset_Hz, ru_cfg.calibrate_clock_ppm); - - // For each DL antenna port in the cell... - for (unsigned port_id = 0; port_id != cell.nof_antennas_dl; ++port_id) { - // Create channel configuration and append it to the previous ones. - radio_configuration::channel tx_ch_config = {}; - tx_ch_config.freq.center_frequency_hz = center_tx_freq_cal_Hz; - if (std::isnormal(ru_cfg.lo_offset_MHz)) { - tx_ch_config.freq.lo_frequency_hz = lo_tx_freq_cal_Hz; - } else { - tx_ch_config.freq.lo_frequency_hz = 0.0; - } - tx_ch_config.gain_dB = ru_cfg.tx_gain_dB; - - // Add the TX ports. - if (ru_cfg.device_driver == "zmq") { - if (sector_id * cell.nof_antennas_dl + port_id >= zmq_tx_addr.size()) { - report_error("ZMQ transmission channel arguments out of bounds\n"); - } - - tx_ch_config.args = zmq_tx_addr[sector_id * cell.nof_antennas_dl + port_id]; - } - tx_stream_config.channels.emplace_back(tx_ch_config); - } - out_cfg.tx_streams.emplace_back(tx_stream_config); - - // For each UL antenna port in the cell... - for (unsigned port_id = 0; port_id != cell.nof_antennas_ul; ++port_id) { - // Create channel configuration and append it to the previous ones. - radio_configuration::channel rx_ch_config = {}; - rx_ch_config.freq.center_frequency_hz = center_rx_freq_cal_Hz; - if (std::isnormal(ru_cfg.lo_offset_MHz)) { - rx_ch_config.freq.lo_frequency_hz = lo_rx_freq_cal_Hz; - } else { - rx_ch_config.freq.lo_frequency_hz = 0.0; - } - rx_ch_config.gain_dB = ru_cfg.rx_gain_dB; - - // Add the RX ports. - if (ru_cfg.device_driver == "zmq") { - if (sector_id * cell.nof_antennas_dl + port_id >= zmq_rx_addr.size()) { - report_error("ZMQ reception channel arguments out of bounds\n"); - } - - rx_ch_config.args = zmq_rx_addr[sector_id * cell.nof_antennas_dl + port_id]; - } - rx_stream_config.channels.emplace_back(rx_ch_config); - } - out_cfg.rx_streams.emplace_back(rx_stream_config); - } -} - -static void generate_ru_generic_config(ru_generic_configuration& out_cfg, - const ru_sdr_unit_config& ru_cfg, - const du_high_unit_config& du_cfg, - const du_low_unit_config& du_low_cfg) -{ - out_cfg.device_driver = ru_cfg.device_driver; - generate_radio_config(out_cfg.radio_cfg, ru_cfg, du_cfg); - - for (unsigned i = 0, e = du_cfg.cells_cfg.size(); i != e; ++i) { - out_cfg.lower_phy_config.emplace_back(); - generate_low_phy_config(out_cfg.lower_phy_config.back(), - du_cfg.cells_cfg[i], - ru_cfg, - du_low_cfg.expert_phy_cfg.max_processing_delay_slots); - } -} - -static bool parse_mac_address(const std::string& mac_str, span mac) -{ - std::array data = {}; - int bytes_read = std::sscanf( - mac_str.c_str(), "%02x:%02x:%02x:%02x:%02x:%02x", &data[0], &data[1], &data[2], &data[3], &data[4], &data[5]); - if (bytes_read != ether::ETH_ADDR_LEN) { - fmt::print("Invalid MAC address provided: {}\n", mac_str); - return false; - } - - std::copy(data.begin(), data.end(), mac.begin()); - - return true; -} - -/// Converts reception window timing parameters from microseconds to number of symbols given the symbol duration. -static ofh::rx_window_timing_parameters -rx_timing_window_params_us_to_symbols(std::chrono::microseconds Ta4_max, - std::chrono::microseconds Ta4_min, - std::chrono::duration symbol_duration) -{ - ofh::rx_window_timing_parameters rx_window_timing_params; - rx_window_timing_params.sym_start = std::ceil(Ta4_min / symbol_duration); - rx_window_timing_params.sym_end = std::ceil(Ta4_max / symbol_duration); - - return rx_window_timing_params; -} - -/// Converts transmission window timing parameters from microseconds to number of symbols given the symbol duration. -static ofh::tx_window_timing_parameters -tx_timing_window_params_us_to_symbols(std::chrono::microseconds T1a_max_cp_dl, - std::chrono::microseconds T1a_min_cp_dl, - std::chrono::microseconds T1a_max_cp_ul, - std::chrono::microseconds T1a_min_cp_ul, - std::chrono::microseconds T1a_max_up, - std::chrono::microseconds T1a_min_up, - std::chrono::duration symbol_duration) -{ - ofh::tx_window_timing_parameters tx_window_timing_params; - tx_window_timing_params.sym_cp_dl_start = std::floor(T1a_max_cp_dl / symbol_duration); - tx_window_timing_params.sym_cp_dl_end = std::ceil(T1a_min_cp_dl / symbol_duration); - tx_window_timing_params.sym_cp_ul_start = std::floor(T1a_max_cp_ul / symbol_duration); - tx_window_timing_params.sym_cp_ul_end = std::ceil(T1a_min_cp_ul / symbol_duration); - tx_window_timing_params.sym_up_dl_start = std::floor(T1a_max_up / symbol_duration); - tx_window_timing_params.sym_up_dl_end = std::ceil(T1a_min_up / symbol_duration); - - return tx_window_timing_params; -} - -static void generate_ru_ofh_config(ru_ofh_configuration& out_cfg, - const ru_ofh_unit_config& ru_cfg, - const du_high_unit_config& du_high, - const du_low_unit_config& du_low, - const gnb_appconfig& gnb_cfg, - span du_cells) -{ - /// Individual Open Fronthaul sector configurations. - std::vector sector_configs; - const du_high_unit_base_cell_config& cell = du_high.cells_cfg.front().cell; - - out_cfg.gps_Alpha = ru_cfg.gps_Alpha; - out_cfg.gps_Beta = ru_cfg.gps_Beta; - out_cfg.max_processing_delay_slots = du_low.expert_phy_cfg.max_processing_delay_slots; - out_cfg.dl_processing_time = std::chrono::microseconds(ru_cfg.dl_processing_time); - out_cfg.uses_dpdk = gnb_cfg.hal_config.has_value(); - - // Add one cell. - for (unsigned i = 0, e = ru_cfg.cells.size(); i != e; ++i) { - const ru_ofh_unit_cell_config& cell_cfg = ru_cfg.cells[i]; - const du_cell_config& du_cell_cfg = du_cells[i]; - out_cfg.sector_configs.emplace_back(); - ru_ofh_sector_configuration& sector_cfg = out_cfg.sector_configs.back(); - - sector_cfg.interface = cell_cfg.network_interface; - sector_cfg.is_promiscuous_mode_enabled = cell_cfg.enable_promiscuous_mode; - sector_cfg.mtu_size = cell_cfg.mtu_size; - if (!parse_mac_address(cell_cfg.du_mac_address, sector_cfg.mac_src_address)) { - srsran_terminate("Invalid Distributed Unit MAC address"); - } - - if (!parse_mac_address(cell_cfg.ru_mac_address, sector_cfg.mac_dst_address)) { - srsran_terminate("Invalid Radio Unit MAC address"); - } - - std::chrono::duration symbol_duration( - (1e6 / (get_nsymb_per_slot(cyclic_prefix::NORMAL) * get_nof_slots_per_subframe(cell.common_scs)))); - - sector_cfg.cp = cyclic_prefix::NORMAL; - sector_cfg.scs = cell.common_scs; - sector_cfg.bw = cell.channel_bw_mhz; - sector_cfg.nof_antennas_ul = cell.nof_antennas_ul; - sector_cfg.ru_operating_bw = cell_cfg.cell.ru_operating_bw; - sector_cfg.is_uplink_static_comp_hdr_enabled = cell_cfg.cell.is_uplink_static_comp_hdr_enabled; - sector_cfg.is_downlink_static_comp_hdr_enabled = cell_cfg.cell.is_downlink_static_comp_hdr_enabled; - sector_cfg.tx_window_timing_params = tx_timing_window_params_us_to_symbols(cell_cfg.cell.T1a_max_cp_dl, - cell_cfg.cell.T1a_min_cp_dl, - cell_cfg.cell.T1a_max_cp_ul, - cell_cfg.cell.T1a_min_cp_ul, - cell_cfg.cell.T1a_max_up, - cell_cfg.cell.T1a_min_up, - symbol_duration); - sector_cfg.rx_window_timing_params = - rx_timing_window_params_us_to_symbols(cell_cfg.cell.Ta4_max, cell_cfg.cell.Ta4_min, symbol_duration); - sector_cfg.is_prach_control_plane_enabled = cell_cfg.cell.is_prach_control_plane_enabled; - sector_cfg.is_downlink_broadcast_enabled = cell_cfg.cell.is_downlink_broadcast_enabled; - sector_cfg.ignore_ecpri_payload_size_field = cell_cfg.cell.ignore_ecpri_payload_size_field; - sector_cfg.ignore_ecpri_seq_id_field = cell_cfg.cell.ignore_ecpri_seq_id_field; - sector_cfg.warn_unreceived_ru_frames = cell_cfg.cell.warn_unreceived_ru_frames; - sector_cfg.ul_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_ul), - cell_cfg.cell.compression_bitwidth_ul}; - sector_cfg.dl_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_dl), - cell_cfg.cell.compression_bitwidth_dl}; - sector_cfg.prach_compression_params = {ofh::to_compression_type(cell_cfg.cell.compression_method_prach), - cell_cfg.cell.compression_bitwidth_prach}; - sector_cfg.iq_scaling = cell_cfg.cell.iq_scaling; - - sector_cfg.tci_cp = cell_cfg.vlan_tag_cp; - sector_cfg.tci_up = cell_cfg.vlan_tag_up; - sector_cfg.prach_eaxc.assign(cell_cfg.ru_prach_port_id.begin(), cell_cfg.ru_prach_port_id.end()); - sector_cfg.dl_eaxc.assign(cell_cfg.ru_dl_port_id.begin(), cell_cfg.ru_dl_port_id.end()); - sector_cfg.ul_eaxc.assign(cell_cfg.ru_ul_port_id.begin(), cell_cfg.ru_ul_port_id.end()); - - // TDD UL DL config. - sector_cfg.tdd_config = du_cell_cfg.tdd_ul_dl_cfg_common; - } - - if (!is_valid_ru_ofh_config(out_cfg)) { - report_error("Invalid Open Fronthaul Radio Unit configuration detected.\n"); - } -} - -static void generate_ru_dummy_config(ru_dummy_configuration& out_cfg, - const ru_dummy_unit_config& ru_cfg, - const gnb_appconfig& config, - const du_high_unit_config& du_high, - const du_low_unit_config& du_low, - span du_cells) -{ - // Select common cell configuration. - const du_high_unit_base_cell_config& cell = du_high.cells_cfg.front().cell; - - // Derive parameters. - unsigned channel_bw_prb = band_helper::get_n_rbs_from_bw(cell.channel_bw_mhz, cell.common_scs, frequency_range::FR1); - - // Fill configuration parameters. - out_cfg.scs = cell.common_scs; - out_cfg.nof_sectors = du_high.cells_cfg.size(); - out_cfg.rx_rg_nof_prb = channel_bw_prb; - out_cfg.rx_rg_nof_ports = cell.nof_antennas_ul; - out_cfg.rx_prach_nof_ports = cell.prach_cfg.ports.size(); - out_cfg.max_processing_delay_slots = du_low.expert_phy_cfg.max_processing_delay_slots; - out_cfg.dl_processing_delay = ru_cfg.dl_processing_delay; -} - -ru_configuration srsran::generate_ru_config(const gnb_appconfig& config, - span cells, - const dynamic_du_unit_config& unit_cfg) -{ - ru_configuration out_cfg; - - if (variant_holds_alternative(unit_cfg.ru_cfg)) { - ru_generic_configuration& cfg = out_cfg.config.emplace(); - generate_ru_generic_config( - cfg, variant_get(unit_cfg.ru_cfg), unit_cfg.du_high_cfg.config, unit_cfg.du_low_cfg); - } else if (variant_holds_alternative(unit_cfg.ru_cfg)) { - ru_ofh_configuration& cfg = out_cfg.config.emplace(); - generate_ru_ofh_config(cfg, - variant_get(unit_cfg.ru_cfg).config, - unit_cfg.du_high_cfg.config, - unit_cfg.du_low_cfg, - config, - cells); - } else { - ru_dummy_configuration& cfg = out_cfg.config.emplace(); - generate_ru_dummy_config(cfg, - variant_get(unit_cfg.ru_cfg), - config, - unit_cfg.du_high_cfg.config, - unit_cfg.du_low_cfg, - cells); - } - - return out_cfg; -} - -upper_phy_config srsran::generate_du_low_config(const du_high_unit_cell_config& config, - const du_low_unit_config& du_low, - unsigned sector_id) -{ - const du_high_unit_base_cell_config& cell = config.cell; - upper_phy_config cfg; - - // Get band, frequency range and duplex mode from the band. - nr_band band = cell.band.value(); - const frequency_range freq_range = band_helper::get_freq_range(band); - const duplex_mode duplex = band_helper::get_duplex_mode(band); - - // Get bandwidth in PRB. - const unsigned bw_rb = band_helper::get_n_rbs_from_bw(cell.channel_bw_mhz, cell.common_scs, freq_range); - // Deduce the number of slots per subframe. - const unsigned nof_slots_per_subframe = get_nof_slots_per_subframe(cell.common_scs); - // Deduce the number of slots per frame. - unsigned nof_slots_per_frame = nof_slots_per_subframe * NOF_SUBFRAMES_PER_FRAME; - // Number of slots per system frame. - unsigned nof_slots_per_system_frame = NOF_SFNS * nof_slots_per_frame; - // PUSCH HARQ process lifetime in slots. It assumes the maximum lifetime is 100ms. - unsigned expire_pusch_harq_timeout_slots = 100 * nof_slots_per_subframe; - - // Calculate the number of UL slots in a frame and in a PUSCH HARQ process lifetime. - unsigned nof_ul_slots_in_harq_lifetime = expire_pusch_harq_timeout_slots; - unsigned nof_ul_slots_per_frame = nof_slots_per_frame; - if (duplex == duplex_mode::TDD && cell.tdd_ul_dl_cfg.has_value()) { - const tdd_ul_dl_pattern_unit_config& pattern1 = cell.tdd_ul_dl_cfg->pattern1; - unsigned period_slots = pattern1.dl_ul_period_slots; - unsigned nof_ul_slots = pattern1.nof_ul_slots + ((pattern1.nof_ul_symbols != 0) ? 1 : 0); - if (cell.tdd_ul_dl_cfg->pattern2.has_value()) { - const tdd_ul_dl_pattern_unit_config& pattern2 = cell.tdd_ul_dl_cfg->pattern2.value(); - period_slots += pattern2.dl_ul_period_slots; - nof_ul_slots += pattern2.nof_ul_slots + ((pattern2.nof_ul_symbols != 0) ? 1 : 0); - } - nof_ul_slots_per_frame = divide_ceil(nof_slots_per_frame, period_slots) * nof_ul_slots; - nof_ul_slots_in_harq_lifetime = divide_ceil(expire_pusch_harq_timeout_slots, period_slots) * nof_ul_slots; - } - - // Deduce the maximum number of codeblocks that can be scheduled for PUSCH in one slot assuming: - // - The maximum number of resource elements used for data for each scheduled resource block; - // - the cell bandwidth; - // - the highest modulation order possible; and - // - the maximum coding rate. - const unsigned max_nof_pusch_cb_slot = divide_ceil( - pusch_constants::MAX_NRE_PER_RB * bw_rb * get_bits_per_symbol(modulation_scheme::QAM256), ldpc::MAX_MESSAGE_SIZE); - - // Calculate the maximum number of active PUSCH HARQ processes from: - // - the maximum number of users per slot; and - // - the number of PUSCH occasions in a HARQ process lifetime. - const unsigned nof_buffers = cell.pusch_cfg.max_puschs_per_slot * nof_ul_slots_in_harq_lifetime; - - // Calculate the maximum number of receive codeblocks. It is equal to the product of: - // - the maximum number of codeblocks that can be scheduled in one slot; and - // - the number of PUSCH occasions in a HARQ process lifetime. - const unsigned max_rx_nof_codeblocks = nof_ul_slots_in_harq_lifetime * max_nof_pusch_cb_slot; - - // Determine processing pipelines depth. Make sure the number of slots per system frame is divisible by the pipeline - // depths. - unsigned dl_pipeline_depth = 4 * du_low.expert_phy_cfg.max_processing_delay_slots; - while (nof_slots_per_system_frame % dl_pipeline_depth != 0) { - ++dl_pipeline_depth; - } - unsigned ul_pipeline_depth = std::max(dl_pipeline_depth, 8U); - - static constexpr unsigned prach_pipeline_depth = 1; - - const prach_configuration prach_cfg = - prach_configuration_get(freq_range, duplex, cell.prach_cfg.prach_config_index.value()); - srsran_assert(prach_cfg.format != prach_format_type::invalid, - "Unsupported PRACH configuration index (i.e., {}) for the given frequency range (i.e., {}) and " - "duplex mode (i.e., {}).", - cell.prach_cfg.prach_config_index.value(), - to_string(freq_range), - to_string(duplex)); - - // Maximum number of concurrent PUSCH transmissions. It is the maximum number of PUSCH transmissions that can be - // processed simultaneously. If there are no dedicated threads for PUSCH decoding, it sets the queue size to one. - // Otherwise, it is set to the maximum number of PUSCH transmissions that can be scheduled in one frame. - unsigned max_pusch_concurrency = 1; - if (du_low.expert_execution_cfg.threads.nof_pusch_decoder_threads > 0) { - max_pusch_concurrency = cell.pusch_cfg.max_puschs_per_slot * nof_ul_slots_per_frame; - } - - cfg.nof_slots_request_headroom = du_low.expert_phy_cfg.nof_slots_request_headroom; - cfg.log_level = srslog::str_to_basic_level(du_low.loggers.phy_level); - cfg.enable_logging_broadcast = du_low.loggers.broadcast_enabled; - cfg.rx_symbol_printer_filename = du_low.loggers.phy_rx_symbols_filename; - cfg.rx_symbol_printer_port = du_low.loggers.phy_rx_symbols_port; - cfg.rx_symbol_printer_prach = du_low.loggers.phy_rx_symbols_prach; - cfg.logger_max_hex_size = du_low.loggers.hex_max_size; - cfg.sector_id = sector_id; - cfg.nof_tx_ports = cell.nof_antennas_dl; - cfg.nof_rx_ports = cell.nof_antennas_ul; - cfg.ldpc_decoder_iterations = du_low.expert_phy_cfg.pusch_decoder_max_iterations; - cfg.ldpc_decoder_early_stop = du_low.expert_phy_cfg.pusch_decoder_early_stop; - cfg.nof_dl_rg = dl_pipeline_depth + 2; - cfg.dl_rg_expire_timeout_slots = dl_pipeline_depth; - cfg.nof_dl_processors = dl_pipeline_depth; - cfg.nof_ul_rg = ul_pipeline_depth; - cfg.max_ul_thread_concurrency = du_low.expert_execution_cfg.threads.nof_ul_threads + 1; - cfg.max_pusch_concurrency = max_pusch_concurrency; - cfg.nof_pusch_decoder_threads = du_low.expert_execution_cfg.threads.nof_pusch_decoder_threads + - du_low.expert_execution_cfg.threads.nof_ul_threads; - cfg.nof_prach_buffer = prach_pipeline_depth * nof_slots_per_subframe; - cfg.max_nof_td_prach_occasions = prach_cfg.nof_occasions_within_slot; - cfg.max_nof_fd_prach_occasions = 1; - cfg.is_prach_long_format = is_long_preamble(prach_cfg.format); - cfg.pusch_sinr_calc_method = - channel_state_information::sinr_type_from_string(du_low.expert_phy_cfg.pusch_sinr_calc_method); - - cfg.active_scs = {}; - cfg.active_scs[to_numerology_value(cell.common_scs)] = true; - - cfg.dl_bw_rb = bw_rb; - cfg.ul_bw_rb = bw_rb; - - cfg.rx_buffer_config.nof_buffers = nof_buffers; - cfg.rx_buffer_config.nof_codeblocks = max_rx_nof_codeblocks; - cfg.rx_buffer_config.max_codeblock_size = ldpc::MAX_CODEBLOCK_SIZE; - cfg.rx_buffer_config.expire_timeout_slots = expire_pusch_harq_timeout_slots; - cfg.rx_buffer_config.external_soft_bits = false; - - if (!is_valid_upper_phy_config(cfg)) { - report_error("Invalid upper PHY configuration.\n"); - } - - return cfg; -} - -mac_expert_config srsran::generate_mac_expert_config(const du_high_unit_config& config) -{ - mac_expert_config out_cfg = {}; - const du_high_unit_base_cell_config& cell = config.cells_cfg.front().cell; - out_cfg.configs.push_back({.max_consecutive_dl_kos = cell.pdsch_cfg.max_consecutive_kos, - .max_consecutive_ul_kos = cell.pusch_cfg.max_consecutive_kos, - .max_consecutive_csi_dtx = cell.pucch_cfg.max_consecutive_kos}); - - return out_cfg; -} - -scheduler_expert_config srsran::generate_scheduler_expert_config(const du_high_unit_config& config) -{ - scheduler_expert_config out_cfg = config_helpers::make_default_scheduler_expert_config(); - - const du_high_unit_base_cell_config& cell = config.cells_cfg.front().cell; - - // UE parameters. - const du_high_unit_pdsch_config& pdsch = cell.pdsch_cfg; - const du_high_unit_pusch_config& pusch = cell.pusch_cfg; - out_cfg.ue.dl_mcs = {pdsch.min_ue_mcs, pdsch.max_ue_mcs}; - out_cfg.ue.pdsch_rv_sequence.assign(pdsch.rv_sequence.begin(), pdsch.rv_sequence.end()); - out_cfg.ue.dl_harq_la_cqi_drop_threshold = pdsch.harq_la_cqi_drop_threshold; - out_cfg.ue.dl_harq_la_ri_drop_threshold = pdsch.harq_la_ri_drop_threshold; - out_cfg.ue.max_nof_harq_retxs = pdsch.max_nof_harq_retxs; - out_cfg.ue.max_pdschs_per_slot = pdsch.max_pdschs_per_slot; - out_cfg.ue.max_pdcch_alloc_attempts_per_slot = pdsch.max_pdcch_alloc_attempts_per_slot; - out_cfg.ue.pdsch_nof_rbs = {pdsch.min_rb_size, pdsch.max_rb_size}; - out_cfg.ue.pusch_nof_rbs = {pusch.min_rb_size, pusch.max_rb_size}; - out_cfg.ue.olla_dl_target_bler = pdsch.olla_target_bler; - out_cfg.ue.olla_cqi_inc = pdsch.olla_cqi_inc; - out_cfg.ue.olla_max_cqi_offset = pdsch.olla_max_cqi_offset; - if (config.ntn_cfg.has_value()) { - out_cfg.ue.auto_ack_harq = true; - } - out_cfg.ue.ul_mcs = {pusch.min_ue_mcs, pusch.max_ue_mcs}; - out_cfg.ue.pusch_rv_sequence.assign(pusch.rv_sequence.begin(), pusch.rv_sequence.end()); - out_cfg.ue.initial_ul_dc_offset = pusch.dc_offset; - out_cfg.ue.max_puschs_per_slot = pusch.max_puschs_per_slot; - out_cfg.ue.olla_ul_target_bler = pusch.olla_target_bler; - out_cfg.ue.olla_ul_snr_inc = pusch.olla_snr_inc; - out_cfg.ue.olla_max_ul_snr_offset = pusch.olla_max_snr_offset; - out_cfg.ue.pdsch_crb_limits = {pdsch.start_rb, pdsch.end_rb}; - out_cfg.ue.pusch_crb_limits = {pusch.start_rb, pusch.end_rb}; - - // PUCCH and scheduler expert parameters. - out_cfg.ue.max_ul_grants_per_slot = cell.ul_common_cfg.max_ul_grants_per_slot; - out_cfg.ue.max_pucchs_per_slot = cell.ul_common_cfg.max_pucchs_per_slot; - out_cfg.ue.min_k1 = cell.pucch_cfg.min_k1; - - // RA parameters. - const du_high_unit_prach_config& prach = cell.prach_cfg; - - out_cfg.ra.rar_mcs_index = pdsch.fixed_rar_mcs; - out_cfg.ra.max_nof_msg3_harq_retxs = prach.max_msg3_harq_retx; - out_cfg.ra.msg3_mcs_index = prach.fixed_msg3_mcs; - - // SI parameters. - out_cfg.si.sib1_mcs_index = pdsch.fixed_sib1_mcs; - out_cfg.si.sib1_dci_aggr_lev = aggregation_level::n4; - - // Logging and tracing. - out_cfg.log_broadcast_messages = config.loggers.broadcast_enabled; - out_cfg.metrics_report_period = std::chrono::milliseconds{config.metrics.stdout_metrics_period}; - - const error_type error = is_scheduler_expert_config_valid(out_cfg); - if (!error) { - report_error("Invalid scheduler expert configuration detected.\n"); - } - - return out_cfg; -} - srsran::sctp_network_connector_config srsran::generate_e2ap_nw_config(const gnb_appconfig& config, int ppid) { srsran::sctp_network_connector_config out_cfg; @@ -1804,41 +89,3 @@ srsran::sctp_network_connector_config srsran::generate_e2ap_nw_config(const gnb_ return out_cfg; } - -e2ap_configuration srsran::generate_e2_config(const du_high_unit_config& du_high) -{ - e2ap_configuration out_cfg = srsran::config_helpers::make_default_e2ap_config(); - out_cfg.gnb_id = du_high.gnb_id; - out_cfg.ran_node_name = du_high.ran_node_name; - out_cfg.plmn = du_high.cells_cfg.front().cell.plmn; - out_cfg.e2sm_kpm_enabled = du_high.e2_cfg.e2sm_kpm_enabled; - out_cfg.e2sm_rc_enabled = du_high.e2_cfg.e2sm_rc_enabled; - - return out_cfg; -} - -void srsran::ntn_augment_rlc_parameters(const ntn_config& ntn_cfg, std::map& srb_cfgs) -{ - // NTN is enabled, so we need to augment the RLC parameters for the NTN cell. - for (auto& srb : srb_cfgs) { - if (ntn_cfg.cell_specific_koffset > 1000) { - srb.second.rlc.am.tx.t_poll_retx = 4000; - } else if (ntn_cfg.cell_specific_koffset > 800) { - srb.second.rlc.am.tx.t_poll_retx = 2000; - } else if (ntn_cfg.cell_specific_koffset > 500) { - srb.second.rlc.am.tx.t_poll_retx = 2000; - } else if (ntn_cfg.cell_specific_koffset > 300) { - srb.second.rlc.am.tx.t_poll_retx = 1000; - } else if (ntn_cfg.cell_specific_koffset > 200) { - srb.second.rlc.am.tx.t_poll_retx = 800; - } else if (ntn_cfg.cell_specific_koffset > 100) { - srb.second.rlc.am.tx.t_poll_retx = 400; - } else if (ntn_cfg.cell_specific_koffset > 50) { - srb.second.rlc.am.tx.t_poll_retx = 200; - } else if (ntn_cfg.cell_specific_koffset > 10) { - srb.second.rlc.am.tx.t_poll_retx = 100; - } else { - srb.second.rlc.am.tx.t_poll_retx = 50; - } - } -} diff --git a/apps/gnb/gnb_appconfig_translators.h b/apps/gnb/gnb_appconfig_translators.h index 9d4a294477..ef562fc4fd 100644 --- a/apps/gnb/gnb_appconfig_translators.h +++ b/apps/gnb/gnb_appconfig_translators.h @@ -48,50 +48,13 @@ struct gnb_appconfig; struct rlc_am_appconfig; struct mac_lc_appconfig; -/// Converts and returns SSB periodicity, offset and duration into a valid SSB measurement and timing configuration. -srs_cu_cp::rrc_ssb_mtc generate_rrc_ssb_mtc(unsigned period, unsigned offset, unsigned duration); - /// Converts and returns the subcarrier spacing. subcarrier_spacing generate_subcarrier_spacing(unsigned sc_spacing); /// Converts and returns the given gnb application configuration to a NGAP Network Gateway configuration. srsran::sctp_network_connector_config generate_ngap_nw_config(const cu_cp_unit_amf_config& config); -/// Converts and returns the given gnb application configuration to a CU-CP configuration. -srs_cu_cp::cu_cp_configuration generate_cu_cp_config(const du_high_unit_config& config, - const cu_cp_unit_config& cu_cfg); - -/// Converts and returns the given gnb application configuration to a DU cell configuration. -std::vector generate_du_cell_config(const du_high_unit_config& config); - -/// Converts and returns the given gnb application QoS configuration to a DU QoS list configuration. -std::map generate_du_qos_config(const du_high_unit_config& config); - -/// Converts and returns the given gnb application QoS configuration to a DU SRB list configuration. -std::map generate_du_srb_config(const du_high_unit_config& config); - -/// Converts and returns the given gnb application configuration to a mac expert configuration. -mac_expert_config generate_mac_expert_config(const du_high_unit_config& config); - -/// Converts and returns the given gnb application configuration to a scheduler expert configuration. -scheduler_expert_config generate_scheduler_expert_config(const du_high_unit_config& config); - -/// Converts and returns the given gnb application configuration to an upper PHY configuration. -upper_phy_config -generate_du_low_config(const du_high_unit_cell_config& config, const du_low_unit_config& du_low, unsigned sector_id); - -/// Converts and returns the given gnb application configuration to a Radio Unit configuration. -ru_configuration generate_ru_config(const gnb_appconfig& config, - span cells, - const dynamic_du_unit_config& unit_cfg); - /// Converts and returns the given gnb application configuration to a E2AP Network Gateway configuration. srsran::sctp_network_connector_config generate_e2ap_nw_config(const gnb_appconfig& config, int ppid); -/// Converts and returns the given gnb application configuration to a E2 configuration. -e2ap_configuration generate_e2_config(const du_high_unit_config& du_high); - -/// Augments RLC parameters based on NTN configuration. -void ntn_augment_rlc_parameters(const ntn_config& ntn_cfg, std::map& srb_cfgs); - } // namespace srsran diff --git a/apps/gnb/gnb_appconfig_validators.cpp b/apps/gnb/gnb_appconfig_validators.cpp index 5b07fb68dd..638b9c1989 100644 --- a/apps/gnb/gnb_appconfig_validators.cpp +++ b/apps/gnb/gnb_appconfig_validators.cpp @@ -21,11 +21,9 @@ */ #include "gnb_appconfig_validators.h" -#include "srsran/adt/span.h" -#include "srsran/pdcp/pdcp_config.h" +#include "apps/units/cu_cp/cu_cp_unit_config.h" +#include "apps/units/flexible_du/du_high/du_high_config.h" #include "srsran/ran/nr_cgi_helpers.h" -#include "srsran/ran/prach/prach_helper.h" -#include "srsran/rlc/rlc_config.h" using namespace srsran; @@ -67,3 +65,21 @@ bool srsran::validate_appconfig(const gnb_appconfig& config) return true; } + +bool srsran::validate_plmn_and_tacs(const du_high_unit_config& du_hi_cfg, const cu_cp_unit_config& cu_cp_cfg) +{ + for (const auto& cell : du_hi_cfg.cells_cfg) { + if (std::find(cu_cp_cfg.plmns.cbegin(), cu_cp_cfg.plmns.cend(), cell.cell.plmn) == cu_cp_cfg.plmns.cend()) { + fmt::print("Could not find cell PLMN '{}' in the CU-CP PLMN list", cell.cell.plmn); + + return false; + } + + if (std::find(cu_cp_cfg.tacs.cbegin(), cu_cp_cfg.tacs.cend(), cell.cell.tac) == cu_cp_cfg.tacs.cend()) { + fmt::print("Could not find cell TAC '{}' in the CU-CP TAC list", cell.cell.tac); + return false; + } + } + + return true; +} diff --git a/apps/gnb/gnb_appconfig_validators.h b/apps/gnb/gnb_appconfig_validators.h index d27da47254..8d6f2229e7 100644 --- a/apps/gnb/gnb_appconfig_validators.h +++ b/apps/gnb/gnb_appconfig_validators.h @@ -26,7 +26,13 @@ namespace srsran { +struct cu_cp_unit_config; +struct du_high_unit_config; + /// Validates the given GNB application configuration. Returns true on success, false otherwise. bool validate_appconfig(const gnb_appconfig& config); +/// Validates that the DU PLMNs and TACs are present in the CU-CP config. +bool validate_plmn_and_tacs(const du_high_unit_config& du_hi_cfg, const cu_cp_unit_config& cu_cp_cfg); + } // namespace srsran diff --git a/apps/services/metrics_log_helper.cpp b/apps/services/metrics_log_helper.cpp index 31f56176f5..4cecf9e3d8 100644 --- a/apps/services/metrics_log_helper.cpp +++ b/apps/services/metrics_log_helper.cpp @@ -52,14 +52,6 @@ static std::string scaled_fmt_integer(uint64_t num) return "Invalid number"; } -static std::string scaled_time(std::chrono::microseconds t) -{ - if (t.count() < 1000) { - return fmt::format("{}us", t.count()); - } - return fmt::format("{}ms", std::chrono::duration_cast(t).count()); -} - static std::string float_to_string(float f, int digits, int field_width) { std::ostringstream os; @@ -186,7 +178,7 @@ void metrics_log_helper::report_metrics(span ue_metr } fmt::format_to(buffer, " bsr={}", scaled_fmt_integer(ue.bsr)); if (ue.last_ta.has_value()) { - fmt::format_to(buffer, " last_ta={}", scaled_time(ue.last_ta.value())); + fmt::format_to(buffer, " last_ta={}s", float_to_eng_string(ue.last_ta->to_seconds(), 0)); } else { fmt::format_to(buffer, " last_ta=n/a"); } diff --git a/apps/services/metrics_plotter_stdout.cpp b/apps/services/metrics_plotter_stdout.cpp index 6432d868f1..5b326a3106 100644 --- a/apps/services/metrics_plotter_stdout.cpp +++ b/apps/services/metrics_plotter_stdout.cpp @@ -52,14 +52,6 @@ static std::string scaled_fmt_integer(uint64_t num) return "Invalid number"; } -static std::string scaled_time(std::chrono::microseconds t) -{ - if (t.count() < 1000) { - return fmt::format("{:>4}us", t.count()); - } - return fmt::format("{:>4}ms", std::chrono::duration_cast(t).count()); -} - static void print_header() { fmt::print("\n"); @@ -67,7 +59,7 @@ static void print_header() " " "|--------------------DL---------------------|-------------------------UL------------------------------\n"); fmt::print(" pci rnti | cqi ri mcs brate ok nok (%) dl_bs | pusch rsrp mcs brate ok nok (%) bsr " - "ta phr\n"); + " ta phr\n"); } static std::string float_to_string(float f, int digits, int field_width) @@ -207,7 +199,7 @@ void metrics_plotter_stdout::report_metrics(span ue_ } fmt::print(" {}", scaled_fmt_integer(ue.bsr)); if (ue.last_ta.has_value()) { - fmt::print("{}", scaled_time(ue.last_ta.value())); + fmt::print(" {}", float_to_eng_string(ue.last_ta->to_seconds(), 0)); } else { fmt::print(" n/a"); } diff --git a/apps/services/worker_manager.cpp b/apps/services/worker_manager.cpp index 1015a37ab7..03cf99bb58 100644 --- a/apps/services/worker_manager.cpp +++ b/apps/services/worker_manager.cpp @@ -30,47 +30,48 @@ using namespace srsran; static const uint32_t task_worker_queue_size = 2048; static std::vector -build_affinity_manager_dependencies(const du_high_unit_cpu_affinities_cell_config& du_high_affinities, - const du_low_unit_cpu_affinities_cell_config& du_low_affinities, - const variant& ru_affinities) +build_affinity_manager_dependencies(const du_high_unit_cpu_affinities_cell_config& du_high_affinities, + const du_low_unit_cpu_affinities_cell_config& du_low_affinities, + const std::variant& ru_affinities) { std::vector out; out.push_back(du_low_affinities.l1_ul_cpu_cfg); out.push_back(du_low_affinities.l1_dl_cpu_cfg); out.push_back(du_high_affinities.l2_cell_cpu_cfg); - if (variant_holds_alternative(ru_affinities)) { - out.push_back(variant_get(ru_affinities).ru_cpu_cfg); + if (std::holds_alternative(ru_affinities)) { + out.push_back(std::get(ru_affinities).ru_cpu_cfg); } - else if (variant_holds_alternative(ru_affinities)) { - out.push_back(variant_get(ru_affinities).ru_cpu_cfg); + else if (std::holds_alternative(ru_affinities)) { + out.push_back(std::get(ru_affinities).ru_cpu_cfg); } else { - out.push_back(variant_get(ru_affinities).ru_cpu_cfg); + out.push_back(std::get(ru_affinities).ru_cpu_cfg); } return out; } -worker_manager::worker_manager(const gnb_appconfig& appcfg, - const dynamic_du_unit_config& du_cfg, - unsigned gtpu_queue_size) : - low_prio_affinity_mng({appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg}) +worker_manager::worker_manager(const dynamic_du_unit_config& du_cfg, + const expert_execution_appconfig& expert_appcfg, + pcap_appconfig& pcap_cfg, + unsigned gtpu_queue_size) : + low_prio_affinity_mng({expert_appcfg.affinities.low_priority_cpu_cfg}) { const unsigned nof_cells = du_cfg.du_high_cfg.config.expert_execution_cfg.cell_affinities.size(); for (unsigned i = 0, e = nof_cells; i != e; ++i) { - variant + std::variant ru; - if (variant_holds_alternative(du_cfg.ru_cfg)) { - ru = variant_get(du_cfg.ru_cfg).expert_execution_cfg.cell_affinities[i]; - } else if (variant_holds_alternative(du_cfg.ru_cfg)) { - ru = variant_get(du_cfg.ru_cfg).config.expert_execution_cfg.cell_affinities[i]; + if (std::holds_alternative(du_cfg.ru_cfg)) { + ru = std::get(du_cfg.ru_cfg).expert_execution_cfg.cell_affinities[i]; + } else if (std::holds_alternative(du_cfg.ru_cfg)) { + ru = std::get(du_cfg.ru_cfg).config.expert_execution_cfg.cell_affinities[i]; } else { - ru = variant_get(du_cfg.ru_cfg).cell_affinities[i]; + ru = std::get(du_cfg.ru_cfg).cell_affinities[i]; } affinity_mng.emplace_back( @@ -79,14 +80,17 @@ worker_manager::worker_manager(const gnb_appconfig& appcfg, ru)); } - create_low_prio_executors( - appcfg, du_cfg.du_high_cfg.config.pcaps, du_cfg.du_high_cfg.config.cells_cfg.size(), gtpu_queue_size); + create_low_prio_executors(expert_appcfg, + 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 (variant_holds_alternative(du_cfg.ru_cfg)) { - const auto& sdr_cfg = variant_get(du_cfg.ru_cfg); + if (std::holds_alternative(du_cfg.ru_cfg)) { + const auto& sdr_cfg = std::get(du_cfg.ru_cfg); is_blocking_mode_active = sdr_cfg.device_driver == "zmq"; } @@ -285,33 +289,35 @@ void worker_manager::create_du_executors(bool is_blocking_m nof_cells); } -execution_config_helper::worker_pool worker_manager::create_low_prio_workers(const gnb_appconfig& appcfg) +execution_config_helper::worker_pool +worker_manager::create_low_prio_workers(const expert_execution_appconfig& expert_appcfg) { using namespace execution_config_helper; // Configure non-RT worker pool. worker_pool non_rt_pool{ "non_rt_pool", - appcfg.expert_execution_cfg.threads.non_rt_threads.nof_non_rt_threads, + expert_appcfg.threads.non_rt_threads.nof_non_rt_threads, {{concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, // two task priority levels. {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}}, // Left empty, is filled later. {}, std::chrono::microseconds{100}, os_thread_realtime_priority::no_realtime(), - std::vector{appcfg.expert_execution_cfg.affinities.low_priority_cpu_cfg.mask}}; + std::vector{expert_appcfg.affinities.low_priority_cpu_cfg.mask}}; return non_rt_pool; } -void worker_manager::create_low_prio_executors(const gnb_appconfig& appcfg, - const du_high_unit_pcap_config& du_pcaps, - unsigned nof_cells, - unsigned gtpu_queue_size) +void worker_manager::create_low_prio_executors(const expert_execution_appconfig& expert_appcfg, + const pcap_appconfig& pcap_cfg, + const du_high_unit_pcap_config& du_pcaps, + unsigned nof_cells, + unsigned gtpu_queue_size) { using namespace execution_config_helper; // TODO: split executor creation and association to workers - worker_pool non_rt_pool = create_low_prio_workers(appcfg); + worker_pool non_rt_pool = create_low_prio_workers(expert_appcfg); // Associate executors to the worker pool. // Used for PCAP writing. @@ -329,7 +335,7 @@ void worker_manager::create_low_prio_executors(const gnb_appconfig& a std::vector& cu_up_strands = non_rt_pool.executors[2].strands; // Configuration of strands for PCAP writing. These strands will use the low priority executor. - append_pcap_strands(low_prio_strands, appcfg.pcap_cfg, du_pcaps); + append_pcap_strands(low_prio_strands, pcap_cfg, 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. @@ -556,39 +562,43 @@ void worker_manager::create_ofh_executors(const ru_ofh_unit_expert_execution_con create_worker_pool(name, nof_ofh_dl_workers, task_worker_queue_size, {{exec_name}}, prio, cpu_masks); ru_dl_exec[i] = exec_mng.executors().at(exec_name); } - - // Executor for Open Fronthaul messages transmission. + // Executor for Open Fronthaul messages decoding. { - const std::string name = "ru_tx_" + std::to_string(i); - const std::string exec_name = "ru_tx_exec_" + std::to_string(i); + const std::string name = "ru_rx_" + std::to_string(i); + const std::string exec_name = "ru_rx_exec_" + std::to_string(i); + // The generic locking queue type is used here to avoid polling for new tasks and thus for saving CPU resources + // with a price of higher latency (it is acceptable for UL tasks that have lower priority compared to DL). const single_worker ru_worker{name, - {concurrent_queue_policy::lockfree_spsc, task_worker_queue_size}, + {concurrent_queue_policy::locking_mpmc, task_worker_queue_size}, {{exec_name}}, - std::chrono::microseconds{5}, - os_thread_realtime_priority::max() - 1, + std::nullopt, + os_thread_realtime_priority::max() - 6, affinity_mng[i].calcute_affinity_mask(sched_affinity_mask_types::ru)}; if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) { report_fatal_error("Failed to instantiate {} execution context", ru_worker.name); } - ru_tx_exec.push_back(exec_mng.executors().at(exec_name)); + ru_rx_exec.push_back(exec_mng.executors().at(exec_name)); } + } + // Executor for Open Fronthaul messages transmission and reception. + { + unsigned nof_txrx_workers = std::max(static_cast(std::ceil(nof_cells / 2.0f)), 1U); - // Executor for Open Fronthaul messages reception. - { - const std::string name = "ru_rx_" + std::to_string(i); - const std::string exec_name = "ru_rx_exec_" + std::to_string(i); + for (unsigned i = 0; i != nof_txrx_workers; ++i) { + const std::string name = "ru_txrx_#" + std::to_string(i); + const std::string exec_name = "ru_txrx_exec_#" + std::to_string(i); const single_worker ru_worker{name, - {concurrent_queue_policy::lockfree_spsc, 2}, + {concurrent_queue_policy::lockfree_mpmc, task_worker_queue_size}, {{exec_name}}, std::chrono::microseconds{1}, os_thread_realtime_priority::max() - 1, - affinity_mng[i].calcute_affinity_mask(sched_affinity_mask_types::ru)}; + affinity_mng.front().calcute_affinity_mask(sched_affinity_mask_types::ru)}; if (not exec_mng.add_execution_context(create_execution_context(ru_worker))) { report_fatal_error("Failed to instantiate {} execution context", ru_worker.name); } - ru_rx_exec.push_back(exec_mng.executors().at(exec_name)); + ru_txrx_exec.push_back(exec_mng.executors().at(exec_name)); } } } @@ -717,22 +727,22 @@ void worker_manager::create_lower_phy_executors(lower_phy_thread_profile lower_p } void worker_manager::create_ru_executors( - const variant& ru_cfg, - const du_high_unit_config& du_high) + const std::variant& ru_cfg, + const du_high_unit_config& du_high) { - if (variant_holds_alternative(ru_cfg)) { + if (std::holds_alternative(ru_cfg)) { std::vector cell_antennas_dl; for (const auto& cell : du_high.cells_cfg) { cell_antennas_dl.push_back(cell.cell.nof_antennas_dl); } - create_ofh_executors(variant_get(ru_cfg).config.expert_execution_cfg, cell_antennas_dl); + create_ofh_executors(std::get(ru_cfg).config.expert_execution_cfg, cell_antennas_dl); return; } - if (variant_holds_alternative(ru_cfg)) { - const ru_sdr_unit_config& sdr_cfg = variant_get(ru_cfg); + if (std::holds_alternative(ru_cfg)) { + const ru_sdr_unit_config& sdr_cfg = std::get(ru_cfg); std::string driver = sdr_cfg.device_driver; create_lower_phy_executors((driver != "zmq") ? sdr_cfg.expert_execution_cfg.threads.execution_profile diff --git a/apps/services/worker_manager.h b/apps/services/worker_manager.h index 4274718753..7ba452ce29 100644 --- a/apps/services/worker_manager.h +++ b/apps/services/worker_manager.h @@ -22,6 +22,7 @@ #pragma once +#include "../du/du_appconfig.h" #include "../gnb/gnb_appconfig.h" #include "../units/flexible_du/split_dynamic/dynamic_du_unit_config.h" #include "os_sched_affinity_manager.h" @@ -36,7 +37,10 @@ namespace srsran { /// Manages the workers of the app. struct worker_manager { - worker_manager(const gnb_appconfig& appcfg, const dynamic_du_unit_config& du_cfg, unsigned gtpu_queue_size); + worker_manager(const dynamic_du_unit_config& du_cfg, + const expert_execution_appconfig& expert_appcfg, + pcap_appconfig& pcap_cfg, + unsigned gtpu_queue_size); void stop(); @@ -68,9 +72,9 @@ struct worker_manager { task_executor* radio_exec = nullptr; task_executor* ru_printer_exec = nullptr; task_executor* ru_timing_exec = nullptr; + std::vector ru_txrx_exec; std::vector fapi_exec; std::vector ru_dl_exec; - std::vector ru_tx_exec; std::vector ru_rx_exec; task_executor* cu_cp_e2_exec = nullptr; task_executor* cu_up_e2_exec = nullptr; @@ -123,11 +127,12 @@ struct worker_manager { os_thread_realtime_priority prio = os_thread_realtime_priority::no_realtime(), span cpu_masks = {}); - execution_config_helper::worker_pool create_low_prio_workers(const gnb_appconfig& appcfg); - void create_low_prio_executors(const gnb_appconfig& appcfg, - const du_high_unit_pcap_config& du_pcaps, - unsigned nof_cells, - unsigned gtpu_queue_size); + execution_config_helper::worker_pool create_low_prio_workers(const expert_execution_appconfig& expert_appcfg); + void create_low_prio_executors(const expert_execution_appconfig& expert_appcfg, + const pcap_appconfig& pcap_cfg, + const du_high_unit_pcap_config& du_pcaps, + unsigned nof_cells, + unsigned gtpu_queue_size); void associate_low_prio_executors(); std::vector create_fapi_workers(unsigned nof_cells); @@ -148,8 +153,9 @@ struct worker_manager { unsigned nof_cells); /// Helper method that creates the Radio Unit executors. - void create_ru_executors(const variant& ru_cfg, - const du_high_unit_config& du_high); + void + create_ru_executors(const std::variant& ru_cfg, + const du_high_unit_config& du_high); /// Helper method that creates the lower PHY executors. void create_lower_phy_executors(lower_phy_thread_profile lower_phy_profile, unsigned nof_cells); diff --git a/apps/units/cu_cp/CMakeLists.txt b/apps/units/cu_cp/CMakeLists.txt index 204871c4bc..d3fd9ac9fd 100644 --- a/apps/units/cu_cp/CMakeLists.txt +++ b/apps/units/cu_cp/CMakeLists.txt @@ -20,6 +20,8 @@ set(SOURCES cu_cp_application_unit_impl.cpp + cu_cp_builder.cpp + cu_cp_config_translators.cpp cu_cp_unit_config_validator.cpp cu_cp_unit_config_cli11_schema.cpp) diff --git a/apps/units/cu_cp/cu_cp_builder.cpp b/apps/units/cu_cp/cu_cp_builder.cpp new file mode 100644 index 0000000000..881810c940 --- /dev/null +++ b/apps/units/cu_cp/cu_cp_builder.cpp @@ -0,0 +1,44 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "cu_cp_builder.h" +#include "apps/services/worker_manager.h" +#include "cu_cp_config_translators.h" +#include "srsran/cu_cp/cu_cp_factory.h" + +using namespace srsran; + +std::unique_ptr srsran::build_cu_cp(const cu_cp_unit_config& cu_cp_unit_cfg, + cu_cp_build_dependencies& dependencies) +{ + srsran_assert(dependencies.cu_cp_executor, "Invalid CU-CP executor"); + srsran_assert(dependencies.cu_cp_e2_exec, "Invalid E2 executor"); + srsran_assert(dependencies.cu_cp_e2_exec, "Invalid E2 executor"); + + srs_cu_cp::cu_cp_configuration cu_cp_cfg = generate_cu_cp_config(cu_cp_unit_cfg); + cu_cp_cfg.cu_cp_executor = dependencies.cu_cp_executor; + cu_cp_cfg.cu_cp_e2_exec = dependencies.cu_cp_e2_exec; + cu_cp_cfg.ngap_notifier = dependencies.ngap_notifier; + cu_cp_cfg.timers = dependencies.timers; + + return create_cu_cp(cu_cp_cfg); +} diff --git a/apps/units/cu_cp/cu_cp_builder.h b/apps/units/cu_cp/cu_cp_builder.h new file mode 100644 index 0000000000..7be430e105 --- /dev/null +++ b/apps/units/cu_cp/cu_cp_builder.h @@ -0,0 +1,43 @@ +/* + * + * 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.h" + +namespace srsran { + +struct cu_cp_unit_config; +struct worker_manager; + +/// CU-CP build dependencies. +struct cu_cp_build_dependencies { + task_executor* cu_cp_executor = nullptr; + task_executor* cu_cp_e2_exec = nullptr; + srs_cu_cp::ngap_message_notifier* ngap_notifier = nullptr; + timer_manager* timers = nullptr; +}; + +/// Builds a CU-CP object with the given configuration. +std::unique_ptr build_cu_cp(const cu_cp_unit_config& cu_cp_unit_cfg, + cu_cp_build_dependencies& dependencies); + +} // namespace srsran diff --git a/apps/units/cu_cp/cu_cp_config_translators.cpp b/apps/units/cu_cp/cu_cp_config_translators.cpp new file mode 100644 index 0000000000..3fb9da99f7 --- /dev/null +++ b/apps/units/cu_cp/cu_cp_config_translators.cpp @@ -0,0 +1,362 @@ +/* + * + * 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_config_translators.h" +#include "cu_cp_unit_config.h" +#include "srsran/cu_cp/cu_cp_configuration_helpers.h" +#include "srsran/ran/nr_cgi_helpers.h" + +using namespace srsran; + +static std::map generate_cu_cp_qos_config(const cu_cp_unit_config& config) +{ + std::map out_cfg = {}; + if (config.qos_cfg.empty()) { + out_cfg = config_helpers::make_default_cu_cp_qos_config_list(); + return out_cfg; + } + + for (const auto& qos : config.qos_cfg) { + if (out_cfg.find(qos.five_qi) != out_cfg.end()) { + report_error("Duplicate 5QI configuration: {}\n", qos.five_qi); + } + // Convert PDCP config + pdcp_config& out_pdcp = out_cfg[qos.five_qi].pdcp; + + // RB type + out_pdcp.rb_type = pdcp_rb_type::drb; + + // RLC mode + rlc_mode mode = {}; + if (!from_string(mode, qos.rlc.mode)) { + report_error("Invalid RLC mode: {}, mode={}\n", qos.five_qi, qos.rlc.mode); + } + if (mode == rlc_mode::um_bidir || mode == rlc_mode::um_unidir_ul || mode == rlc_mode::um_unidir_dl) { + out_pdcp.rlc_mode = pdcp_rlc_mode::um; + } else if (mode == rlc_mode::am) { + out_pdcp.rlc_mode = pdcp_rlc_mode::am; + } else { + report_error("Invalid RLC mode: {}, mode={}\n", qos.five_qi, qos.rlc.mode); + } + + // Integrity Protection required + out_pdcp.integrity_protection_required = qos.pdcp.integrity_protection_required; + + // Ciphering required + out_pdcp.ciphering_required = true; + + // > Tx + // >> SN size + if (!pdcp_sn_size_from_uint(out_pdcp.tx.sn_size, qos.pdcp.tx.sn_field_length)) { + report_error("Invalid PDCP TX SN: {}, SN={}\n", qos.five_qi, qos.pdcp.tx.sn_field_length); + } + + // >> discard timer + out_pdcp.tx.discard_timer = pdcp_discard_timer{}; + if (!pdcp_discard_timer_from_int(out_pdcp.tx.discard_timer.value(), qos.pdcp.tx.discard_timer)) { + report_error("Invalid PDCP discard timer. 5QI {} discard_timer {}\n", qos.five_qi, qos.pdcp.tx.discard_timer); + } + + // >> status report required + out_pdcp.tx.status_report_required = qos.pdcp.tx.status_report_required; + + // > Rx + // >> SN size + if (!pdcp_sn_size_from_uint(out_pdcp.rx.sn_size, qos.pdcp.rx.sn_field_length)) { + report_error("Invalid PDCP RX SN: {}, SN={}\n", qos.five_qi, qos.pdcp.rx.sn_field_length); + } + + // >> out of order delivery + out_pdcp.rx.out_of_order_delivery = qos.pdcp.rx.out_of_order_delivery; + + // >> t-Reordering + if (!pdcp_t_reordering_from_int(out_pdcp.rx.t_reordering, qos.pdcp.rx.t_reordering)) { + report_error("Invalid PDCP t-Reordering. {} t-Reordering {}\n", qos.five_qi, qos.pdcp.rx.t_reordering); + } + } + return out_cfg; +} + +static security::preferred_integrity_algorithms +generate_preferred_integrity_algorithms_list(const cu_cp_unit_config& config) +{ + // String splitter helper + auto split = [](const std::string& s, char delim) -> std::vector { + std::vector result; + std::stringstream ss(s); + for (std::string item; getline(ss, item, delim);) { + result.push_back(item); + } + return result; + }; + + // > Remove spaces, convert to lower case and split on comma + std::string nia_preference_list = config.security_config.nia_preference_list; + nia_preference_list.erase(std::remove_if(nia_preference_list.begin(), nia_preference_list.end(), ::isspace), + nia_preference_list.end()); + std::transform(nia_preference_list.begin(), + nia_preference_list.end(), + nia_preference_list.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::vector nea_v = split(nia_preference_list, ','); + + security::preferred_integrity_algorithms algo_list = {}; + int idx = 0; + for (const std::string& nea : nea_v) { + if (nea == "nia0") { + algo_list[idx] = security::integrity_algorithm::nia0; + } else if (nea == "nia1") { + algo_list[idx] = security::integrity_algorithm::nia1; + } else if (nea == "nia2") { + algo_list[idx] = security::integrity_algorithm::nia2; + } else if (nea == "nia3") { + algo_list[idx] = security::integrity_algorithm::nia3; + } + idx++; + } + return algo_list; +} + +static security::preferred_ciphering_algorithms +generate_preferred_ciphering_algorithms_list(const cu_cp_unit_config& config) +{ + // String splitter helper + auto split = [](const std::string& s, char delim) -> std::vector { + std::vector result; + std::stringstream ss(s); + for (std::string item; getline(ss, item, delim);) { + result.push_back(item); + } + return result; + }; + + // > Remove spaces, convert to lower case and split on comma + std::string nea_preference_list = config.security_config.nea_preference_list; + nea_preference_list.erase(std::remove_if(nea_preference_list.begin(), nea_preference_list.end(), ::isspace), + nea_preference_list.end()); + std::transform(nea_preference_list.begin(), + nea_preference_list.end(), + nea_preference_list.begin(), + [](unsigned char c) { return std::tolower(c); }); + std::vector nea_v = split(nea_preference_list, ','); + + security::preferred_ciphering_algorithms algo_list = {}; + int idx = 0; + for (const std::string& nea : nea_v) { + if (nea == "nea0") { + algo_list[idx] = security::ciphering_algorithm::nea0; + } else if (nea == "nea1") { + algo_list[idx] = security::ciphering_algorithm::nea1; + } else if (nea == "nea2") { + algo_list[idx] = security::ciphering_algorithm::nea2; + } else if (nea == "nea3") { + algo_list[idx] = security::ciphering_algorithm::nea3; + } + idx++; + } + return algo_list; +} + +static srs_cu_cp::rrc_ssb_mtc generate_rrc_ssb_mtc(unsigned period, unsigned offset, unsigned duration) +{ + srs_cu_cp::rrc_ssb_mtc ssb_mtc; + ssb_mtc.periodicity_and_offset.periodicity = (srs_cu_cp::rrc_periodicity_and_offset::periodicity_t)period; + ssb_mtc.periodicity_and_offset.offset = offset; + ssb_mtc.dur = duration; + + return ssb_mtc; +} + +srs_cu_cp::cu_cp_configuration srsran::generate_cu_cp_config(const cu_cp_unit_config& cu_cfg) +{ + srs_cu_cp::cu_cp_configuration out_cfg = config_helpers::make_default_cu_cp_config(); + out_cfg.max_nof_dus = cu_cfg.max_nof_dus; + out_cfg.max_nof_cu_ups = cu_cfg.max_nof_cu_ups; + + out_cfg.ngap_config.gnb_id = cu_cfg.gnb_id; + out_cfg.ngap_config.ran_node_name = cu_cfg.ran_node_name; + out_cfg.ngap_config.slice_configurations = cu_cfg.slice_cfg; + + srsran_assert(!cu_cfg.plmns.empty(), "PLMN list is empty"); + srsran_assert(!cu_cfg.tacs.empty(), "PLMN list is empty"); + out_cfg.ngap_config.plmn = cu_cfg.plmns.front(); + out_cfg.ngap_config.tac = cu_cfg.tacs.front(); + + out_cfg.rrc_config.gnb_id = cu_cfg.gnb_id; + out_cfg.rrc_config.force_reestablishment_fallback = cu_cfg.rrc_config.force_reestablishment_fallback; + out_cfg.rrc_config.rrc_procedure_timeout_ms = cu_cfg.rrc_config.rrc_procedure_timeout_ms; + out_cfg.rrc_config.int_algo_pref_list = generate_preferred_integrity_algorithms_list(cu_cfg); + out_cfg.rrc_config.enc_algo_pref_list = generate_preferred_ciphering_algorithms_list(cu_cfg); + out_cfg.rrc_config.drb_config = generate_cu_cp_qos_config(cu_cfg); + + if (!from_string(out_cfg.default_security_indication.integrity_protection_ind, + cu_cfg.security_config.integrity_protection)) { + report_error("Invalid value for integrity_protection={}\n", cu_cfg.security_config.integrity_protection); + } + if (!from_string(out_cfg.default_security_indication.confidentiality_protection_ind, + cu_cfg.security_config.confidentiality_protection)) { + report_error("Invalid value for confidentiality_protection={}\n", + cu_cfg.security_config.confidentiality_protection); + } + + out_cfg.ue_config.inactivity_timer = std::chrono::seconds{cu_cfg.inactivity_timer}; + out_cfg.ue_config.max_nof_supported_ues = cu_cfg.max_nof_dus * srsran::srs_cu_cp::MAX_NOF_UES_PER_DU; + out_cfg.ngap_config.pdu_session_setup_timeout = std::chrono::seconds{cu_cfg.pdu_session_setup_timeout}; + out_cfg.statistics_report_period = std::chrono::seconds{cu_cfg.metrics.cu_cp_statistics_report_period}; + + out_cfg.mobility_config.mobility_manager_config.trigger_handover_from_measurements = + cu_cfg.mobility_config.trigger_handover_from_measurements; + + // F1AP-CU config. + out_cfg.f1ap_config.ue_context_setup_timeout = std::chrono::milliseconds{cu_cfg.f1ap_config.ue_context_setup_timeout}; + out_cfg.f1ap_config.json_log_enabled = cu_cfg.loggers.f1ap_json_enabled; + + // Convert appconfig's cell list into cell manager type. + for (const auto& app_cfg_item : cu_cfg.mobility_config.cells) { + srs_cu_cp::cell_meas_config meas_cfg_item; + meas_cfg_item.serving_cell_cfg.nci = app_cfg_item.nr_cell_id; + if (app_cfg_item.periodic_report_cfg_id.has_value()) { + meas_cfg_item.periodic_report_cfg_id = + srs_cu_cp::uint_to_report_cfg_id(app_cfg_item.periodic_report_cfg_id.value()); + } + + if (app_cfg_item.gnb_id_bit_length.has_value()) { + meas_cfg_item.serving_cell_cfg.gnb_id = + config_helpers::get_gnb_id(app_cfg_item.nr_cell_id, app_cfg_item.gnb_id_bit_length.value()); + } + meas_cfg_item.serving_cell_cfg.pci = app_cfg_item.pci; + meas_cfg_item.serving_cell_cfg.band = app_cfg_item.band; + meas_cfg_item.serving_cell_cfg.ssb_arfcn = app_cfg_item.ssb_arfcn; + if (app_cfg_item.ssb_scs.has_value()) { + meas_cfg_item.serving_cell_cfg.ssb_scs.emplace() = + to_subcarrier_spacing(std::to_string(app_cfg_item.ssb_scs.value())); + } + if (app_cfg_item.ssb_duration.has_value() && app_cfg_item.ssb_offset.has_value() && + app_cfg_item.ssb_period.has_value()) { + // Add MTC config. + meas_cfg_item.serving_cell_cfg.ssb_mtc.emplace() = generate_rrc_ssb_mtc( + app_cfg_item.ssb_period.value(), app_cfg_item.ssb_offset.value(), app_cfg_item.ssb_duration.value()); + } + + for (const auto& ncell : app_cfg_item.ncells) { + srs_cu_cp::neighbor_cell_meas_config ncell_meas_cfg; + ncell_meas_cfg.nci = ncell.nr_cell_id; + for (const auto& report_id : ncell.report_cfg_ids) { + ncell_meas_cfg.report_cfg_ids.push_back(srs_cu_cp::uint_to_report_cfg_id(report_id)); + } + + meas_cfg_item.ncells.push_back(ncell_meas_cfg); + } + + // Store config. + out_cfg.mobility_config.meas_manager_config.cells[meas_cfg_item.serving_cell_cfg.nci] = meas_cfg_item; + } + + // Convert report config. + for (const auto& report_cfg_item : cu_cfg.mobility_config.report_configs) { + srs_cu_cp::rrc_report_cfg_nr report_cfg; + + if (report_cfg_item.report_type == "periodical") { + srs_cu_cp::rrc_periodical_report_cfg periodical; + + periodical.rs_type = srs_cu_cp::rrc_nr_rs_type::ssb; + if (report_cfg_item.report_interval_ms.has_value()) { + periodical.report_interv = report_cfg_item.report_interval_ms.value(); + } else { + periodical.report_interv = 1024; + } + periodical.report_amount = -1; + periodical.report_quant_cell.rsrp = true; + periodical.report_quant_cell.rsrq = true; + periodical.report_quant_cell.sinr = true; + periodical.max_report_cells = 4; + + srs_cu_cp::rrc_meas_report_quant report_quant_rs_idxes; + report_quant_rs_idxes.rsrp = true; + report_quant_rs_idxes.rsrq = true; + report_quant_rs_idxes.sinr = true; + periodical.report_quant_rs_idxes = report_quant_rs_idxes; + + periodical.max_nrof_rs_idxes_to_report = 4; + periodical.include_beam_meass = true; + periodical.use_allowed_cell_list = false; + + report_cfg.periodical = periodical; + } else { + srs_cu_cp::rrc_event_trigger_cfg event_trigger_cfg; + + // event id + // A3 event config is currently the only supported event. + auto& event_a3 = event_trigger_cfg.event_id.event_a3.emplace(); + + if (report_cfg_item.a3_report_type.empty() or !report_cfg_item.a3_offset_db.has_value() or + !report_cfg_item.a3_hysteresis_db.has_value()) { + report_error("Invalid measurement report configuration.\n"); + } + + if (report_cfg_item.a3_report_type == "rsrp") { + event_a3.a3_offset.rsrp = report_cfg_item.a3_offset_db.value(); + } else if (report_cfg_item.a3_report_type == "rsrq") { + event_a3.a3_offset.rsrq = report_cfg_item.a3_offset_db.value(); + } else if (report_cfg_item.a3_report_type == "sinr") { + event_a3.a3_offset.sinr = report_cfg_item.a3_offset_db.value(); + } + + event_a3.report_on_leave = false; + + event_a3.hysteresis = report_cfg_item.a3_hysteresis_db.value(); + event_a3.time_to_trigger = report_cfg_item.a3_time_to_trigger_ms.value(); + + event_a3.use_allowed_cell_list = false; + + event_trigger_cfg.rs_type = srs_cu_cp::rrc_nr_rs_type::ssb; + if (report_cfg_item.report_interval_ms.has_value()) { + event_trigger_cfg.report_interv = report_cfg_item.report_interval_ms.value(); + } else { + event_trigger_cfg.report_interv = 1024; + } + event_trigger_cfg.report_amount = -1; + event_trigger_cfg.report_quant_cell.rsrp = true; + event_trigger_cfg.report_quant_cell.rsrq = true; + event_trigger_cfg.report_quant_cell.sinr = true; + event_trigger_cfg.max_report_cells = 4; + + srs_cu_cp::rrc_meas_report_quant report_quant_rs_idxes; + report_quant_rs_idxes.rsrp = true; + report_quant_rs_idxes.rsrq = true; + report_quant_rs_idxes.sinr = true; + event_trigger_cfg.report_quant_rs_idxes = report_quant_rs_idxes; + + report_cfg.event_triggered = event_trigger_cfg; + } + + // Store config. + out_cfg.mobility_config.meas_manager_config + .report_config_ids[srs_cu_cp::uint_to_report_cfg_id(report_cfg_item.report_cfg_id)] = report_cfg; + } + + if (!config_helpers::is_valid_configuration(out_cfg)) { + report_error("Invalid CU-CP configuration.\n"); + } + + return out_cfg; +} diff --git a/apps/units/cu_cp/cu_cp_config_translators.h b/apps/units/cu_cp/cu_cp_config_translators.h new file mode 100644 index 0000000000..1cf408631a --- /dev/null +++ b/apps/units/cu_cp/cu_cp_config_translators.h @@ -0,0 +1,34 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/cu_cp/cu_cp_configuration.h" + +namespace srsran { + +struct cu_cp_unit_config; + +/// Converts and returns the given gnb application configuration to a CU-CP configuration. +srs_cu_cp::cu_cp_configuration generate_cu_cp_config(const cu_cp_unit_config& cu_cfg); + +} // namespace srsran diff --git a/apps/units/cu_cp/cu_cp_unit_config.h b/apps/units/cu_cp/cu_cp_unit_config.h index 2d6a4a67f0..b7d3d2519c 100644 --- a/apps/units/cu_cp/cu_cp_unit_config.h +++ b/apps/units/cu_cp/cu_cp_unit_config.h @@ -110,6 +110,8 @@ struct cu_cp_unit_security_config { struct cu_cp_unit_f1ap_config { /// Timeout for the UE context setup procedure in milliseconds. unsigned ue_context_setup_timeout = 1000; + /// F1-C bind address + std::string f1c_bind_address = "127.0.10.1"; }; /// RLC UM TX configuration @@ -239,6 +241,10 @@ struct cu_cp_unit_config { std::string ran_node_name = "cu_cp_01"; /// gNB identifier. gnb_id_t gnb_id = {411, 22}; + /// List of accepted PLMNs. + std::vector plmns; + /// List of accepted TACs. + std::vector tacs; /// Maximum number of DUs. uint16_t max_nof_dus = 6; /// Maximum number of CU-UPs. diff --git a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp index 97df03c740..3a8777eb12 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp +++ b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.cpp @@ -200,6 +200,7 @@ static void configure_cli11_f1ap_args(CLI::App& app, cu_cp_unit_f1ap_config& f1a f1ap_params.ue_context_setup_timeout, "UE context setup timeout in milliseconds") ->capture_default_str(); + add_option(app, "--f1c_bind_address", f1ap_params.f1c_bind_address, "F1-C bind address")->capture_default_str(); } static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_unit_config& cu_cp_params) @@ -216,6 +217,21 @@ static void configure_cli11_cu_cp_args(CLI::App& app, cu_cp_unit_config& cu_cp_p ->capture_default_str() ->check(CLI::Range(1, 7200)); + add_option(app, "--plmns", cu_cp_params.plmns, "List of allowed PLMNs"); + add_option(app, "--tacs", cu_cp_params.tacs, "List of allowed TACs")->check([](const std::string& value) { + std::stringstream ss(value); + unsigned tac; + ss >> tac; + + // Values 0 and 0xfffffe are reserved. + if (tac == 0U || tac == 0xfffffeU) { + return "TAC values 0 or 0xfffffe are reserved"; + } + + return (tac <= 0xffffffU) ? "" : "TAC value out of range"; + }); + ; + add_option(app, "--pdu_session_setup_timeout", cu_cp_params.pdu_session_setup_timeout, @@ -463,3 +479,37 @@ void srsran::configure_cli11_with_cu_cp_unit_config_schema(CLI::App& app, cu_cp_ }; add_option_cell(app, "--slicing", slicing_lambda, "Network slicing configuration"); } + +static std::vector auto_generate_plmns() +{ + std::vector out_cfg = {"00101"}; + + return out_cfg; +} + +static std::vector auto_generate_tacs() +{ + std::vector out_cfg = {7}; + + return out_cfg; +} + +void srsran::autoderive_cu_cp_parameters_after_parsing(CLI::App& app, + cu_cp_unit_config& unit_cfg, + std::vector plmns, + std::vector tacs) +{ + auto cu_cp_app = app.get_subcommand_ptr("cu_cp"); + // No PLMNs defined in the cu_cp section. Use the given ones. + if (cu_cp_app->count_all() == 0 || cu_cp_app->count("--plmns") == 0) { + srsran_assert(unit_cfg.plmns.empty(), "PLMN list is not empty"); + + unit_cfg.plmns = plmns.empty() ? auto_generate_plmns() : std::move(plmns); + } + + if (cu_cp_app->count_all() == 0 || cu_cp_app->count("--tacs") == 0) { + srsran_assert(unit_cfg.tacs.empty(), "TAC list is not empty"); + + unit_cfg.tacs = tacs.empty() ? auto_generate_tacs() : std::move(tacs); + } +} diff --git a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h index 030f765020..4bc512607c 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h +++ b/apps/units/cu_cp/cu_cp_unit_config_cli11_schema.h @@ -22,6 +22,8 @@ #pragma once +#include "srsran/adt/span.h" + #include "CLI/CLI11.hpp" namespace srsran { @@ -31,4 +33,10 @@ struct cu_cp_unit_config; /// Configures the given CLI11 application with the CU-CP application unit configuration schema. void configure_cli11_with_cu_cp_unit_config_schema(CLI::App& app, cu_cp_unit_config& unit_cfg); +/// Auto derive DU high parameters after the parsing. +void autoderive_cu_cp_parameters_after_parsing(CLI::App& app, + cu_cp_unit_config& unit_cfg, + std::vector plmns, + std::vector tacs); + } // namespace srsran diff --git a/apps/units/cu_cp/cu_cp_unit_config_validator.cpp b/apps/units/cu_cp/cu_cp_unit_config_validator.cpp index 2abac22156..ec159ef6d7 100644 --- a/apps/units/cu_cp/cu_cp_unit_config_validator.cpp +++ b/apps/units/cu_cp/cu_cp_unit_config_validator.cpp @@ -438,6 +438,12 @@ static bool validate_cu_cp_appconfig(const gnb_id_t gnb_id, const cu_cp_unit_con return false; } + if (config.plmns.size() != config.tacs.size()) { + fmt::print("Number of PLMNs '{}' do not match the number of TACs '{}'\n", config.plmns.size(), config.tacs.size()); + + return false; + } + return true; } diff --git a/apps/units/cu_up/cu_up_builder.cpp b/apps/units/cu_up/cu_up_builder.cpp index dbd7df393d..3102b01ac1 100644 --- a/apps/units/cu_up/cu_up_builder.cpp +++ b/apps/units/cu_up/cu_up_builder.cpp @@ -29,20 +29,20 @@ using namespace srsran; -std::unique_ptr srsran::build_cu_up(const cu_up_unit_config& unit_cfg, - worker_manager& workers, - srs_cu_up::e1ap_connection_client& e1ap_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk) +std::unique_ptr srsran::build_cu_up(const cu_up_unit_config& unit_cfg, + worker_manager& workers, + srs_cu_up::e1_connection_client& e1_conn_client, + f1u_cu_up_gateway& f1u_gateway, + dlt_pcap& gtpu_pcap, + timer_manager& timers, + io_broker& io_brk) { srs_cu_up::cu_up_configuration config = generate_cu_up_config(unit_cfg); config.ctrl_executor = workers.cu_up_ctrl_exec; config.cu_up_e2_exec = workers.cu_up_e2_exec; config.ue_exec_pool = workers.cu_up_exec_mapper.get(); config.io_ul_executor = workers.cu_up_io_ul_exec; // Optionally select separate exec for UL IO - config.e1ap.e1ap_conn_client = &e1ap_conn_client; + config.e1ap.e1_conn_client = &e1_conn_client; config.f1u_gateway = &f1u_gateway; config.gtpu_pcap = >pu_pcap; config.timers = &timers; diff --git a/apps/units/cu_up/cu_up_builder.h b/apps/units/cu_up/cu_up_builder.h index 47733e96ce..d1070771dc 100644 --- a/apps/units/cu_up/cu_up_builder.h +++ b/apps/units/cu_up/cu_up_builder.h @@ -28,7 +28,7 @@ namespace srsran { namespace srs_cu_up { -class e1ap_connection_client; +class e1_connection_client; } struct cu_up_unit_config; @@ -38,12 +38,12 @@ class io_broker; struct worker_manager; /// Builds the CU UP using the given arguments. -std::unique_ptr build_cu_up(const cu_up_unit_config& unit_cfg, - worker_manager& workers, - srs_cu_up::e1ap_connection_client& e1ap_conn_client, - f1u_cu_up_gateway& f1u_gateway, - dlt_pcap& gtpu_pcap, - timer_manager& timers, - io_broker& io_brk); +std::unique_ptr build_cu_up(const cu_up_unit_config& unit_cfg, + worker_manager& workers, + srs_cu_up::e1_connection_client& e1ap_conn_client, + f1u_cu_up_gateway& f1u_gateway, + dlt_pcap& gtpu_pcap, + timer_manager& timers, + io_broker& io_brk); } // namespace srsran diff --git a/apps/units/cu_up/cu_up_unit_config_translators.cpp b/apps/units/cu_up/cu_up_unit_config_translators.cpp index eb18e24eff..bc9e5f1eec 100644 --- a/apps/units/cu_up/cu_up_unit_config_translators.cpp +++ b/apps/units/cu_up/cu_up_unit_config_translators.cpp @@ -42,7 +42,6 @@ srs_cu_up::cu_up_configuration srsran::generate_cu_up_config(const cu_up_unit_co out_cfg.net_cfg.n3_ext_addr = config.upf_cfg.n3_ext_addr; out_cfg.net_cfg.n3_bind_interface = config.upf_cfg.n3_bind_interface; out_cfg.net_cfg.n3_rx_max_mmsg = config.upf_cfg.udp_rx_max_msgs; - out_cfg.net_cfg.f1u_bind_addr = config.upf_cfg.bind_addr; // FIXME: check if this can be removed for co-located case return out_cfg; } diff --git a/apps/units/flexible_du/du_high/CMakeLists.txt b/apps/units/flexible_du/du_high/CMakeLists.txt index 82e30c85c4..e07f3848c8 100644 --- a/apps/units/flexible_du/du_high/CMakeLists.txt +++ b/apps/units/flexible_du/du_high/CMakeLists.txt @@ -26,3 +26,4 @@ set(SOURCES add_library(srsran_du_high_unit_helpers STATIC ${SOURCES}) target_include_directories(srsran_du_high_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) +target_link_libraries(srsran_du_high_unit_helpers sched_config) 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 133764dd94..1857807613 100644 --- a/apps/units/flexible_du/du_high/du_high_config.h +++ b/apps/units/flexible_du/du_high/du_high_config.h @@ -28,6 +28,7 @@ #include "srsran/ran/bs_channel_bandwidth.h" #include "srsran/ran/direct_current_offset.h" #include "srsran/ran/five_qi.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/ran/gnb_id.h" #include "srsran/ran/lcid.h" #include "srsran/ran/ntn.h" @@ -350,6 +351,9 @@ struct du_high_unit_sib_config { std::vector sib_mapping_info; /// Periodicity of the SI-message in radio frames. Values: {8, 16, 32, 64, 128, 256, 512}. unsigned si_period_rf = 32; + /// SI window position of the associated SI-message. See TS 38.331, \c SchedulingInfo2-r17. Values: {1,...,256}. + /// \remark This field is only applicable for release 17 \c SI-SchedulingInfo. + std::optional si_window_position; }; struct sib_ue_timers_and_constants { @@ -732,6 +736,8 @@ struct du_high_unit_config { gnb_id_t gnb_id = {411, 22}; /// Node name. std::string ran_node_name = "srsgnb01"; + /// DU identifier. + gnb_du_id_t gnb_du_id = gnb_du_id_t::min; /// PCAPs. du_high_unit_pcap_config pcaps; /// Metrics. 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 2285558106..8203b34658 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 @@ -818,6 +818,10 @@ static void configure_cli11_si_sched_info(CLI::App& app, du_high_unit_sib_config "Mapping of SIB types to SI-messages. SIB numbers should not be repeated") ->capture_default_str() ->check(CLI::IsMember({2, 19})); + add_option( + app, "--si_window_position", si_sched_info.si_window_position, "SI window position of the associated SI-message") + ->capture_default_str() + ->check(CLI::Range(1, 256)); } static void configure_cli11_prach_args(CLI::App& app, du_high_unit_prach_config& prach_params) @@ -1405,6 +1409,9 @@ void srsran::configure_cli11_with_du_high_config_schema(CLI::App& app, du_high_p ->capture_default_str() ->check(CLI::Range(22, 32)); add_option(app, "--ran_node_name", parsed_cfg.config.ran_node_name, "RAN node name")->capture_default_str(); + add_option(app, "--gnb_du_id", parsed_cfg.config.gnb_du_id, "gNB-DU Id") + ->capture_default_str() + ->check(CLI::Range(static_cast(0U), static_cast(pow(2, 36) - 1))); // Loggers section. CLI::App* log_subcmd = add_subcommand(app, "log", "Logging configuration")->configurable(); 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 b6d16ee6ff..d977844f95 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 @@ -125,21 +125,21 @@ static sib19_info create_sib19_info(const du_high_unit_config& config) sib19.ephemeris_info = config.ntn_cfg.value().ephemeris_info; // These ephemeris parameters are all scaled in accordance with NIMA TR 8350.2. - if (variant_holds_alternative(sib19.ephemeris_info.value())) { - variant_get(sib19.ephemeris_info.value()).position_x /= 1.3; - variant_get(sib19.ephemeris_info.value()).position_y /= 1.3; - variant_get(sib19.ephemeris_info.value()).position_z /= 1.3; - variant_get(sib19.ephemeris_info.value()).velocity_vx /= 0.06; - variant_get(sib19.ephemeris_info.value()).velocity_vy /= 0.06; - variant_get(sib19.ephemeris_info.value()).velocity_vz /= 0.06; - } else if (variant_holds_alternative(sib19.ephemeris_info.value())) { - variant_get(sib19.ephemeris_info.value()).semi_major_axis -= 6500000; - variant_get(sib19.ephemeris_info.value()).semi_major_axis /= 0.004249; - variant_get(sib19.ephemeris_info.value()).eccentricity /= 0.00000001431; - variant_get(sib19.ephemeris_info.value()).periapsis /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).longitude /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).inclination /= 0.00000002341; - variant_get(sib19.ephemeris_info.value()).mean_anomaly /= 0.00000002341; + if (std::holds_alternative(sib19.ephemeris_info.value())) { + std::get(sib19.ephemeris_info.value()).position_x /= 1.3; + std::get(sib19.ephemeris_info.value()).position_y /= 1.3; + std::get(sib19.ephemeris_info.value()).position_z /= 1.3; + std::get(sib19.ephemeris_info.value()).velocity_vx /= 0.06; + std::get(sib19.ephemeris_info.value()).velocity_vy /= 0.06; + std::get(sib19.ephemeris_info.value()).velocity_vz /= 0.06; + } else if (std::holds_alternative(sib19.ephemeris_info.value())) { + std::get(sib19.ephemeris_info.value()).semi_major_axis -= 6500000; + std::get(sib19.ephemeris_info.value()).semi_major_axis /= 0.004249; + std::get(sib19.ephemeris_info.value()).eccentricity /= 0.00000001431; + std::get(sib19.ephemeris_info.value()).periapsis /= 0.00000002341; + std::get(sib19.ephemeris_info.value()).longitude /= 0.00000002341; + std::get(sib19.ephemeris_info.value()).inclination /= 0.00000002341; + std::get(sib19.ephemeris_info.value()).mean_anomaly /= 0.00000002341; } if (config.ntn_cfg.value().distance_threshold.has_value()) { sib19.distance_thres = config.ntn_cfg.value().distance_threshold.value(); @@ -314,6 +314,7 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c auto& out_si = out_cell.si_config->si_sched_info[i]; out_si.si_period_radio_frames = base_cell.sib_cfg.si_sched_info[i].si_period_rf; out_si.sib_mapping_info.resize(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size()); + out_si.si_window_position = base_cell.sib_cfg.si_sched_info[i].si_window_position; for (unsigned j = 0; j != base_cell.sib_cfg.si_sched_info[i].sib_mapping_info.size(); ++j) { sibs_included.push_back(base_cell.sib_cfg.si_sched_info[i].sib_mapping_info[j]); out_si.sib_mapping_info[j] = static_cast(sibs_included.back()); @@ -561,7 +562,7 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c .uci_cfg.value() .beta_offsets_cfg->emplace(); } - if (not variant_holds_alternative( + if (not std::holds_alternative( out_cell.ue_ded_serv_cell_cfg.ul_config.value() .init_ul_bwp.pusch_cfg.value() .uci_cfg.value() @@ -575,11 +576,10 @@ std::vector srsran::generate_du_cell_config(const du_high_unit_c .uci_cfg.value() .beta_offsets_cfg->emplace(); } - auto& b_offsets = - variant_get(out_cell.ue_ded_serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()); + auto& b_offsets = std::get(out_cell.ue_ded_serv_cell_cfg.ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()); b_offsets.beta_offset_ack_idx_1 = base_cell.pusch_cfg.b_offset_ack_idx_1; b_offsets.beta_offset_ack_idx_2 = base_cell.pusch_cfg.b_offset_ack_idx_2; b_offsets.beta_offset_ack_idx_3 = base_cell.pusch_cfg.b_offset_ack_idx_3; 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 ab7b47264c..5d0da16b16 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 @@ -293,6 +293,18 @@ static bool validate_pdsch_cell_unit_config(const du_high_unit_pdsch_config& con return false; } + unsigned max_ue_mcs = 28; + if (config.mcs_table == pdsch_mcs_table::qam256) { + max_ue_mcs = 27; + } + + if (config.min_ue_mcs > max_ue_mcs) { + fmt::print("Invalid PDSCH min_ue_mcs (i.e., {}) for the selected MCS table (i.e., {}).\n", + config.min_ue_mcs, + (config.mcs_table == pdsch_mcs_table::qam256) ? "qam256" : "qam64"); + return false; + } + if (not validate_rv_sequence(config.rv_sequence)) { return false; } @@ -336,6 +348,18 @@ static bool validate_pusch_cell_unit_config(const du_high_unit_pusch_config& con return false; } + unsigned max_ue_mcs = 28; + if (config.mcs_table == pusch_mcs_table::qam256) { + max_ue_mcs = 27; + } + + if (config.min_ue_mcs > max_ue_mcs) { + fmt::print("Invalid PUSCH min_ue_mcs (i.e., {}) for the selected MCS table (i.e., {}).\n", + config.min_ue_mcs, + (config.mcs_table == pusch_mcs_table::qam256) ? "qam256" : "qam64"); + return false; + } + if (not validate_rv_sequence(config.rv_sequence)) { return false; } @@ -584,6 +608,9 @@ static bool validate_dl_arfcn_and_band(const du_high_unit_base_cell_config& conf static bool validate_cell_sib_config(const du_high_unit_base_cell_config& cell_cfg) { + // See TS 38.331, V17.0.0, \c type1-r17 in \c SIB-TypeInfo-v1700. + static const unsigned r17_min_sib_type = 15; + const du_high_unit_sib_config& sib_cfg = cell_cfg.sib_cfg; for (const auto& si_msg : sib_cfg.si_sched_info) { @@ -597,20 +624,41 @@ static bool validate_cell_sib_config(const du_high_unit_base_cell_config& cell_c } } - // Check if there are repeated SIBs in the SI messages. - std::vector sibs_included; + std::vector sibs_included; + std::vector si_window_positions; for (const auto& si_msg : sib_cfg.si_sched_info) { for (const uint8_t sib_it : si_msg.sib_mapping_info) { + // si-WindowPosition-r17 is part of release 17 specification only. See TS 38.331, V17.0.0, \c SchedulingInfo2-r17. + if (sib_it < r17_min_sib_type and si_msg.si_window_position.has_value()) { + fmt::print("The SIB{} cannot be configured with SI-window position", sib_it); + return false; + } sibs_included.push_back(sib_it); } + if (si_msg.si_window_position.has_value()) { + si_window_positions.push_back(si_msg.si_window_position.value()); + } } std::sort(sibs_included.begin(), sibs_included.end()); + // Check if there are repeated SIBs in the SI messages. const auto duplicate_it = std::adjacent_find(sibs_included.begin(), sibs_included.end()); if (duplicate_it != sibs_included.end()) { fmt::print("The SIB{} cannot be included more than once in the broadcast SI messages", *duplicate_it); return false; } + // Check whether SI window position when provided in SI scheduling information is in ascending order. See TS 38.331, + // \c si-WindowPosition. + for (unsigned i = 0, j = 0; i < si_window_positions.size() && j < si_window_positions.size(); ++i, ++j) { + if (si_window_positions[i] > si_window_positions[j]) { + fmt::print("The SI window position in the subsequent entry in SI scheduling information must have higher value " + "than in the previous entry ({}>{})", + si_window_positions[i], + si_window_positions[j]); + return false; + } + } + return true; } 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 b149db5d23..d8d79f555a 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 @@ -32,60 +32,62 @@ using namespace srsran; -void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, - const du_high_unit_config& du_high_unit_cfg, - unsigned du_id, - du_high_executor_mapper& execution_mapper, - srs_du::f1c_connection_client& f1c_client_handler, - srs_du::f1u_du_gateway& f1u_gw, - timer_manager& timer_mng, - mac_pcap& mac_p, - rlc_pcap& rlc_p, - console_helper& console_helper, - 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) +void srsran::configure_du_high_metrics(const du_high_unit_config& du_high_unit_cfg, + console_helper& console_helper, + metrics_log_helper& metrics_logger, + e2_metric_connector_manager& e2_metric_connectors, + metrics_hub& metrics_hub) { // Generate DU cells. - out_cfg.du_hi.cells = generate_du_cell_config(du_high_unit_cfg); + auto cells = generate_du_cell_config(du_high_unit_cfg); // DU cell config - console_helper.set_cells(out_cfg.du_hi.cells); + console_helper.set_cells(cells); // Set up sources for the DU Scheruler UE metrics and add them to metric hub. - for (unsigned i = 0; i < out_cfg.du_hi.cells.size(); i++) { - std::string source_name = "DU " + std::to_string(i + du_id); + for (unsigned i = 0; i != cells.size(); i++) { + std::string source_name = "DU " + std::to_string(i); auto source = std::make_unique(source_name); - metrics_hub.add_source(std::move(source)); - - // Get DU Scheduler UE metrics source pointer. - scheduler_ue_metrics_source* sched_source = metrics_hub.get_scheduler_ue_metrics_source(source_name); - if (sched_source == nullptr) { - continue; - } // Connect Console Aggregator to DU Scheduler UE metrics. - sched_source->add_subscriber(console_helper.get_stdout_metrics_notifier()); + source->add_subscriber(console_helper.get_stdout_metrics_notifier()); if (metrics_logger.is_enabled()) { - sched_source->add_subscriber(metrics_logger); + source->add_subscriber(metrics_logger); } // Connect JSON metrics reporter to DU Scheduler UE metrics. if (du_high_unit_cfg.metrics.enable_json_metrics) { - sched_source->add_subscriber(console_helper.get_json_metrics_notifier()); + source->add_subscriber(console_helper.get_json_metrics_notifier()); } // Connect E2 agent to DU Scheduler UE metrics. if (du_high_unit_cfg.e2_cfg.enable_du_e2) { - sched_source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(i)); + source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(i)); } + + metrics_hub.add_source(std::move(source)); } +} +void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, + const du_high_unit_config& du_high_unit_cfg, + unsigned du_idx, + du_high_executor_mapper& execution_mapper, + srs_du::f1c_connection_client& f1c_client_handler, + srs_du::f1u_du_gateway& f1u_gw, + 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. srs_du::du_high_configuration& du_hi_cfg = out_cfg.du_hi; + du_hi_cfg.cells = generate_du_cell_config(du_high_unit_cfg); du_hi_cfg.exec_mapper = &execution_mapper; du_hi_cfg.f1c_client = &f1c_client_handler; du_hi_cfg.f1u_gw = &f1u_gw; @@ -95,19 +97,18 @@ void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, du_hi_cfg.qos = generate_du_qos_config(du_high_unit_cfg); du_hi_cfg.mac_p = &mac_p; du_hi_cfg.rlc_p = &rlc_p; - du_hi_cfg.gnb_du_id = du_id + 1; + du_hi_cfg.gnb_du_id = (gnb_du_id_t)((unsigned)du_high_unit_cfg.gnb_du_id + du_idx); du_hi_cfg.gnb_du_name = fmt::format("srsdu{}", du_hi_cfg.gnb_du_id); - du_hi_cfg.du_bind_addr = transport_layer_address::create_from_string(fmt::format("127.0.0.{}", du_hi_cfg.gnb_du_id)); - du_hi_cfg.mac_cfg = generate_mac_expert_config(du_high_unit_cfg); + du_hi_cfg.mac_cfg = generate_mac_expert_config(du_high_unit_cfg); // Assign different initial C-RNTIs to different DUs. - du_hi_cfg.mac_cfg.initial_crnti = to_rnti(0x4601 + (0x1000 * du_id)); - du_hi_cfg.sched_ue_metrics_notifier = metrics_hub.get_scheduler_ue_metrics_source("DU " + std::to_string(du_id)); + 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.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_id); + 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) { @@ -121,8 +122,8 @@ void srsran::fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, // 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_id)); - rlc_source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(du_id)); + 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)); 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 0fdc9b4654..e4b36b43b9 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 @@ -43,6 +43,13 @@ struct du_high_unit_config; struct du_high_wrapper_config; 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, + console_helper& console_helper, + metrics_log_helper& metrics_logger, + e2_metric_connector_manager& e2_metric_connectors, + metrics_hub& metrics_hub); + /// Fills the given DU high wrapper configuration. void fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, const du_high_unit_config& du_high_unit_cfg, @@ -53,7 +60,6 @@ void fill_du_high_wrapper_config(du_high_wrapper_config& out_cfg, timer_manager& timer_mng, mac_pcap& mac_p, rlc_pcap& rlc_p, - console_helper& console_helper, metrics_log_helper& metrics_logger, e2_connection_client& e2_client_handler, e2_metric_connector_manager& e2_metric_connectors, diff --git a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp index f1bae501e6..dd6dccb8c6 100644 --- a/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_cli11_schema.cpp @@ -23,11 +23,24 @@ #include "du_low_config_cli11_schema.h" #include "apps/units/flexible_du/support/cli11_cpu_affinities_parser_helper.h" #include "du_low_config.h" +#include "srsran/adt/expected.h" #include "srsran/support/cli11_utils.h" #include "srsran/support/config_parsers.h" using namespace srsran; +template +static expected parse_int(const std::string& value) +{ + try { + return std::stoi(value); + } catch (const std::invalid_argument& e) { + return {e.what()}; + } catch (const std::out_of_range& e) { + return {e.what()}; + } +} + static void configure_cli11_log_args(CLI::App& app, du_low_unit_logger_config& log_params) { auto level_check = [](const std::string& value) -> std::string { @@ -43,6 +56,28 @@ static void configure_cli11_log_args(CLI::App& app, du_low_unit_logger_config& l log_params.broadcast_enabled, "Enable logging in the physical and MAC layer of broadcast messages and all PRACH opportunities") ->always_capture_default(); + app.add_option("--phy_rx_symbols_filename", + log_params.phy_rx_symbols_filename, + "Set to a valid file path to print the received symbols.") + ->always_capture_default(); + app.add_option_function( + "--phy_rx_symbols_port", + [&log_params](const std::string& value) { + if (value == "all") { + log_params.phy_rx_symbols_port = std::nullopt; + } else { + log_params.phy_rx_symbols_port = parse_int(value).value(); + } + }, + "Set to a valid receive port number to dump the IQ symbols from that port only, or set to \"all\" to dump the " + "IQ symbols from all UL receive ports. Only works if \"phy_rx_symbols_filename\" is set.") + ->default_str("0") + ->check(CLI::NonNegativeNumber | CLI::IsMember({"all"})); + app.add_option("--phy_rx_symbols_prach", + log_params.phy_rx_symbols_prach, + "Set to true to dump the IQ symbols from all the PRACH ports. Only works if " + "\"phy_rx_symbols_filename\" is set.") + ->capture_default_str(); add_option( app, "--hex_max_size", log_params.hex_max_size, "Maximum number of bytes to print in hex (zero for no hex dumps)") diff --git a/apps/units/flexible_du/du_low/du_low_config_translator.cpp b/apps/units/flexible_du/du_low/du_low_config_translator.cpp index df92700cff..95097624bb 100644 --- a/apps/units/flexible_du/du_low/du_low_config_translator.cpp +++ b/apps/units/flexible_du/du_low/du_low_config_translator.cpp @@ -32,7 +32,8 @@ using namespace srsran; static void generate_du_low_config(du_low_config& out_config, const du_low_unit_config& du_low, span du_cells, - span max_puschs_per_slot) + span max_puschs_per_slot, + unsigned du_id) { out_config.cells.reserve(du_cells.size()); @@ -126,7 +127,7 @@ static void generate_du_low_config(du_low_config& out_config, upper_phy_cell.rx_symbol_printer_port = du_low.loggers.phy_rx_symbols_port; upper_phy_cell.rx_symbol_printer_prach = du_low.loggers.phy_rx_symbols_prach; upper_phy_cell.logger_max_hex_size = du_low.loggers.hex_max_size; - upper_phy_cell.sector_id = i; + upper_phy_cell.sector_id = du_id + i; upper_phy_cell.nof_tx_ports = cell.dl_carrier.nof_ant; upper_phy_cell.nof_rx_ports = cell.ul_carrier.nof_ant; upper_phy_cell.ldpc_decoder_iterations = du_low.expert_phy_cfg.pusch_decoder_max_iterations; @@ -171,8 +172,9 @@ void srsran::generate_du_low_wrapper_config(du_low_wrapper_config& const du_low_unit_config& du_low_unit_cfg, std::vector prach_ports, span du_cells, - span max_puschs_per_slot) + span max_puschs_per_slot, + unsigned du_id) { - generate_du_low_config(out_config.du_low_cfg, du_low_unit_cfg, du_cells, max_puschs_per_slot); + generate_du_low_config(out_config.du_low_cfg, du_low_unit_cfg, du_cells, max_puschs_per_slot, du_id); out_config.prach_ports = std::move(prach_ports); } \ No newline at end of file diff --git a/apps/units/flexible_du/du_low/du_low_config_translator.h b/apps/units/flexible_du/du_low/du_low_config_translator.h index 577b38d7f4..86f1821e6e 100644 --- a/apps/units/flexible_du/du_low/du_low_config_translator.h +++ b/apps/units/flexible_du/du_low/du_low_config_translator.h @@ -35,6 +35,7 @@ void generate_du_low_wrapper_config(du_low_wrapper_config& out_conf const du_low_unit_config& du_low_unit_cfg, std::vector prach_ports, span du_cells, - span max_puschs_per_slot); + span max_puschs_per_slot, + unsigned du_id); } // namespace srsran diff --git a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp index c856cb5aa6..1c2c663bf8 100644 --- a/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp +++ b/apps/units/flexible_du/du_low/du_low_wrapper_config_helper.cpp @@ -68,8 +68,8 @@ void srsran::make_du_low_wrapper_config_and_dependencies( { out_cfg.du_low_cfg.logger = &srslog::fetch_basic_logger("DU"); - generate_du_low_wrapper_config(out_cfg, du_low_unit_cfg, std::move(prach_ports), du_cells, max_puschs_per_slot); - out_cfg.prach_ports = std::move(prach_ports); + generate_du_low_wrapper_config( + out_cfg, du_low_unit_cfg, std::move(prach_ports), du_cells, max_puschs_per_slot, du_id); // Fill the workers information. for (unsigned i = 0, e = out_cfg.du_low_cfg.cells.size(); i != e; ++i) { diff --git a/apps/units/flexible_du/split_7_2/CMakeLists.txt b/apps/units/flexible_du/split_7_2/CMakeLists.txt index 36c4a90efe..24cefdc87c 100644 --- a/apps/units/flexible_du/split_7_2/CMakeLists.txt +++ b/apps/units/flexible_du/split_7_2/CMakeLists.txt @@ -29,5 +29,5 @@ set(SOURCES ru_ofh_config_validator.cpp) add_library(srsran_split_7_2_app_unit_helpers STATIC ${SOURCES}) -target_link_libraries(srsran_split_7_2_app_unit_helpers srsran_flexible_du_helpers) +target_link_libraries(srsran_split_7_2_app_unit_helpers srsran_ru_ofh srsran_flexible_du_helpers) target_include_directories(srsran_split_7_2_app_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/units/flexible_du/split_7_2/ru_ofh_factories.cpp b/apps/units/flexible_du/split_7_2/ru_ofh_factories.cpp index 9382b6fc4e..6f8302dd87 100644 --- a/apps/units/flexible_du/split_7_2/ru_ofh_factories.cpp +++ b/apps/units/flexible_du/split_7_2/ru_ofh_factories.cpp @@ -27,6 +27,9 @@ using namespace srsran; +// Number of OFH sectors served by a single executor for transmitter and receiver tasks. +static const unsigned NOF_SECTORS_PER_TXRX_EXECUTOR = 2; + /// Resolves the Open Fronthaul Radio Unit dependencies and adds them to the configuration. static void configure_ru_ofh_executors_and_notifiers(unsigned nof_sectors, ru_ofh_dependencies& dependencies, @@ -45,10 +48,11 @@ static void configure_ru_ofh_executors_and_notifiers(unsigned for (unsigned i = 0; i != nof_sectors; ++i) { dependencies.sector_dependencies.emplace_back(); ru_ofh_sector_dependencies& sector_deps = dependencies.sector_dependencies.back(); - sector_deps.logger = dependencies.logger; - sector_deps.receiver_executor = workers.ru_rx_exec[i]; - sector_deps.transmitter_executor = workers.ru_tx_exec[i]; - sector_deps.downlink_executor = workers.ru_dl_exec[i]; + // Note, one executor for transmitter and receiver tasks is shared per two sectors. + sector_deps.txrx_executor = workers.ru_txrx_exec[i / NOF_SECTORS_PER_TXRX_EXECUTOR]; + sector_deps.uplink_executor = workers.ru_rx_exec[i]; + sector_deps.downlink_executor = workers.ru_dl_exec[i]; + sector_deps.logger = dependencies.logger; } } diff --git a/apps/units/flexible_du/split_8/CMakeLists.txt b/apps/units/flexible_du/split_8/CMakeLists.txt index 5cb83d0f3e..807b71d3a3 100644 --- a/apps/units/flexible_du/split_8/CMakeLists.txt +++ b/apps/units/flexible_du/split_8/CMakeLists.txt @@ -25,5 +25,5 @@ set(SOURCES ru_sdr_factories.cpp) add_library(srsran_split_8_app_unit_helpers STATIC ${SOURCES}) -target_link_libraries(srsran_split_8_app_unit_helpers srsran_flexible_du_helpers) +target_link_libraries(srsran_split_8_app_unit_helpers srsran_ru_generic srsran_lower_phy srsran_flexible_du_helpers) target_include_directories(srsran_split_8_app_unit_helpers PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/apps/units/flexible_du/split_dynamic/CMakeLists.txt b/apps/units/flexible_du/split_dynamic/CMakeLists.txt index add97df7f8..52c64b5559 100644 --- a/apps/units/flexible_du/split_dynamic/CMakeLists.txt +++ b/apps/units/flexible_du/split_dynamic/CMakeLists.txt @@ -26,24 +26,11 @@ set(SOURCES dynamic_du_unit_cli11_schema.cpp dynamic_du_unit_config_validator.cpp) -add_library(srsran_flexible_du_ru_dynamic STATIC ${SOURCES}) -target_include_directories(srsran_flexible_du_ru_dynamic PRIVATE ${CMAKE_SOURCE_DIR}) -target_link_libraries(srsran_flexible_du_ru_dynamic - srsran_du_high - srsran_du - mac_fapi_adaptor_factory - phy_fapi_adaptor_factory - srsran_fapi - srsran_phy_support - srsran_lower_phy - srsran_upper_phy - srsran_signal_processors - srsran_channel_processors - srsran_channel_equalizer - srsran_channel_precoder +add_library(srsran_flexible_du_dynamic STATIC ${SOURCES}) +target_include_directories(srsran_flexible_du_dynamic PRIVATE ${CMAKE_SOURCE_DIR}) +target_link_libraries(srsran_flexible_du_dynamic + srsran_du_wrapper srsran_ru_dummy - srsran_ru_generic - srsran_ru_ofh srsran_pcap srsran_app_services srsran_fapi_app_unit 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 372876adda..56fe4367eb 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.cpp @@ -21,10 +21,11 @@ */ #include "dynamic_du_factory.h" -#include "apps/gnb/gnb_appconfig_translators.h" #include "apps/services/console_helper.h" #include "apps/services/e2_metric_connector_manager.h" #include "apps/services/metrics_log_helper.h" +#include "apps/units/flexible_du/du_high/du_high_config_translators.h" +#include "apps/units/flexible_du/du_high/du_high_wrapper_config_helper.h" #include "apps/units/flexible_du/du_low/du_low_config_translator.h" #include "apps/units/flexible_du/du_low/du_low_wrapper_config_helper.h" #include "apps/units/flexible_du/split_7_2/ru_ofh_factories.h" @@ -38,230 +39,6 @@ using namespace srsran; -static du_low_wrapper_config create_du_low_config(const du_high_unit_cell_config& du_high, - const du_low_unit_config& params, - upper_phy_rg_gateway* rg_gateway, - span dl_executors, - task_executor* pucch_executor, - task_executor* pusch_executor, - task_executor* pusch_decoder_executor, - task_executor* prach_executor, - task_executor* srs_executor, - task_executor* pdsch_codeblock_executor, - upper_phy_rx_symbol_request_notifier* rx_symbol_request_notifier, - unsigned sector_id) -{ - du_low_wrapper_config du_lo_wrap_cfg{}; - du_low_cell_config& du_lo_cell_cfg = du_lo_wrap_cfg.du_low_cfg.cells.emplace_back(); - - du_lo_wrap_cfg.du_low_cfg.logger = &srslog::fetch_basic_logger("DU"); - - du_lo_cell_cfg.dl_proc_cfg.ldpc_encoder_type = "auto"; - du_lo_cell_cfg.dl_proc_cfg.crc_calculator_type = "auto"; - - const du_low_unit_expert_threads_config& upper_phy_threads_cfg = params.expert_execution_cfg.threads; - - if ((upper_phy_threads_cfg.pdsch_processor_type == "lite") || - ((upper_phy_threads_cfg.pdsch_processor_type == "auto") && (upper_phy_threads_cfg.nof_dl_threads == 1))) { - du_lo_cell_cfg.dl_proc_cfg.pdsch_processor.emplace(); - } else if ((upper_phy_threads_cfg.pdsch_processor_type == "concurrent") || - ((upper_phy_threads_cfg.pdsch_processor_type == "auto") && (upper_phy_threads_cfg.nof_dl_threads > 1))) { - pdsch_processor_concurrent_configuration pdsch_proc_config; - pdsch_proc_config.nof_pdsch_codeblock_threads = upper_phy_threads_cfg.nof_dl_threads; - pdsch_proc_config.max_nof_simultaneous_pdsch = - (MAX_UE_PDUS_PER_SLOT + 1) * params.expert_phy_cfg.max_processing_delay_slots; - pdsch_proc_config.pdsch_codeblock_task_executor = pdsch_codeblock_executor; - du_lo_cell_cfg.dl_proc_cfg.pdsch_processor.emplace(pdsch_proc_config); - } else if (upper_phy_threads_cfg.pdsch_processor_type == "generic") { - du_lo_cell_cfg.dl_proc_cfg.pdsch_processor.emplace(); - } else { - srsran_assert(false, "Invalid PDSCH processor type {}.", upper_phy_threads_cfg.pdsch_processor_type); - } - du_lo_cell_cfg.dl_proc_cfg.nof_concurrent_threads = upper_phy_threads_cfg.nof_dl_threads; - - du_lo_cell_cfg.upper_phy_cfg = generate_du_low_config(du_high, params, sector_id); - du_lo_wrap_cfg.prach_ports.push_back(du_high.cell.prach_cfg.ports); - - // Fill the rest with the parameters. - upper_phy_config& cfg = du_lo_cell_cfg.upper_phy_cfg; - cfg.rg_gateway = rg_gateway; - cfg.dl_executors = std::vector(dl_executors.begin(), dl_executors.end()); - cfg.pucch_executor = pucch_executor; - cfg.pusch_executor = pusch_executor; - cfg.pusch_decoder_executor = pusch_decoder_executor; - cfg.prach_executor = prach_executor; - cfg.srs_executor = srs_executor; - cfg.rx_symbol_request_notifier = rx_symbol_request_notifier; - cfg.crc_calculator_type = "auto"; - cfg.ldpc_rate_dematcher_type = "auto"; - cfg.ldpc_decoder_type = "auto"; - - return du_lo_wrap_cfg; -} - -std::vector> -srsran::make_gnb_dus(const gnb_appconfig& gnb_cfg, - const dynamic_du_unit_config& dyn_du_cfg, - span du_cells, - worker_manager& workers, - upper_phy_rg_gateway& rg_gateway, - upper_phy_rx_symbol_request_notifier& rx_symbol_request_notifier, - srs_du::f1c_connection_client& f1c_client_handler, - srs_du::f1u_du_gateway& f1u_gw, - timer_manager& timer_mng, - mac_pcap& mac_p, - rlc_pcap& rlc_p, - console_helper& console_helper, - 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) -{ - const du_high_unit_config& du_high = dyn_du_cfg.du_high_cfg.config; - const du_low_unit_config& du_low = dyn_du_cfg.du_low_cfg; - const fapi_unit_config& fapi_cfg = dyn_du_cfg.fapi_cfg; - - // DU cell config - console_helper.set_cells(du_cells); - - // Set up sources for the DU Scheruler UE metrics and add them to metric hub. - for (unsigned i = 0; i < du_cells.size(); i++) { - std::string source_name = "DU " + std::to_string(i); - auto source = std::make_unique(source_name); - metrics_hub.add_source(std::move(source)); - - // Get DU Scheduler UE metrics source pointer. - scheduler_ue_metrics_source* sched_source = metrics_hub.get_scheduler_ue_metrics_source(source_name); - if (sched_source == nullptr) { - continue; - } - - // Connect Console Aggregator to DU Scheduler UE metrics. - sched_source->add_subscriber(console_helper.get_stdout_metrics_notifier()); - - if (metrics_logger.is_enabled()) { - sched_source->add_subscriber(metrics_logger); - } - - // Connect JSON metrics reporter to DU Scheduler UE metrics. - if (gnb_cfg.metrics_cfg.enable_json_metrics) { - sched_source->add_subscriber(console_helper.get_json_metrics_notifier()); - } - - // Connect E2 agent to DU Scheduler UE metrics. - if (gnb_cfg.e2_cfg.enable_du_e2) { - sched_source->add_subscriber(e2_metric_connectors.get_e2_du_metric_notifier(i)); - } - } - - std::vector> du_insts; - for (unsigned i = 0, e = du_cells.size(); i != e; ++i) { - // Create a gNB config with one cell. - du_high_unit_config tmp_cfg = du_high; - tmp_cfg.cells_cfg.resize(1); - tmp_cfg.cells_cfg[0] = du_high.cells_cfg[i]; - - du_wrapper_config du_cfg = {}; - std::vector du_low_dl_exec; - workers.get_du_low_dl_executors(du_low_dl_exec, i); - - // DU-low configuration. - du_cfg.du_low_cfg = create_du_low_config(tmp_cfg.cells_cfg[i], - du_low, - &rg_gateway, - du_low_dl_exec, - workers.upper_pucch_exec[i], - workers.upper_pusch_exec[i], - workers.upper_pusch_decoder_exec[i], - workers.upper_prach_exec[i], - workers.upper_srs_exec[i], - workers.upper_pdsch_exec[i], - &rx_symbol_request_notifier, - i); - - // DU-high configuration. - srs_du::du_high_configuration& du_hi_cfg = du_cfg.du_high_cfg.du_hi; - du_hi_cfg.exec_mapper = &workers.get_du_high_executor_mapper(i); - du_hi_cfg.f1c_client = &f1c_client_handler; - du_hi_cfg.f1u_gw = &f1u_gw; - du_hi_cfg.phy_adapter = nullptr; - du_hi_cfg.timers = &timer_mng; - du_hi_cfg.cells = {du_cells[i]}; - du_hi_cfg.srbs = generate_du_srb_config(du_high); - du_hi_cfg.qos = generate_du_qos_config(du_high); - du_hi_cfg.mac_p = &mac_p; - du_hi_cfg.rlc_p = &rlc_p; - du_hi_cfg.gnb_du_id = du_insts.size() + 1; - du_hi_cfg.gnb_du_name = fmt::format("srsdu{}", du_hi_cfg.gnb_du_id); - du_hi_cfg.du_bind_addr = - transport_layer_address::create_from_string(fmt::format("127.0.0.{}", du_hi_cfg.gnb_du_id)); - du_hi_cfg.mac_cfg = generate_mac_expert_config(du_high); - // Assign different initial C-RNTIs to different DUs. - du_hi_cfg.mac_cfg.initial_crnti = to_rnti(0x4601 + (0x1000 * du_insts.size())); - du_hi_cfg.sched_ue_metrics_notifier = metrics_hub.get_scheduler_ue_metrics_source("DU " + std::to_string(i)); - du_hi_cfg.sched_cfg = generate_scheduler_expert_config(du_high); - - // Connect RLC metrics to sinks, if required - if (du_high.metrics.rlc.json_enabled || gnb_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(i); - auto rlc_source = std::make_unique(source_name); - - if (du_high.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 (gnb_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); - du_hi_cfg.e2_du_metric_iface = &(e2_metric_connectors.get_e2_du_metrics_interface(i)); - 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)); - du_hi_cfg.rlc_metrics_notif = metrics_hub.get_rlc_metrics_source(source_name); - } - - // Configure test mode - if (du_high.test_mode_cfg.test_ue.rnti != rnti_t::INVALID_RNTI) { - du_hi_cfg.test_cfg.test_ue = - srs_du::du_test_config::test_ue_config{du_high.test_mode_cfg.test_ue.rnti, - du_high.test_mode_cfg.test_ue.nof_ues, - du_high.test_mode_cfg.test_ue.auto_ack_indication_delay, - du_high.test_mode_cfg.test_ue.pdsch_active, - du_high.test_mode_cfg.test_ue.pusch_active, - du_high.test_mode_cfg.test_ue.cqi, - du_high.test_mode_cfg.test_ue.ri, - du_high.test_mode_cfg.test_ue.pmi, - du_high.test_mode_cfg.test_ue.i_1_1, - du_high.test_mode_cfg.test_ue.i_1_3, - du_high.test_mode_cfg.test_ue.i_2}; - } - - // FAPI configuration. - du_cfg.du_high_cfg.fapi.log_level = fapi_cfg.fapi_level; - if (fapi_cfg.l2_nof_slots_ahead != 0) { - du_cfg.du_high_cfg.fapi.executor.emplace(workers.fapi_exec[i]); - } else { - report_error_if_not(workers.fapi_exec[i] == nullptr, - "FAPI buffered worker created for a cell with no MAC delay configured"); - } - - // As the temporal configuration contains only once cell, pick the data from that cell. - du_cfg.du_high_cfg.fapi.l2_nof_slots_ahead = fapi_cfg.l2_nof_slots_ahead; - - du_insts.push_back(make_du_wrapper(du_cfg)); - report_error_if_not(du_insts.back(), "Invalid Distributed Unit"); - } - - return du_insts; -} - static std::unique_ptr create_dummy_radio_unit(const ru_dummy_unit_config& ru_cfg, unsigned max_processing_delay_slots, unsigned nof_prach_ports, @@ -282,18 +59,18 @@ static std::unique_ptr create_dummy_radio_unit(const ru_dummy_unit_c } static std::unique_ptr -create_radio_unit(const variant& ru_cfg, - worker_manager& workers, - span du_cells, - ru_uplink_plane_rx_symbol_notifier& symbol_notifier, - ru_timing_notifier& timing_notifier, - ru_error_notifier& error_notifier, - unsigned max_processing_delay, - unsigned prach_nof_ports) +create_radio_unit(const std::variant& ru_cfg, + worker_manager& workers, + span du_cells, + ru_uplink_plane_rx_symbol_notifier& symbol_notifier, + ru_timing_notifier& timing_notifier, + ru_error_notifier& error_notifier, + unsigned max_processing_delay, + unsigned prach_nof_ports) { - if (variant_holds_alternative(ru_cfg)) { + if (std::holds_alternative(ru_cfg)) { ru_ofh_factory_config config; - config.ru_cfg = variant_get(ru_cfg).config; + config.ru_cfg = std::get(ru_cfg).config; config.max_processing_delay_slots = max_processing_delay; config.du_cells = du_cells; @@ -306,10 +83,10 @@ create_radio_unit(const variant(ru_cfg)) { + if (std::holds_alternative(ru_cfg)) { ru_sdr_factory_config config; config.du_cells = du_cells; - config.ru_cfg = variant_get(ru_cfg); + config.ru_cfg = std::get(ru_cfg); config.max_processing_delay_slots = max_processing_delay; ru_sdr_factory_dependencies dependencies; @@ -321,7 +98,7 @@ create_radio_unit(const variant(ru_cfg), + return create_dummy_radio_unit(std::get(ru_cfg), max_processing_delay, prach_nof_ports, workers, @@ -331,28 +108,28 @@ create_radio_unit(const variant srsran::create_du_wrapper(const dynamic_du_unit_config& dyn_du_cfg, - worker_manager& workers, - srs_du::f1c_connection_client& f1c_client_handler, - srs_du::f1u_du_gateway& f1u_gw, - timer_manager& timer_mng, - mac_pcap& mac_p, - rlc_pcap& rlc_p, - console_helper& console_helper, - e2_connection_client& e2_client_handler, - e2_metric_connector_manager& e2_metric_connectors, - rlc_metrics_notifier& rlc_json_metrics, - metrics_hub& metrics_hub, - span sched_metrics_subscribers) +std::unique_ptr srsran::create_du(const dynamic_du_unit_config& dyn_du_cfg, + worker_manager& workers, + srs_du::f1c_connection_client& f1c_client_handler, + srs_du::f1u_du_gateway& f1u_gw, + timer_manager& timer_mng, + mac_pcap& mac_p, + rlc_pcap& rlc_p, + console_helper& console_helper, + 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) { const du_high_unit_config& du_hi = dyn_du_cfg.du_high_cfg.config; const du_low_unit_config& du_lo = dyn_du_cfg.du_low_cfg; const fapi_unit_config& fapi_cfg = dyn_du_cfg.fapi_cfg; - auto du_cells = generate_du_cell_config(du_hi); + // Configure the application unit metrics for the DU high. + configure_du_high_metrics(du_hi, console_helper, metrics_logger, e2_metric_connectors, metrics_hub); - // DU cell config - console_helper.set_cells(du_cells); + auto du_cells = generate_du_cell_config(du_hi); std::vector> du_insts; auto du_impl = std::make_unique(du_cells.size()); @@ -381,6 +158,21 @@ std::unique_ptr srsran::create_du_wrapper(const dynamic_du_unit_config& workers, i); + fill_du_high_wrapper_config(du_cfg.du_high_cfg, + tmp_cfg, + i, + workers.get_du_high_executor_mapper(i), + f1c_client_handler, + f1u_gw, + timer_mng, + mac_p, + rlc_p, + metrics_logger, + e2_client_handler, + e2_metric_connectors, + rlc_json_metrics, + metrics_hub); + // FAPI configuration. du_cfg.du_high_cfg.fapi.log_level = fapi_cfg.fapi_level; if (fapi_cfg.l2_nof_slots_ahead != 0) { @@ -396,14 +188,20 @@ std::unique_ptr srsran::create_du_wrapper(const dynamic_du_unit_config& report_error_if_not(du_insts.back(), "Invalid Distributed Unit"); } - du_impl->add_ru(create_radio_unit(dyn_du_cfg.ru_cfg, - workers, - du_cells, - du_impl->get_upper_ru_ul_adapter(), - du_impl->get_upper_ru_timing_adapter(), - du_impl->get_upper_ru_error_adapter(), - du_lo.expert_phy_cfg.max_processing_delay_slots, - du_hi.cells_cfg.front().cell.prach_cfg.ports.size())); + std::unique_ptr ru = create_radio_unit(dyn_du_cfg.ru_cfg, + workers, + du_cells, + du_impl->get_upper_ru_ul_adapter(), + du_impl->get_upper_ru_timing_adapter(), + du_impl->get_upper_ru_error_adapter(), + du_lo.expert_phy_cfg.max_processing_delay_slots, + du_hi.cells_cfg.front().cell.prach_cfg.ports.size()); + + srsran_assert(ru, "Invalid Radio Unit"); + + console_helper.set_ru_controller(ru->get_controller()); + + du_impl->add_ru(std::move(ru)); du_impl->add_dus(std::move(du_insts)); diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.h b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.h index e93abc7d79..5192872dd0 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_factory.h +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_factory.h @@ -25,13 +25,9 @@ #include "apps/gnb/gnb_appconfig.h" #include "apps/services/metrics_hub.h" #include "apps/services/worker_manager.h" -#include "srsran/du/du_cell_config.h" -#include "srsran/du/du_wrapper.h" +#include "srsran/du/du.h" #include "srsran/pcap/rlc_pcap.h" -// :TODO: remove this when inherits from DU. -#include "dynamic_du_impl.h" - namespace srsran { class e2_connection_client; @@ -49,37 +45,18 @@ class f1c_connection_client; class f1u_du_gateway; } // namespace srs_du -/// \brief Instantiates a list of Distributed Unit (DU) given a gNB application configuration. -std::vector> make_gnb_dus(const gnb_appconfig& gnb_cfg, - const dynamic_du_unit_config& dyn_du_cfg, - span du_cells, - worker_manager& workers, - upper_phy_rg_gateway& rg_gateway, - upper_phy_rx_symbol_request_notifier& rx_symbol_request_notifier, - srs_du::f1c_connection_client& f1c_client_handler, - srs_du::f1u_du_gateway& f1u_gw, - timer_manager& timer_mng, - mac_pcap& mac_p, - rlc_pcap& rlc_p, - console_helper& console_helper, - 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); - -std::unique_ptr create_du_wrapper(const dynamic_du_unit_config& dyn_du_cfg, - worker_manager& workers, - srs_du::f1c_connection_client& f1c_client_handler, - srs_du::f1u_du_gateway& f1u_gw, - timer_manager& timer_mng, - mac_pcap& mac_p, - rlc_pcap& rlc_p, - console_helper& console_helper, - e2_connection_client& e2_client_handler, - e2_metric_connector_manager& e2_metric_connectors, - rlc_metrics_notifier& rlc_json_metrics, - metrics_hub& metrics_hub, - span sched_metrics_subscribers); +std::unique_ptr create_du(const dynamic_du_unit_config& dyn_du_cfg, + worker_manager& workers, + srs_du::f1c_connection_client& f1c_client_handler, + srs_du::f1u_du_gateway& f1u_gw, + timer_manager& timer_mng, + mac_pcap& mac_p, + rlc_pcap& rlc_p, + console_helper& console_helper, + 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_impl.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_impl.cpp index dde8caa968..8b635e9c38 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_impl.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_impl.cpp @@ -68,15 +68,13 @@ void dynamic_du_impl::add_dus(std::vector> active_du du_list = std::move(active_du); srsran_assert(!du_list.empty(), "Cannot set an empty DU list"); - for (unsigned sector_id = 0, sector_end = du_list.size(); sector_id != sector_end; ++sector_id) { - srsran_assert(du_list[sector_id], "Invalid DU"); - - auto& du_obj = du_list[sector_id]; - auto& upper = du_obj->get_du_low_wrapper().get_du_low().get_upper_phy(sector_id); - - // Make connections between DU and RU. - ru_ul_adapt.map_handler(sector_id, upper.get_rx_symbol_handler()); - ru_timing_adapt.map_handler(sector_id, upper.get_timing_handler()); - ru_error_adapt.map_handler(sector_id, upper.get_error_handler()); + for (auto& du_obj : du_list) { + span upper_ptrs = du_obj->get_du_low_wrapper().get_du_low().get_all_upper_phys(); + for (auto* upper : upper_ptrs) { + // Make connections between DU and RU. + ru_ul_adapt.map_handler(upper->get_sector_id(), upper->get_rx_symbol_handler()); + ru_timing_adapt.map_handler(upper->get_sector_id(), upper->get_timing_handler()); + ru_error_adapt.map_handler(upper->get_sector_id(), upper->get_error_handler()); + } } } \ No newline at end of file diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp index f9f2bbedfd..ea4025f677 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_cli11_schema.cpp @@ -141,8 +141,8 @@ void srsran::autoderive_dynamic_du_parameters_after_parsing(CLI::App& app, dynam // Set the parsed RU. manage_ru(app, parsed_cfg); - if (variant_holds_alternative(parsed_cfg.ru_cfg)) { - auto& dummy = variant_get(parsed_cfg.ru_cfg); + if (std::holds_alternative(parsed_cfg.ru_cfg)) { + auto& dummy = std::get(parsed_cfg.ru_cfg); if (dummy.cell_affinities.size() < parsed_cfg.du_high_cfg.config.cells_cfg.size()) { dummy.cell_affinities.resize(parsed_cfg.du_high_cfg.config.cells_cfg.size()); } @@ -152,8 +152,8 @@ void srsran::autoderive_dynamic_du_parameters_after_parsing(CLI::App& app, dynam const auto& cell = parsed_cfg.du_high_cfg.config.cells_cfg.front().cell; nr_band band = cell.band ? cell.band.value() : band_helper::get_band_from_dl_arfcn(cell.dl_arfcn); bool is_zmq_rf_driver = false; - if (variant_holds_alternative(parsed_cfg.ru_cfg)) { - is_zmq_rf_driver = variant_get(parsed_cfg.ru_cfg).device_driver == "zmq"; + if (std::holds_alternative(parsed_cfg.ru_cfg)) { + is_zmq_rf_driver = std::get(parsed_cfg.ru_cfg).device_driver == "zmq"; } autoderive_du_low_parameters_after_parsing(app, parsed_cfg.du_low_cfg, diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config.h b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config.h index bcc3ee264a..0a6de9be8e 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config.h +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config.h @@ -27,7 +27,7 @@ #include "apps/units/flexible_du/fapi/fapi_config.h" #include "apps/units/flexible_du/split_7_2/ru_ofh_config.h" #include "apps/units/flexible_du/split_8/ru_sdr_config.h" -#include "srsran/adt/variant.h" +#include namespace srsran { @@ -59,7 +59,7 @@ struct dynamic_du_unit_config { /// FAPI configuration. fapi_unit_config fapi_cfg; /// Radio Unit configuration. - variant ru_cfg = {ru_sdr_unit_config{}}; + std::variant ru_cfg = {ru_sdr_unit_config{}}; }; } // namespace srsran diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.cpp b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.cpp index 6e6f952d0c..41d30832bb 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.cpp +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_config_validator.cpp @@ -139,18 +139,18 @@ bool srsran::validate_dynamic_du_unit_config(const dynamic_du_unit_config& co return false; } - if (variant_holds_alternative(config.ru_cfg)) { + if (std::holds_alternative(config.ru_cfg)) { auto ru_ofh_dependencies = get_ru_ofh_validation_dependencies(config.du_high_cfg.config); return validate_ru_ofh_config( - variant_get(config.ru_cfg).config, ru_ofh_dependencies, available_cpus); + std::get(config.ru_cfg).config, ru_ofh_dependencies, available_cpus); } - if (variant_holds_alternative(config.ru_cfg)) { + if (std::holds_alternative(config.ru_cfg)) { auto ru_sdr_dependencies = get_ru_sdr_validation_dependencies(config.du_high_cfg.config); - return validate_ru_sdr_config(variant_get(config.ru_cfg), ru_sdr_dependencies, available_cpus); + return validate_ru_sdr_config(std::get(config.ru_cfg), ru_sdr_dependencies, available_cpus); } - if (!validate_expert_execution_unit_config(variant_get(config.ru_cfg), available_cpus)) { + if (!validate_expert_execution_unit_config(std::get(config.ru_cfg), available_cpus)) { return false; } diff --git a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h index 0a4991a7be..bb0fd28a42 100644 --- a/apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h +++ b/apps/units/flexible_du/split_dynamic/dynamic_du_unit_logger_registrator.h @@ -38,12 +38,12 @@ inline void register_dynamic_du_loggers(const dynamic_du_unit_config& config) register_du_low_loggers(config.du_low_cfg.loggers); register_fapi_loggers(config.fapi_cfg); - if (variant_holds_alternative(config.ru_cfg)) { - register_ru_sdr_logs(variant_get(config.ru_cfg).loggers); + if (std::holds_alternative(config.ru_cfg)) { + register_ru_sdr_logs(std::get(config.ru_cfg).loggers); } - if (variant_holds_alternative(config.ru_cfg)) { - register_ru_ofh_loggers(variant_get(config.ru_cfg).config.loggers); + if (std::holds_alternative(config.ru_cfg)) { + register_ru_ofh_loggers(std::get(config.ru_cfg).config.loggers); } } diff --git a/external/any/any.hpp b/external/any/any.hpp deleted file mode 100644 index f50f621c6a..0000000000 --- a/external/any/any.hpp +++ /dev/null @@ -1,506 +0,0 @@ -// -// Implementation of N4562 std::experimental::any (merged into C++17) for C++11 compilers. -// -// See also: -// + http://en.cppreference.com/w/cpp/any -// + http://en.cppreference.com/w/cpp/experimental/any -// + http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/n4562.html#any -// + https://cplusplus.github.io/LWG/lwg-active.html#2509 -// -// -// Copyright (c) 2016 Denilson das Mercês Amorim -// -// Distributed under the Boost Software License, Version 1.0. (See accompanying -// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) -// -#ifndef LINB_ANY_HPP -#define LINB_ANY_HPP -#pragma once -#include -#include -#include -#include -#include - -#if defined(PARTICLE) -#if !defined(__cpp_exceptions) && !defined(ANY_IMPL_NO_EXCEPTIONS) && !defined(ANY_IMPL_EXCEPTIONS) -# define ANY_IMPL_NO_EXCEPTIONS -# endif -#else -// you can opt-out of exceptions by definining ANY_IMPL_NO_EXCEPTIONS, -// but you must ensure not to cast badly when passing an `any' object to any_cast(any) -#endif - -#if defined(PARTICLE) -#if !defined(__cpp_rtti) && !defined(ANY_IMPL_NO_RTTI) && !defined(ANY_IMPL_RTTI) -# define ANY_IMPL_NO_RTTI -# endif -#else -// you can opt-out of RTTI by defining ANY_IMPL_NO_RTTI, -// in order to disable functions working with the typeid of a type -#endif - - -namespace linb -{ - -class bad_any_cast : public std::bad_cast -{ -public: - const char* what() const noexcept override - { - return "bad any cast"; - } -}; - -class any final -{ -public: - /// Constructs an object of type any with an empty state. - any() : - vtable(nullptr) - { - } - - /// Constructs an object of type any with an equivalent state as other. - any(const any& rhs) : - vtable(rhs.vtable) - { - if(!rhs.empty()) - { - rhs.vtable->copy(rhs.storage, this->storage); - } - } - - /// Constructs an object of type any with a state equivalent to the original state of other. - /// rhs is left in a valid but otherwise unspecified state. - any(any&& rhs) noexcept : - vtable(rhs.vtable) - { - if(!rhs.empty()) - { - rhs.vtable->move(rhs.storage, this->storage); - rhs.vtable = nullptr; - } - } - - /// Same effect as this->clear(). - ~any() - { - this->clear(); - } - - /// Constructs an object of type any that contains an object of type T direct-initialized with std::forward(value). - /// - /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. - /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. - template::type, any>::value>::type> - any(ValueType&& value) - { - static_assert(std::is_copy_constructible::type>::value, - "T shall satisfy the CopyConstructible requirements."); - this->construct(std::forward(value)); - } - - /// Has the same effect as any(rhs).swap(*this). No effects if an exception is thrown. - any& operator=(const any& rhs) - { - any(rhs).swap(*this); - return *this; - } - - /// Has the same effect as any(std::move(rhs)).swap(*this). - /// - /// The state of *this is equivalent to the original state of rhs and rhs is left in a valid - /// but otherwise unspecified state. - any& operator=(any&& rhs) noexcept - { - any(std::move(rhs)).swap(*this); - return *this; - } - - /// Has the same effect as any(std::forward(value)).swap(*this). No effect if a exception is thrown. - /// - /// T shall satisfy the CopyConstructible requirements, otherwise the program is ill-formed. - /// This is because an `any` may be copy constructed into another `any` at any time, so a copy should always be allowed. - template::type, any>::value>::type> - any& operator=(ValueType&& value) - { - static_assert(std::is_copy_constructible::type>::value, - "T shall satisfy the CopyConstructible requirements."); - any(std::forward(value)).swap(*this); - return *this; - } - - /// If not empty, destroys the contained object. - void clear() noexcept - { - if(!empty()) - { - this->vtable->destroy(storage); - this->vtable = nullptr; - } - } - - /// Returns true if *this has no contained object, otherwise false. - bool empty() const noexcept - { - return this->vtable == nullptr; - } - -#ifndef ANY_IMPL_NO_RTTI - /// If *this has a contained object of type T, typeid(T); otherwise typeid(void). - const std::type_info& type() const noexcept - { - return empty()? typeid(void) : this->vtable->type(); - } -#endif - - /// Exchange the states of *this and rhs. - void swap(any& rhs) noexcept - { - if(this->vtable != rhs.vtable) - { - any tmp(std::move(rhs)); - - // move from *this to rhs. - rhs.vtable = this->vtable; - if(this->vtable != nullptr) - { - this->vtable->move(this->storage, rhs.storage); - //this->vtable = nullptr; -- unneeded, see below - } - - // move from tmp (previously rhs) to *this. - this->vtable = tmp.vtable; - if(tmp.vtable != nullptr) - { - tmp.vtable->move(tmp.storage, this->storage); - tmp.vtable = nullptr; - } - } - else // same types - { - if(this->vtable != nullptr) - this->vtable->swap(this->storage, rhs.storage); - } - } - -private: // Storage and Virtual Method Table - - union storage_union - { - using stack_storage_t = typename std::aligned_storage<2 * sizeof(void*), std::alignment_of::value>::type; - - void* dynamic; - stack_storage_t stack; // 2 words for e.g. shared_ptr - }; - - /// Base VTable specification. - struct vtable_type - { - // Note: The caller is responssible for doing .vtable = nullptr after destructful operations - // such as destroy() and/or move(). - -#ifndef ANY_IMPL_NO_RTTI - /// The type of the object this vtable is for. - const std::type_info& (*type)() noexcept; -#endif - - /// Destroys the object in the union. - /// The state of the union after this call is unspecified, caller must ensure not to use src anymore. - void(*destroy)(storage_union&) noexcept; - - /// Copies the **inner** content of the src union into the yet unitialized dest union. - /// As such, both inner objects will have the same state, but on separate memory locations. - void(*copy)(const storage_union& src, storage_union& dest); - - /// Moves the storage from src to the yet unitialized dest union. - /// The state of src after this call is unspecified, caller must ensure not to use src anymore. - void(*move)(storage_union& src, storage_union& dest) noexcept; - - /// Exchanges the storage between lhs and rhs. - void(*swap)(storage_union& lhs, storage_union& rhs) noexcept; - }; - - /// VTable for dynamically allocated storage. - template - struct vtable_dynamic - { -#ifndef ANY_IMPL_NO_RTTI - static const std::type_info& type() noexcept - { - return typeid(T); - } -#endif - - static void destroy(storage_union& storage) noexcept - { - //assert(reinterpret_cast(storage.dynamic)); - delete reinterpret_cast(storage.dynamic); - } - - static void copy(const storage_union& src, storage_union& dest) - { - dest.dynamic = new T(*reinterpret_cast(src.dynamic)); - } - - static void move(storage_union& src, storage_union& dest) noexcept - { - dest.dynamic = src.dynamic; - src.dynamic = nullptr; - } - - static void swap(storage_union& lhs, storage_union& rhs) noexcept - { - // just exchage the storage pointers. - std::swap(lhs.dynamic, rhs.dynamic); - } - }; - - /// VTable for stack allocated storage. - template - struct vtable_stack - { -#ifndef ANY_IMPL_NO_RTTI - static const std::type_info& type() noexcept - { - return typeid(T); - } -#endif - - static void destroy(storage_union& storage) noexcept - { - reinterpret_cast(&storage.stack)->~T(); - } - - static void copy(const storage_union& src, storage_union& dest) - { - new (&dest.stack) T(reinterpret_cast(src.stack)); - } - - static void move(storage_union& src, storage_union& dest) noexcept - { - // one of the conditions for using vtable_stack is a nothrow move constructor, - // so this move constructor will never throw a exception. - new (&dest.stack) T(std::move(reinterpret_cast(src.stack))); - destroy(src); - } - - static void swap(storage_union& lhs, storage_union& rhs) noexcept - { - storage_union tmp_storage; - move(rhs, tmp_storage); - move(lhs, rhs); - move(tmp_storage, lhs); - } - }; - - /// Whether the type T must be dynamically allocated or can be stored on the stack. - template - struct requires_allocation : - std::integral_constant::value // N4562 §6.3/3 [any.class] - && sizeof(T) <= sizeof(storage_union::stack) - && std::alignment_of::value <= std::alignment_of::value)> - {}; - - /// Returns the pointer to the vtable of the type T. - template - static vtable_type* vtable_for_type() - { - using VTableType = typename std::conditional::value, vtable_dynamic, vtable_stack>::type; - static vtable_type table = { -#ifndef ANY_IMPL_NO_RTTI - VTableType::type, -#endif - VTableType::destroy, - VTableType::copy, VTableType::move, - VTableType::swap, - }; - return &table; - } - -protected: - template - friend const T* any_cast(const any* operand) noexcept; - template - friend T* any_cast(any* operand) noexcept; - -#ifndef ANY_IMPL_NO_RTTI - /// Same effect as is_same(this->type(), t); - bool is_typed(const std::type_info& t) const - { - return is_same(this->type(), t); - } -#endif - -#ifndef ANY_IMPL_NO_RTTI - /// Checks if two type infos are the same. - /// - /// If ANY_IMPL_FAST_TYPE_INFO_COMPARE is defined, checks only the address of the - /// type infos, otherwise does an actual comparision. Checking addresses is - /// only a valid approach when there's no interaction with outside sources - /// (other shared libraries and such). - static bool is_same(const std::type_info& a, const std::type_info& b) - { -#ifdef ANY_IMPL_FAST_TYPE_INFO_COMPARE - return &a == &b; -#else - return a == b; -#endif - } -#endif - - /// Casts (with no type_info checks) the storage pointer as const T*. - template - const T* cast() const noexcept - { - return requires_allocation::type>::value? - reinterpret_cast(storage.dynamic) : - reinterpret_cast(&storage.stack); - } - - /// Casts (with no type_info checks) the storage pointer as T*. - template - T* cast() noexcept - { - return requires_allocation::type>::value? - reinterpret_cast(storage.dynamic) : - reinterpret_cast(&storage.stack); - } - -private: - storage_union storage; // on offset(0) so no padding for align - vtable_type* vtable; - - template - typename std::enable_if::value>::type - do_construct(ValueType&& value) - { - storage.dynamic = new T(std::forward(value)); - } - - template - typename std::enable_if::value>::type - do_construct(ValueType&& value) - { - new (&storage.stack) T(std::forward(value)); - } - - /// Chooses between stack and dynamic allocation for the type decay_t, - /// assigns the correct vtable, and constructs the object on our storage. - template - void construct(ValueType&& value) - { - using T = typename std::decay::type; - - this->vtable = vtable_for_type(); - - do_construct(std::forward(value)); - } -}; - - - -namespace detail -{ - template - inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::true_type) - { - return std::move(*p); - } - - template - inline ValueType any_cast_move_if_true(typename std::remove_reference::type* p, std::false_type) - { - return *p; - } -} - -/// Performs *any_cast>>(&operand), or throws bad_any_cast on failure. -template -inline ValueType any_cast(const any& operand) -{ - auto p = any_cast::type>::type>(&operand); -#ifndef ANY_IMPL_NO_EXCEPTIONS - if(p == nullptr) throw bad_any_cast(); -#endif - return *p; -} - -/// Performs *any_cast>(&operand), or throws bad_any_cast on failure. -template -inline ValueType any_cast(any& operand) -{ - auto p = any_cast::type>(&operand); -#ifndef ANY_IMPL_NO_EXCEPTIONS - if(p == nullptr) throw bad_any_cast(); -#endif - return *p; -} - -/// -/// If ValueType is MoveConstructible and isn't a lvalue reference, performs -/// std::move(*any_cast>(&operand)), otherwise -/// *any_cast>(&operand). Throws bad_any_cast on failure. -/// -template -inline ValueType any_cast(any&& operand) -{ - using can_move = std::integral_constant::value - && !std::is_lvalue_reference::value>; - - auto p = any_cast::type>(&operand); -#ifndef ANY_IMPL_NO_EXCEPTIONS - if(p == nullptr) throw bad_any_cast(); -#endif - return detail::any_cast_move_if_true(p, can_move()); -} - -/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object -/// contained by operand, otherwise nullptr. -template -inline const ValueType* any_cast(const any* operand) noexcept -{ - using T = typename std::decay::type; - -#ifndef ANY_IMPL_NO_RTTI - if (operand && operand->is_typed(typeid(T))) -#else - if (operand && operand->vtable == any::vtable_for_type()) -#endif - return operand->cast(); - else - return nullptr; -} - -/// If operand != nullptr && operand->type() == typeid(ValueType), a pointer to the object -/// contained by operand, otherwise nullptr. -template -inline ValueType* any_cast(any* operand) noexcept -{ - using T = typename std::decay::type; - -#ifndef ANY_IMPL_NO_RTTI - if (operand && operand->is_typed(typeid(T))) -#else - if (operand && operand->vtable == any::vtable_for_type()) -#endif - return operand->cast(); - else - return nullptr; -} - -} - -namespace std -{ - inline void swap(linb::any& lhs, linb::any& rhs) noexcept - { - lhs.swap(rhs); - } -} - -#endif \ No newline at end of file diff --git a/external/variant/variant.hpp b/external/variant/variant.hpp deleted file mode 100644 index f5636aa80e..0000000000 --- a/external/variant/variant.hpp +++ /dev/null @@ -1,2810 +0,0 @@ -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#pragma once - -/* - variant synopsis - -namespace std { - - // 20.7.2, class template variant - template - class variant { - public: - - // 20.7.2.1, constructors - constexpr variant() noexcept(see below); - variant(const variant&); - variant(variant&&) noexcept(see below); - - template constexpr variant(T&&) noexcept(see below); - - template - constexpr explicit variant(in_place_type_t, Args&&...); - - template - constexpr explicit variant( - in_place_type_t, initializer_list, Args&&...); - - template - constexpr explicit variant(in_place_index_t, Args&&...); - - template - constexpr explicit variant( - in_place_index_t, initializer_list, Args&&...); - - // 20.7.2.2, destructor - ~variant(); - - // 20.7.2.3, assignment - variant& operator=(const variant&); - variant& operator=(variant&&) noexcept(see below); - - template variant& operator=(T&&) noexcept(see below); - - // 20.7.2.4, modifiers - template - T& emplace(Args&&...); - - template - T& emplace(initializer_list, Args&&...); - - template - variant_alternative& emplace(Args&&...); - - template - variant_alternative& emplace(initializer_list, Args&&...); - - // 20.7.2.5, value status - constexpr bool valueless_by_exception() const noexcept; - constexpr size_t index() const noexcept; - - // 20.7.2.6, swap - void swap(variant&) noexcept(see below); - }; - - // 20.7.3, variant helper classes - template struct variant_size; // undefined - - template - constexpr size_t variant_size_v = variant_size::value; - - template struct variant_size; - template struct variant_size; - template struct variant_size; - - template - struct variant_size>; - - template struct variant_alternative; // undefined - - template - using variant_alternative_t = typename variant_alternative::type; - - template struct variant_alternative; - template struct variant_alternative; - template struct variant_alternative; - - template - struct variant_alternative>; - - constexpr size_t variant_npos = -1; - - // 20.7.4, value access - template - constexpr bool holds_alternative(const variant&) noexcept; - - template - constexpr variant_alternative_t>& - get(variant&); - - template - constexpr variant_alternative_t>&& - get(variant&&); - - template - constexpr variant_alternative_t> const& - get(const variant&); - - template - constexpr variant_alternative_t> const&& - get(const variant&&); - - template - constexpr T& get(variant&); - - template - constexpr T&& get(variant&&); - - template - constexpr const T& get(const variant&); - - template - constexpr const T&& get(const variant&&); - - template - constexpr add_pointer_t>> - get_if(variant*) noexcept; - - template - constexpr add_pointer_t>> - get_if(const variant*) noexcept; - - template - constexpr add_pointer_t - get_if(variant*) noexcept; - - template - constexpr add_pointer_t - get_if(const variant*) noexcept; - - // 20.7.5, relational operators - template - constexpr bool operator==(const variant&, const variant&); - - template - constexpr bool operator!=(const variant&, const variant&); - - template - constexpr bool operator<(const variant&, const variant&); - - template - constexpr bool operator>(const variant&, const variant&); - - template - constexpr bool operator<=(const variant&, const variant&); - - template - constexpr bool operator>=(const variant&, const variant&); - - // 20.7.6, visitation - template - constexpr see below visit(Visitor&&, Variants&&...); - - // 20.7.7, class monostate - struct monostate; - - // 20.7.8, monostate relational operators - constexpr bool operator<(monostate, monostate) noexcept; - constexpr bool operator>(monostate, monostate) noexcept; - constexpr bool operator<=(monostate, monostate) noexcept; - constexpr bool operator>=(monostate, monostate) noexcept; - constexpr bool operator==(monostate, monostate) noexcept; - constexpr bool operator!=(monostate, monostate) noexcept; - - // 20.7.9, specialized algorithms - template - void swap(variant&, variant&) noexcept(see below); - - // 20.7.10, class bad_variant_access - class bad_variant_access; - - // 20.7.11, hash support - template struct hash; - template struct hash>; - template <> struct hash; - -} // namespace std - -*/ - -#include -#include -#include -#include -#include -#include -#include - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_CONFIG_HPP -#define MPARK_CONFIG_HPP - -// MSVC 2015 Update 3. -#if __cplusplus < 201103L && (!defined(_MSC_VER) || _MSC_FULL_VER < 190024210) -#error "MPark.Variant requires C++11 support." -#endif - -#ifndef __has_attribute -#define __has_attribute(x) 0 -#endif - -#ifndef __has_builtin -#define __has_builtin(x) 0 -#endif - -#ifndef __has_include -#define __has_include(x) 0 -#endif - -#ifndef __has_feature -#define __has_feature(x) 0 -#endif - -#if __has_attribute(always_inline) || defined(__GNUC__) -#define MPARK_ALWAYS_INLINE __attribute__((__always_inline__)) inline -#elif defined(_MSC_VER) -#define MPARK_ALWAYS_INLINE __forceinline -#else -#define MPARK_ALWAYS_INLINE inline -#endif - -#if __has_builtin(__builtin_addressof) || \ - (defined(__GNUC__) && __GNUC__ >= 7) || defined(_MSC_VER) -#define MPARK_BUILTIN_ADDRESSOF -#endif - -#if __has_builtin(__builtin_unreachable) || defined(__GNUC__) -#define MPARK_BUILTIN_UNREACHABLE __builtin_unreachable() -#elif defined(_MSC_VER) -#define MPARK_BUILTIN_UNREACHABLE __assume(false) -#else -#define MPARK_BUILTIN_UNREACHABLE -#endif - -#if __has_builtin(__type_pack_element) -#define MPARK_TYPE_PACK_ELEMENT -#endif - -#if defined(__cpp_constexpr) && __cpp_constexpr >= 200704 && \ - !(defined(__GNUC__) && __GNUC__ == 4 && __GNUC_MINOR__ == 9) -#define MPARK_CPP11_CONSTEXPR -#endif - -#if defined(__cpp_constexpr) && __cpp_constexpr >= 201304 -#define MPARK_CPP14_CONSTEXPR -#endif - -#if __has_feature(cxx_exceptions) || defined(__cpp_exceptions) || \ - (defined(_MSC_VER) && defined(_CPPUNWIND)) -#define MPARK_EXCEPTIONS -#endif - -#if defined(__cpp_generic_lambdas) || defined(_MSC_VER) -#define MPARK_GENERIC_LAMBDAS -#endif - -#if defined(__cpp_lib_integer_sequence) -#define MPARK_INTEGER_SEQUENCE -#endif - -#if defined(__cpp_return_type_deduction) || defined(_MSC_VER) -#define MPARK_RETURN_TYPE_DEDUCTION -#endif - -#if defined(__cpp_lib_transparent_operators) || defined(_MSC_VER) -#define MPARK_TRANSPARENT_OPERATORS -#endif - -#if defined(__cpp_variable_templates) || defined(_MSC_VER) -#define MPARK_VARIABLE_TEMPLATES -#endif - -#if !defined(__GLIBCXX__) || __has_include() // >= libstdc++-5 -#define MPARK_TRIVIALITY_TYPE_TRAITS -#define MPARK_INCOMPLETE_TYPE_TRAITS -#endif - -#endif // MPARK_CONFIG_HPP - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_IN_PLACE_HPP -#define MPARK_IN_PLACE_HPP - -#include - - -namespace mpark { - - struct in_place_t { explicit in_place_t() = default; }; - - template - struct in_place_index_t { explicit in_place_index_t() = default; }; - - template - struct in_place_type_t { explicit in_place_type_t() = default; }; - -#ifdef MPARK_VARIABLE_TEMPLATES - constexpr in_place_t in_place{}; - - template constexpr in_place_index_t in_place_index{}; - - template constexpr in_place_type_t in_place_type{}; -#endif - -} // namespace mpark - -#endif // MPARK_IN_PLACE_HPP - -// MPark.Variant -// -// Copyright Michael Park, 2015-2017 -// -// Distributed under the Boost Software License, Version 1.0. -// (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) - -#ifndef MPARK_LIB_HPP -#define MPARK_LIB_HPP - -#include -#include -#include -#include - - -#define MPARK_RETURN(...) \ - noexcept(noexcept(__VA_ARGS__)) -> decltype(__VA_ARGS__) { return __VA_ARGS__; } - -namespace mpark { - namespace lib { - template - struct identity { using type = T; }; - - inline namespace cpp14 { - template - struct array { - constexpr const T &operator[](std::size_t index) const { - return data[index]; - } - - T data[N == 0 ? 1 : N]; - }; - - template - using add_pointer_t = typename std::add_pointer::type; - - template - using common_type_t = typename std::common_type::type; - - template - using decay_t = typename std::decay::type; - - template - using enable_if_t = typename std::enable_if::type; - - template - using remove_const_t = typename std::remove_const::type; - - template - using remove_reference_t = typename std::remove_reference::type; - - template - inline constexpr T &&forward(remove_reference_t &t) noexcept { - return static_cast(t); - } - - template - inline constexpr T &&forward(remove_reference_t &&t) noexcept { - static_assert(!std::is_lvalue_reference::value, - "can not forward an rvalue as an lvalue"); - return static_cast(t); - } - - template - inline constexpr remove_reference_t &&move(T &&t) noexcept { - return static_cast &&>(t); - } - -#ifdef MPARK_INTEGER_SEQUENCE - using std::integer_sequence; - using std::index_sequence; - using std::make_index_sequence; - using std::index_sequence_for; -#else - template - struct integer_sequence { - using value_type = T; - static constexpr std::size_t size() noexcept { return sizeof...(Is); } - }; - - template - using index_sequence = integer_sequence; - - template - struct make_index_sequence_concat; - - template - struct make_index_sequence_concat, - index_sequence> - : identity> {}; - - template - struct make_index_sequence_impl; - - template - using make_index_sequence = typename make_index_sequence_impl::type; - - template - struct make_index_sequence_impl - : make_index_sequence_concat, - make_index_sequence> {}; - - template <> - struct make_index_sequence_impl<0> : identity> {}; - - template <> - struct make_index_sequence_impl<1> : identity> {}; - - template - using index_sequence_for = make_index_sequence; -#endif - - // -#ifdef MPARK_TRANSPARENT_OPERATORS - using equal_to = std::equal_to<>; -#else - struct equal_to { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) == lib::forward(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using not_equal_to = std::not_equal_to<>; -#else - struct not_equal_to { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) != lib::forward(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using less = std::less<>; -#else - struct less { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) < lib::forward(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using greater = std::greater<>; -#else - struct greater { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) > lib::forward(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using less_equal = std::less_equal<>; -#else - struct less_equal { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) <= lib::forward(rhs)) - }; -#endif - -#ifdef MPARK_TRANSPARENT_OPERATORS - using greater_equal = std::greater_equal<>; -#else - struct greater_equal { - template - inline constexpr auto operator()(Lhs &&lhs, Rhs &&rhs) const - MPARK_RETURN(lib::forward(lhs) >= lib::forward(rhs)) - }; -#endif - } // namespace cpp14 - - inline namespace cpp17 { - - // - template - using bool_constant = std::integral_constant; - - template - struct voider : identity {}; - - template - using void_t = typename voider::type; - - namespace detail { - namespace swappable { - - using std::swap; - - template - struct is_swappable { - private: - template (), - std::declval()))> - inline static std::true_type test(int); - - template - inline static std::false_type test(...); - - public: - static constexpr bool value = decltype(test(0))::value; - }; - - template - struct is_nothrow_swappable { - static constexpr bool value = - noexcept(swap(std::declval(), std::declval())); - }; - - template - struct is_nothrow_swappable : std::false_type {}; - - } // namespace swappable - } // namespace detail - - using detail::swappable::is_swappable; - - template - using is_nothrow_swappable = - detail::swappable::is_nothrow_swappable::value, T>; - - // - namespace detail { - - template - struct is_reference_wrapper : std::false_type {}; - - template - struct is_reference_wrapper> - : std::true_type {}; - - template - struct Invoke; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args) - MPARK_RETURN((lib::forward(arg).*pmf)(lib::forward(args)...)) - }; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args) - MPARK_RETURN((lib::forward(arg).get().*pmf)(lib::forward(args)...)) - }; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmf, Arg &&arg, Args &&... args) - MPARK_RETURN(((*lib::forward(arg)).*pmf)(lib::forward(args)...)) - }; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmo, Arg &&arg) - MPARK_RETURN(lib::forward(arg).*pmo) - }; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmo, Arg &&arg) - MPARK_RETURN(lib::forward(arg).get().*pmo) - }; - - template <> - struct Invoke { - template - inline static constexpr auto invoke(R T::*pmo, Arg &&arg) - MPARK_RETURN((*lib::forward(arg)).*pmo) - }; - - template - inline constexpr auto invoke(R T::*f, Arg &&arg, Args &&... args) - MPARK_RETURN( - Invoke::value, - (std::is_base_of>::value - ? 0 - : is_reference_wrapper>::value - ? 1 - : 2)>::invoke(f, - lib::forward(arg), - lib::forward(args)...)) - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif - template - inline constexpr auto invoke(F &&f, Args &&... args) - MPARK_RETURN(lib::forward(f)(lib::forward(args)...)) -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } // namespace detail - - template - inline constexpr auto invoke(F &&f, Args &&... args) - MPARK_RETURN(detail::invoke(lib::forward(f), - lib::forward(args)...)) - - namespace detail { - - template - struct invoke_result {}; - - template - struct invoke_result(), std::declval()...))>, - F, - Args...> - : identity(), std::declval()...))> {}; - - } // namespace detail - - template - using invoke_result = detail::invoke_result; - - template - using invoke_result_t = typename invoke_result::type; - - namespace detail { - - template - struct is_invocable : std::false_type {}; - - template - struct is_invocable>, F, Args...> - : std::true_type {}; - - template - struct is_invocable_r : std::false_type {}; - - template - struct is_invocable_r>, - R, - F, - Args...> - : std::is_convertible, R> {}; - - } // namespace detail - - template - using is_invocable = detail::is_invocable; - - template - using is_invocable_r = detail::is_invocable_r; - - namespace detail { - - template - struct is_nothrow_invocable { - static constexpr bool value = - noexcept(lib::invoke(std::declval(), std::declval()...)); - }; - - template - struct is_nothrow_invocable : std::false_type {}; - - template - struct is_nothrow_invocable_r { - private: - inline static R impl() { - return lib::invoke(std::declval(), std::declval()...); - } - - public: - static constexpr bool value = noexcept(impl()); - }; - - template - struct is_nothrow_invocable_r : std::false_type {}; - - } // namespace detail - - template - using is_nothrow_invocable = detail:: - is_nothrow_invocable::value, F, Args...>; - - template - using is_nothrow_invocable_r = - detail::is_nothrow_invocable_r::value, - R, - F, - Args...>; - - // -#ifdef MPARK_BUILTIN_ADDRESSOF - template - inline constexpr T *addressof(T &arg) noexcept { - return __builtin_addressof(arg); - } -#else - namespace detail { - - namespace has_addressof_impl { - - struct fail; - - template - inline fail operator&(T &&); - - template - inline static constexpr bool impl() { - return (std::is_class::value || std::is_union::value) && - !std::is_same()), fail>::value; - } - - } // namespace has_addressof_impl - - template - using has_addressof = bool_constant()>; - - template - inline constexpr T *addressof(T &arg, std::true_type) noexcept { - return std::addressof(arg); - } - - template - inline constexpr T *addressof(T &arg, std::false_type) noexcept { - return &arg; - } - - } // namespace detail - - template - inline constexpr T *addressof(T &arg) noexcept { - return detail::addressof(arg, detail::has_addressof{}); - } -#endif - - template - inline constexpr T *addressof(const T &&) = delete; - - } // namespace cpp17 - - template - struct remove_all_extents : identity {}; - - template - struct remove_all_extents> : remove_all_extents {}; - - template - using remove_all_extents_t = typename remove_all_extents::type; - - template - using size_constant = std::integral_constant; - - template - struct indexed_type : size_constant { using type = T; }; - - template - using all = std::is_same, - integer_sequence>; - -#ifdef MPARK_TYPE_PACK_ELEMENT - template - using type_pack_element_t = __type_pack_element; -#else - template - struct type_pack_element_impl { - private: - template - struct set; - - template - struct set> : indexed_type... {}; - - template - inline static std::enable_if impl(indexed_type); - - inline static std::enable_if impl(...); - - public: - using type = decltype(impl(set>{})); - }; - - template - using type_pack_element = typename type_pack_element_impl::type; - - template - using type_pack_element_t = typename type_pack_element::type; -#endif - -#ifdef MPARK_TRIVIALITY_TYPE_TRAITS - using std::is_trivially_copy_constructible; - using std::is_trivially_move_constructible; - using std::is_trivially_copy_assignable; - using std::is_trivially_move_assignable; -#else - template - struct is_trivially_copy_constructible - : bool_constant< - std::is_copy_constructible::value && __has_trivial_copy(T)> {}; - - template - struct is_trivially_move_constructible : bool_constant<__is_trivial(T)> {}; - - template - struct is_trivially_copy_assignable - : bool_constant< - std::is_copy_assignable::value && __has_trivial_assign(T)> {}; - - template - struct is_trivially_move_assignable : bool_constant<__is_trivial(T)> {}; -#endif - - template - struct dependent_type : T {}; - - template - struct push_back; - - template - using push_back_t = typename push_back::type; - - template - struct push_back, J> { - using type = index_sequence; - }; - - } // namespace lib -} // namespace mpark - -#undef MPARK_RETURN - -#endif // MPARK_LIB_HPP - - -namespace mpark { - -#ifdef MPARK_RETURN_TYPE_DEDUCTION - -#define AUTO auto -#define AUTO_RETURN(...) { return __VA_ARGS__; } - -#define AUTO_REFREF auto && -#define AUTO_REFREF_RETURN(...) { return __VA_ARGS__; } - -#define DECLTYPE_AUTO decltype(auto) -#define DECLTYPE_AUTO_RETURN(...) { return __VA_ARGS__; } - -#else - -#define AUTO auto -#define AUTO_RETURN(...) \ - -> lib::decay_t { return __VA_ARGS__; } - -#define AUTO_REFREF auto -#define AUTO_REFREF_RETURN(...) \ - -> decltype((__VA_ARGS__)) { \ - static_assert(std::is_reference::value, ""); \ - return __VA_ARGS__; \ - } - -#define DECLTYPE_AUTO auto -#define DECLTYPE_AUTO_RETURN(...) \ - -> decltype(__VA_ARGS__) { return __VA_ARGS__; } - -#endif - - class bad_variant_access : public std::exception { - public: - virtual const char *what() const noexcept override { return "bad_variant_access"; } - }; - - [[noreturn]] inline void throw_bad_variant_access() { -#ifdef MPARK_EXCEPTIONS - throw bad_variant_access{}; -#else - std::terminate(); - MPARK_BUILTIN_UNREACHABLE; -#endif - } - - template - class variant; - - template - struct variant_size; - -#ifdef MPARK_VARIABLE_TEMPLATES - template - constexpr std::size_t variant_size_v = variant_size::value; -#endif - - template - struct variant_size : variant_size {}; - - template - struct variant_size : variant_size {}; - - template - struct variant_size : variant_size {}; - - template - struct variant_size> : lib::size_constant {}; - - template - struct variant_alternative; - - template - using variant_alternative_t = typename variant_alternative::type; - - template - struct variant_alternative - : std::add_const> {}; - - template - struct variant_alternative - : std::add_volatile> {}; - - template - struct variant_alternative - : std::add_cv> {}; - - template - struct variant_alternative> { - static_assert(I < sizeof...(Ts), - "index out of bounds in `std::variant_alternative<>`"); - using type = lib::type_pack_element_t; - }; - - constexpr std::size_t variant_npos = static_cast(-1); - - namespace detail { - - constexpr std::size_t not_found = static_cast(-1); - constexpr std::size_t ambiguous = static_cast(-2); - -#ifdef MPARK_CPP14_CONSTEXPR - template - inline constexpr std::size_t find_index() { - constexpr lib::array matches = { - {std::is_same::value...} - }; - std::size_t result = not_found; - for (std::size_t i = 0; i < sizeof...(Ts); ++i) { - if (matches[i]) { - if (result != not_found) { - return ambiguous; - } - result = i; - } - } - return result; - } -#else - inline constexpr std::size_t find_index_impl(std::size_t result, - std::size_t) { - return result; - } - - template - inline constexpr std::size_t find_index_impl(std::size_t result, - std::size_t idx, - bool b, - Bs... bs) { - return b ? (result != not_found ? ambiguous - : find_index_impl(idx, idx + 1, bs...)) - : find_index_impl(result, idx + 1, bs...); - } - - template - inline constexpr std::size_t find_index() { - return find_index_impl(not_found, 0, std::is_same::value...); - } -#endif - - template - using find_index_sfinae_impl = - lib::enable_if_t>; - - template - using find_index_sfinae = find_index_sfinae_impl()>; - - template - struct find_index_checked_impl : lib::size_constant { - static_assert(I != not_found, "the specified type is not found."); - static_assert(I != ambiguous, "the specified type is ambiguous."); - }; - - template - using find_index_checked = find_index_checked_impl()>; - - struct valueless_t {}; - - enum class Trait { TriviallyAvailable, Available, Unavailable }; - - template class IsTriviallyAvailable, - template class IsAvailable> - inline constexpr Trait trait() { - return IsTriviallyAvailable::value - ? Trait::TriviallyAvailable - : IsAvailable::value ? Trait::Available - : Trait::Unavailable; - } - -#ifdef MPARK_CPP14_CONSTEXPR - template - inline constexpr Trait common_trait(Traits... traits_) { - Trait result = Trait::TriviallyAvailable; - lib::array traits = {{traits_...}}; - for (std::size_t i = 0; i < sizeof...(Traits); ++i) { - Trait t = traits[i]; - if (static_cast(t) > static_cast(result)) { - result = t; - } - } - return result; - } -#else - inline constexpr Trait common_trait_impl(Trait result) { return result; } - - template - inline constexpr Trait common_trait_impl(Trait result, - Trait t, - Traits... ts) { - return static_cast(t) > static_cast(result) - ? common_trait_impl(t, ts...) - : common_trait_impl(result, ts...); - } - - template - inline constexpr Trait common_trait(Traits... ts) { - return common_trait_impl(Trait::TriviallyAvailable, ts...); - } -#endif - - template - struct traits { - static constexpr Trait copy_constructible_trait = - common_trait(trait()...); - - static constexpr Trait move_constructible_trait = - common_trait(trait()...); - - static constexpr Trait copy_assignable_trait = - common_trait(copy_constructible_trait, - trait()...); - - static constexpr Trait move_assignable_trait = - common_trait(move_constructible_trait, - trait()...); - - static constexpr Trait destructible_trait = - common_trait(trait()...); - }; - - namespace access { - - struct recursive_union { -#ifdef MPARK_RETURN_TYPE_DEDUCTION - template - inline static constexpr auto &&get_alt(V &&v, in_place_index_t<0>) { - return lib::forward(v).head_; - } - - template - inline static constexpr auto &&get_alt(V &&v, in_place_index_t) { - return get_alt(lib::forward(v).tail_, in_place_index_t{}); - } -#else - template - struct get_alt_impl { - template - inline constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v).tail_)) - }; - - template - struct get_alt_impl<0, Dummy> { - template - inline constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN(lib::forward(v).head_) - }; - - template - inline static constexpr AUTO_REFREF get_alt(V &&v, in_place_index_t) - AUTO_REFREF_RETURN(get_alt_impl{}(lib::forward(v))) -#endif - }; - - struct base { - template - inline static constexpr AUTO_REFREF get_alt(V &&v) -#ifdef _MSC_VER - AUTO_REFREF_RETURN(recursive_union::get_alt( - lib::forward(v).data_, in_place_index_t{})) -#else - AUTO_REFREF_RETURN(recursive_union::get_alt( - data(lib::forward(v)), in_place_index_t{})) -#endif - }; - - struct variant { - template - inline static constexpr AUTO_REFREF get_alt(V &&v) - AUTO_REFREF_RETURN(base::get_alt(lib::forward(v).impl_)) - }; - - } // namespace access - - namespace visitation { - -#if defined(MPARK_CPP14_CONSTEXPR) && !defined(_MSC_VER) -#define MPARK_VARIANT_SWITCH_VISIT -#endif - - struct base { - template - using dispatch_result_t = decltype( - lib::invoke(std::declval(), - access::base::get_alt<0>(std::declval())...)); - - template - struct expected { - template - inline static constexpr bool but_got() { - return std::is_same::value; - } - }; - - template - struct visit_return_type_check { - static_assert( - expected::template but_got(), - "`visit` requires the visitor to have a single return type"); - - template - inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, - Alts &&... alts) - DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward(visitor), - lib::forward(alts)...)) - }; - -#ifdef MPARK_VARIANT_SWITCH_VISIT - template - struct dispatcher; - - template - struct dispatcher { - template - MPARK_ALWAYS_INLINE static constexpr R dispatch( - F &&, typename ITs::type &&..., Vs &&...) { - MPARK_BUILTIN_UNREACHABLE; - } - - template - MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&, Vs &&...) { - MPARK_BUILTIN_UNREACHABLE; - } - - template - MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t, - F &&, - Vs &&...) { - MPARK_BUILTIN_UNREACHABLE; - } - }; - - template - struct dispatcher { - template - MPARK_ALWAYS_INLINE static constexpr R dispatch( - F &&f, typename ITs::type &&... visited_vs) { - using Expected = R; - using Actual = decltype(lib::invoke( - lib::forward(f), - access::base::get_alt( - lib::forward(visited_vs))...)); - return visit_return_type_check::invoke( - lib::forward(f), - access::base::get_alt( - lib::forward(visited_vs))...); - } - - template - MPARK_ALWAYS_INLINE static constexpr R dispatch( - F &&f, typename ITs::type &&... visited_vs, V &&v, Vs &&... vs) { -#define MPARK_DISPATCH(I) \ - dispatcher<(I < lib::decay_t::size()), \ - R, \ - ITs..., \ - lib::indexed_type>:: \ - template dispatch<0>(lib::forward(f), \ - lib::forward(visited_vs)..., \ - lib::forward(v), \ - lib::forward(vs)...) - -#define MPARK_DEFAULT(I) \ - dispatcher<(I < lib::decay_t::size()), R, ITs...>::template dispatch( \ - lib::forward(f), \ - lib::forward(visited_vs)..., \ - lib::forward(v), \ - lib::forward(vs)...) - - switch (v.index()) { - case B + 0: return MPARK_DISPATCH(B + 0); - case B + 1: return MPARK_DISPATCH(B + 1); - case B + 2: return MPARK_DISPATCH(B + 2); - case B + 3: return MPARK_DISPATCH(B + 3); - case B + 4: return MPARK_DISPATCH(B + 4); - case B + 5: return MPARK_DISPATCH(B + 5); - case B + 6: return MPARK_DISPATCH(B + 6); - case B + 7: return MPARK_DISPATCH(B + 7); - case B + 8: return MPARK_DISPATCH(B + 8); - case B + 9: return MPARK_DISPATCH(B + 9); - case B + 10: return MPARK_DISPATCH(B + 10); - case B + 11: return MPARK_DISPATCH(B + 11); - case B + 12: return MPARK_DISPATCH(B + 12); - case B + 13: return MPARK_DISPATCH(B + 13); - case B + 14: return MPARK_DISPATCH(B + 14); - case B + 15: return MPARK_DISPATCH(B + 15); - case B + 16: return MPARK_DISPATCH(B + 16); - case B + 17: return MPARK_DISPATCH(B + 17); - case B + 18: return MPARK_DISPATCH(B + 18); - case B + 19: return MPARK_DISPATCH(B + 19); - case B + 20: return MPARK_DISPATCH(B + 20); - case B + 21: return MPARK_DISPATCH(B + 21); - case B + 22: return MPARK_DISPATCH(B + 22); - case B + 23: return MPARK_DISPATCH(B + 23); - case B + 24: return MPARK_DISPATCH(B + 24); - case B + 25: return MPARK_DISPATCH(B + 25); - case B + 26: return MPARK_DISPATCH(B + 26); - case B + 27: return MPARK_DISPATCH(B + 27); - case B + 28: return MPARK_DISPATCH(B + 28); - case B + 29: return MPARK_DISPATCH(B + 29); - case B + 30: return MPARK_DISPATCH(B + 30); - case B + 31: return MPARK_DISPATCH(B + 31); - default: return MPARK_DEFAULT(B + 32); - } - -#undef MPARK_DEFAULT -#undef MPARK_DISPATCH - } - - template - MPARK_ALWAYS_INLINE static constexpr R dispatch_case(F &&f, - Vs &&... vs) { - using Expected = R; - using Actual = decltype( - lib::invoke(lib::forward(f), - access::base::get_alt(lib::forward(vs))...)); - return visit_return_type_check::invoke( - lib::forward(f), - access::base::get_alt(lib::forward(vs))...); - } - - template - MPARK_ALWAYS_INLINE static constexpr R dispatch_at(std::size_t index, - F &&f, - V &&v, - Vs &&... vs) { - static_assert(lib::all<(lib::decay_t::size() == - lib::decay_t::size())...>::value, - "all of the variants must be the same size."); -#define MPARK_DISPATCH_AT(I) \ - dispatcher<(I < lib::decay_t::size()), R>::template dispatch_case( \ - lib::forward(f), lib::forward(v), lib::forward(vs)...) - -#define MPARK_DEFAULT(I) \ - dispatcher<(I < lib::decay_t::size()), R>::template dispatch_at( \ - index, lib::forward(f), lib::forward(v), lib::forward(vs)...) - - switch (index) { - case B + 0: return MPARK_DISPATCH_AT(B + 0); - case B + 1: return MPARK_DISPATCH_AT(B + 1); - case B + 2: return MPARK_DISPATCH_AT(B + 2); - case B + 3: return MPARK_DISPATCH_AT(B + 3); - case B + 4: return MPARK_DISPATCH_AT(B + 4); - case B + 5: return MPARK_DISPATCH_AT(B + 5); - case B + 6: return MPARK_DISPATCH_AT(B + 6); - case B + 7: return MPARK_DISPATCH_AT(B + 7); - case B + 8: return MPARK_DISPATCH_AT(B + 8); - case B + 9: return MPARK_DISPATCH_AT(B + 9); - case B + 10: return MPARK_DISPATCH_AT(B + 10); - case B + 11: return MPARK_DISPATCH_AT(B + 11); - case B + 12: return MPARK_DISPATCH_AT(B + 12); - case B + 13: return MPARK_DISPATCH_AT(B + 13); - case B + 14: return MPARK_DISPATCH_AT(B + 14); - case B + 15: return MPARK_DISPATCH_AT(B + 15); - case B + 16: return MPARK_DISPATCH_AT(B + 16); - case B + 17: return MPARK_DISPATCH_AT(B + 17); - case B + 18: return MPARK_DISPATCH_AT(B + 18); - case B + 19: return MPARK_DISPATCH_AT(B + 19); - case B + 20: return MPARK_DISPATCH_AT(B + 20); - case B + 21: return MPARK_DISPATCH_AT(B + 21); - case B + 22: return MPARK_DISPATCH_AT(B + 22); - case B + 23: return MPARK_DISPATCH_AT(B + 23); - case B + 24: return MPARK_DISPATCH_AT(B + 24); - case B + 25: return MPARK_DISPATCH_AT(B + 25); - case B + 26: return MPARK_DISPATCH_AT(B + 26); - case B + 27: return MPARK_DISPATCH_AT(B + 27); - case B + 28: return MPARK_DISPATCH_AT(B + 28); - case B + 29: return MPARK_DISPATCH_AT(B + 29); - case B + 30: return MPARK_DISPATCH_AT(B + 30); - case B + 31: return MPARK_DISPATCH_AT(B + 31); - default: return MPARK_DEFAULT(B + 32); - } - -#undef MPARK_DEFAULT -#undef MPARK_DISPATCH_AT - } - }; -#else - template - inline static constexpr const T &at(const T &elem) noexcept { - return elem; - } - - template - inline static constexpr const lib::remove_all_extents_t &at( - const lib::array &elems, std::size_t i, Is... is) noexcept { - return at(elems[i], is...); - } - - template - inline static constexpr lib::array, sizeof...(Fs) + 1> - make_farray(F &&f, Fs &&... fs) { - return {{lib::forward(f), lib::forward(fs)...}}; - } - - template - struct make_fmatrix_impl { - - template - inline static constexpr dispatch_result_t dispatch( - F &&f, Vs &&... vs) { - using Expected = dispatch_result_t; - using Actual = decltype(lib::invoke( - lib::forward(f), - access::base::get_alt(lib::forward(vs))...)); - return visit_return_type_check::invoke( - lib::forward(f), - access::base::get_alt(lib::forward(vs))...); - } - -#ifdef MPARK_RETURN_TYPE_DEDUCTION - template - inline static constexpr auto impl(lib::index_sequence) { - return &dispatch; - } - - template - inline static constexpr auto impl(Is, - lib::index_sequence, - Ls... ls) { - return make_farray(impl(lib::push_back_t{}, ls...)...); - } -#else - template - struct impl; - - template - struct impl> { - inline constexpr AUTO operator()() const - AUTO_RETURN(&dispatch) - }; - - template - struct impl, Ls...> { - inline constexpr AUTO operator()() const - AUTO_RETURN( - make_farray(impl, Ls...>{}()...)) - }; -#endif - }; - -#ifdef MPARK_RETURN_TYPE_DEDUCTION - template - inline static constexpr auto make_fmatrix() { - return make_fmatrix_impl::impl( - lib::index_sequence<>{}, - lib::make_index_sequence::size()>{}...); - } -#else - template - inline static constexpr AUTO make_fmatrix() - AUTO_RETURN( - typename make_fmatrix_impl::template impl< - lib::index_sequence<>, - lib::make_index_sequence::size()>...>{}()) -#endif - - template - struct make_fdiagonal_impl { - template - inline static constexpr dispatch_result_t dispatch( - F &&f, Vs &&... vs) { - using Expected = dispatch_result_t; - using Actual = decltype( - lib::invoke(lib::forward(f), - access::base::get_alt(lib::forward(vs))...)); - return visit_return_type_check::invoke( - lib::forward(f), - access::base::get_alt(lib::forward(vs))...); - } - - template - inline static constexpr AUTO impl(lib::index_sequence) - AUTO_RETURN(make_farray(&dispatch...)) - }; - - template - inline static constexpr auto make_fdiagonal() - -> decltype(make_fdiagonal_impl::impl( - lib::make_index_sequence::size()>{})) { - static_assert(lib::all<(lib::decay_t::size() == - lib::decay_t::size())...>::value, - "all of the variants must be the same size."); - return make_fdiagonal_impl::impl( - lib::make_index_sequence::size()>{}); - } -#endif - }; - -#if !defined(MPARK_VARIANT_SWITCH_VISIT) && \ - (!defined(_MSC_VER) || _MSC_VER >= 1910) - template - using fmatrix_t = decltype(base::make_fmatrix()); - - template - struct fmatrix { - static constexpr fmatrix_t value = - base::make_fmatrix(); - }; - - template - constexpr fmatrix_t fmatrix::value; - - template - using fdiagonal_t = decltype(base::make_fdiagonal()); - - template - struct fdiagonal { - static constexpr fdiagonal_t value = - base::make_fdiagonal(); - }; - - template - constexpr fdiagonal_t fdiagonal::value; -#endif - - struct alt { - template - inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, - Vs &&... vs) -#ifdef MPARK_VARIANT_SWITCH_VISIT - DECLTYPE_AUTO_RETURN( - base::dispatcher< - true, - base::dispatch_result_t(vs)))...>>:: - template dispatch<0>(lib::forward(visitor), - as_base(lib::forward(vs))...)) -#elif !defined(_MSC_VER) || _MSC_VER >= 1910 - DECLTYPE_AUTO_RETURN(base::at( - fmatrix(vs)))...>::value, - vs.index()...)(lib::forward(visitor), - as_base(lib::forward(vs))...)) -#else - DECLTYPE_AUTO_RETURN(base::at( - base::make_fmatrix(vs)))...>(), - vs.index()...)(lib::forward(visitor), - as_base(lib::forward(vs))...)) -#endif - - template - inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) -#ifdef MPARK_VARIANT_SWITCH_VISIT - DECLTYPE_AUTO_RETURN( - base::dispatcher< - true, - base::dispatch_result_t(vs)))...>>:: - template dispatch_at<0>(index, - lib::forward(visitor), - as_base(lib::forward(vs))...)) -#elif !defined(_MSC_VER) || _MSC_VER >= 1910 - DECLTYPE_AUTO_RETURN(base::at( - fdiagonal(vs)))...>::value, - index)(lib::forward(visitor), - as_base(lib::forward(vs))...)) -#else - DECLTYPE_AUTO_RETURN(base::at( - base::make_fdiagonal(vs)))...>(), - index)(lib::forward(visitor), - as_base(lib::forward(vs))...)) -#endif - }; - - struct variant { - private: - template - struct visitor { - template - inline static constexpr bool does_not_handle() { - return lib::is_invocable::value; - } - }; - - template - struct visit_exhaustiveness_check { - static_assert(visitor::template does_not_handle(), - "`visit` requires the visitor to be exhaustive."); - - inline static constexpr DECLTYPE_AUTO invoke(Visitor &&visitor, - Values &&... values) - DECLTYPE_AUTO_RETURN(lib::invoke(lib::forward(visitor), - lib::forward(values)...)) - }; - - template - struct value_visitor { - Visitor &&visitor_; - - template - inline constexpr DECLTYPE_AUTO operator()(Alts &&... alts) const - DECLTYPE_AUTO_RETURN( - visit_exhaustiveness_check< - Visitor, - decltype((lib::forward(alts).value))...>:: - invoke(lib::forward(visitor_), - lib::forward(alts).value...)) - }; - - template - inline static constexpr AUTO make_value_visitor(Visitor &&visitor) - AUTO_RETURN(value_visitor{lib::forward(visitor)}) - - public: - template - inline static constexpr DECLTYPE_AUTO visit_alt(Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN(alt::visit_alt(lib::forward(visitor), - lib::forward(vs).impl_...)) - - template - inline static constexpr DECLTYPE_AUTO visit_alt_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - alt::visit_alt_at(index, - lib::forward(visitor), - lib::forward(vs).impl_...)) - - template - inline static constexpr DECLTYPE_AUTO visit_value(Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt(make_value_visitor(lib::forward(visitor)), - lib::forward(vs)...)) - - template - inline static constexpr DECLTYPE_AUTO visit_value_at(std::size_t index, - Visitor &&visitor, - Vs &&... vs) - DECLTYPE_AUTO_RETURN( - visit_alt_at(index, - make_value_visitor(lib::forward(visitor)), - lib::forward(vs)...)) - }; - - } // namespace visitation - - template - struct alt { - using value_type = T; - -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - template - inline explicit constexpr alt(in_place_t, Args &&... args) - : value(lib::forward(args)...) {} -#ifdef _MSC_VER -#pragma warning(pop) -#endif - - T value; - }; - - template - union recursive_union; - - template - union recursive_union {}; - -#define MPARK_VARIANT_RECURSIVE_UNION(destructible_trait, destructor) \ - template \ - union recursive_union { \ - public: \ - inline explicit constexpr recursive_union(valueless_t) noexcept \ - : dummy_{} {} \ - \ - template \ - inline explicit constexpr recursive_union(in_place_index_t<0>, \ - Args &&... args) \ - : head_(in_place_t{}, lib::forward(args)...) {} \ - \ - template \ - inline explicit constexpr recursive_union(in_place_index_t, \ - Args &&... args) \ - : tail_(in_place_index_t{}, lib::forward(args)...) {} \ - \ - recursive_union(const recursive_union &) = default; \ - recursive_union(recursive_union &&) = default; \ - \ - destructor \ - \ - recursive_union &operator=(const recursive_union &) = default; \ - recursive_union &operator=(recursive_union &&) = default; \ - \ - private: \ - char dummy_; \ - alt head_; \ - recursive_union tail_; \ - \ - friend struct access::recursive_union; \ - } - - MPARK_VARIANT_RECURSIVE_UNION(Trait::TriviallyAvailable, - ~recursive_union() = default;); - MPARK_VARIANT_RECURSIVE_UNION(Trait::Available, - ~recursive_union() {}); - MPARK_VARIANT_RECURSIVE_UNION(Trait::Unavailable, - ~recursive_union() = delete;); - -#undef MPARK_VARIANT_RECURSIVE_UNION - - using index_t = unsigned int; - - template - class base { - public: - inline explicit constexpr base(valueless_t tag) noexcept - : data_(tag), index_(static_cast(-1)) {} - - template - inline explicit constexpr base(in_place_index_t, Args &&... args) - : data_(in_place_index_t{}, lib::forward(args)...), - index_(I) {} - - inline constexpr bool valueless_by_exception() const noexcept { - return index_ == static_cast(-1); - } - - inline constexpr std::size_t index() const noexcept { - return valueless_by_exception() ? variant_npos : index_; - } - - protected: - using data_t = recursive_union; - - friend inline constexpr base &as_base(base &b) { return b; } - friend inline constexpr const base &as_base(const base &b) { return b; } - friend inline constexpr base &&as_base(base &&b) { return lib::move(b); } - friend inline constexpr const base &&as_base(const base &&b) { return lib::move(b); } - - friend inline constexpr data_t &data(base &b) { return b.data_; } - friend inline constexpr const data_t &data(const base &b) { return b.data_; } - friend inline constexpr data_t &&data(base &&b) { return lib::move(b).data_; } - friend inline constexpr const data_t &&data(const base &&b) { return lib::move(b).data_; } - - inline static constexpr std::size_t size() { return sizeof...(Ts); } - - data_t data_; - index_t index_; - - friend struct access::base; - friend struct visitation::base; - }; - - struct dtor { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4100) -#endif - template - inline void operator()(Alt &alt) const noexcept { alt.~Alt(); } -#ifdef _MSC_VER -#pragma warning(pop) -#endif - }; - -#if !defined(_MSC_VER) || _MSC_VER >= 1910 -#define MPARK_INHERITING_CTOR(type, base) using base::base; -#else -#define MPARK_INHERITING_CTOR(type, base) \ - template \ - inline explicit constexpr type(Args &&... args) \ - : base(lib::forward(args)...) {} -#endif - - template - class destructor; - -#define MPARK_VARIANT_DESTRUCTOR(destructible_trait, definition, destroy) \ - template \ - class destructor, destructible_trait> \ - : public base { \ - using super = base; \ - \ - public: \ - MPARK_INHERITING_CTOR(destructor, super) \ - using super::operator=; \ - \ - destructor(const destructor &) = default; \ - destructor(destructor &&) = default; \ - definition \ - destructor &operator=(const destructor &) = default; \ - destructor &operator=(destructor &&) = default; \ - \ - protected: \ - destroy \ - } - - MPARK_VARIANT_DESTRUCTOR( - Trait::TriviallyAvailable, - ~destructor() = default;, - inline void destroy() noexcept { - this->index_ = static_cast(-1); - }); - - MPARK_VARIANT_DESTRUCTOR( - Trait::Available, - ~destructor() { destroy(); }, - inline void destroy() noexcept { - if (!this->valueless_by_exception()) { - visitation::alt::visit_alt(dtor{}, *this); - } - this->index_ = static_cast(-1); - }); - - MPARK_VARIANT_DESTRUCTOR( - Trait::Unavailable, - ~destructor() = delete;, - inline void destroy() noexcept = delete;); - -#undef MPARK_VARIANT_DESTRUCTOR - - template - class constructor : public destructor { - using super = destructor; - - public: - MPARK_INHERITING_CTOR(constructor, super) - using super::operator=; - - protected: -#ifndef MPARK_GENERIC_LAMBDAS - struct ctor { - template - inline void operator()(LhsAlt &lhs_alt, RhsAlt &&rhs_alt) const { - constructor::construct_alt(lhs_alt, - lib::forward(rhs_alt).value); - } - }; -#endif - - template - inline static T &construct_alt(alt &a, Args &&... args) { - auto *result = ::new (static_cast(lib::addressof(a))) - alt(in_place_t{}, lib::forward(args)...); - return result->value; - } - - template - inline static void generic_construct(constructor &lhs, Rhs &&rhs) { - lhs.destroy(); - if (!rhs.valueless_by_exception()) { - visitation::alt::visit_alt_at( - rhs.index(), -#ifdef MPARK_GENERIC_LAMBDAS - [](auto &lhs_alt, auto &&rhs_alt) { - constructor::construct_alt( - lhs_alt, lib::forward(rhs_alt).value); - } -#else - ctor{} -#endif - , - lhs, - lib::forward(rhs)); - lhs.index_ = rhs.index_; - } - } - }; - - template - class move_constructor; - -#define MPARK_VARIANT_MOVE_CONSTRUCTOR(move_constructible_trait, definition) \ - template \ - class move_constructor, move_constructible_trait> \ - : public constructor> { \ - using super = constructor>; \ - \ - public: \ - MPARK_INHERITING_CTOR(move_constructor, super) \ - using super::operator=; \ - \ - move_constructor(const move_constructor &) = default; \ - definition \ - ~move_constructor() = default; \ - move_constructor &operator=(const move_constructor &) = default; \ - move_constructor &operator=(move_constructor &&) = default; \ - } - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::TriviallyAvailable, - move_constructor(move_constructor &&that) = default;); - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::Available, - move_constructor(move_constructor &&that) noexcept( - lib::all::value...>::value) - : move_constructor(valueless_t{}) { - this->generic_construct(*this, lib::move(that)); - }); - - MPARK_VARIANT_MOVE_CONSTRUCTOR( - Trait::Unavailable, - move_constructor(move_constructor &&) = delete;); - -#undef MPARK_VARIANT_MOVE_CONSTRUCTOR - - template - class copy_constructor; - -#define MPARK_VARIANT_COPY_CONSTRUCTOR(copy_constructible_trait, definition) \ - template \ - class copy_constructor, copy_constructible_trait> \ - : public move_constructor> { \ - using super = move_constructor>; \ - \ - public: \ - MPARK_INHERITING_CTOR(copy_constructor, super) \ - using super::operator=; \ - \ - definition \ - copy_constructor(copy_constructor &&) = default; \ - ~copy_constructor() = default; \ - copy_constructor &operator=(const copy_constructor &) = default; \ - copy_constructor &operator=(copy_constructor &&) = default; \ - } - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::TriviallyAvailable, - copy_constructor(const copy_constructor &that) = default;); - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::Available, - copy_constructor(const copy_constructor &that) - : copy_constructor(valueless_t{}) { - this->generic_construct(*this, that); - }); - - MPARK_VARIANT_COPY_CONSTRUCTOR( - Trait::Unavailable, - copy_constructor(const copy_constructor &) = delete;); - -#undef MPARK_VARIANT_COPY_CONSTRUCTOR - - template - class assignment : public copy_constructor { - using super = copy_constructor; - - public: - MPARK_INHERITING_CTOR(assignment, super) - using super::operator=; - - template - inline /* auto & */ auto emplace(Args &&... args) - -> decltype(this->construct_alt(access::base::get_alt(*this), - lib::forward(args)...)) { - this->destroy(); - auto &result = this->construct_alt(access::base::get_alt(*this), - lib::forward(args)...); - this->index_ = I; - return result; - } - - protected: -#ifndef MPARK_GENERIC_LAMBDAS - template - struct assigner { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &&that_alt) const { - self->assign_alt(this_alt, lib::forward(that_alt).value); - } - assignment *self; - }; -#endif - - template - inline void assign_alt(alt &a, Arg &&arg) { - if (this->index() == I) { -#ifdef _MSC_VER -#pragma warning(push) -#pragma warning(disable : 4244) -#endif - a.value = lib::forward(arg); -#ifdef _MSC_VER -#pragma warning(pop) -#endif - } else { - struct { - void operator()(std::true_type) const { - this_->emplace(lib::forward(arg_)); - } - void operator()(std::false_type) const { - this_->emplace(T(lib::forward(arg_))); - } - assignment *this_; - Arg &&arg_; - } impl{this, lib::forward(arg)}; - impl(lib::bool_constant< - std::is_nothrow_constructible::value || - !std::is_nothrow_move_constructible::value>{}); - } - } - - template - inline void generic_assign(That &&that) { - if (this->valueless_by_exception() && that.valueless_by_exception()) { - // do nothing. - } else if (that.valueless_by_exception()) { - this->destroy(); - } else { - visitation::alt::visit_alt_at( - that.index(), -#ifdef MPARK_GENERIC_LAMBDAS - [this](auto &this_alt, auto &&that_alt) { - this->assign_alt( - this_alt, lib::forward(that_alt).value); - } -#else - assigner{this} -#endif - , - *this, - lib::forward(that)); - } - } - }; - - template - class move_assignment; - -#define MPARK_VARIANT_MOVE_ASSIGNMENT(move_assignable_trait, definition) \ - template \ - class move_assignment, move_assignable_trait> \ - : public assignment> { \ - using super = assignment>; \ - \ - public: \ - MPARK_INHERITING_CTOR(move_assignment, super) \ - using super::operator=; \ - \ - move_assignment(const move_assignment &) = default; \ - move_assignment(move_assignment &&) = default; \ - ~move_assignment() = default; \ - move_assignment &operator=(const move_assignment &) = default; \ - definition \ - } - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::TriviallyAvailable, - move_assignment &operator=(move_assignment &&that) = default;); - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::Available, - move_assignment & - operator=(move_assignment &&that) noexcept( - lib::all<(std::is_nothrow_move_constructible::value && - std::is_nothrow_move_assignable::value)...>::value) { - this->generic_assign(lib::move(that)); - return *this; - }); - - MPARK_VARIANT_MOVE_ASSIGNMENT( - Trait::Unavailable, - move_assignment &operator=(move_assignment &&) = delete;); - -#undef MPARK_VARIANT_MOVE_ASSIGNMENT - - template - class copy_assignment; - -#define MPARK_VARIANT_COPY_ASSIGNMENT(copy_assignable_trait, definition) \ - template \ - class copy_assignment, copy_assignable_trait> \ - : public move_assignment> { \ - using super = move_assignment>; \ - \ - public: \ - MPARK_INHERITING_CTOR(copy_assignment, super) \ - using super::operator=; \ - \ - copy_assignment(const copy_assignment &) = default; \ - copy_assignment(copy_assignment &&) = default; \ - ~copy_assignment() = default; \ - definition \ - copy_assignment &operator=(copy_assignment &&) = default; \ - } - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::TriviallyAvailable, - copy_assignment &operator=(const copy_assignment &that) = default;); - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::Available, - copy_assignment &operator=(const copy_assignment &that) { - this->generic_assign(that); - return *this; - }); - - MPARK_VARIANT_COPY_ASSIGNMENT( - Trait::Unavailable, - copy_assignment &operator=(const copy_assignment &) = delete;); - -#undef MPARK_VARIANT_COPY_ASSIGNMENT - - template - class impl : public copy_assignment> { - using super = copy_assignment>; - - public: - MPARK_INHERITING_CTOR(impl, super) - using super::operator=; - - template - inline void assign(Arg &&arg) { - this->assign_alt(access::base::get_alt(*this), - lib::forward(arg)); - } - - inline void swap(impl &that) { - if (this->valueless_by_exception() && that.valueless_by_exception()) { - // do nothing. - } else if (this->index() == that.index()) { - visitation::alt::visit_alt_at(this->index(), -#ifdef MPARK_GENERIC_LAMBDAS - [](auto &this_alt, auto &that_alt) { - using std::swap; - swap(this_alt.value, - that_alt.value); - } -#else - swapper{} -#endif - , - *this, - that); - } else { - impl *lhs = this; - impl *rhs = lib::addressof(that); - if (lhs->move_nothrow() && !rhs->move_nothrow()) { - std::swap(lhs, rhs); - } - impl tmp(lib::move(*rhs)); -#ifdef MPARK_EXCEPTIONS - // EXTENSION: When the move construction of `lhs` into `rhs` throws - // and `tmp` is nothrow move constructible then we move `tmp` back - // into `rhs` and provide the strong exception safety guarantee. - try { - this->generic_construct(*rhs, lib::move(*lhs)); - } catch (...) { - if (tmp.move_nothrow()) { - this->generic_construct(*rhs, lib::move(tmp)); - } - throw; - } -#else - this->generic_construct(*rhs, lib::move(*lhs)); -#endif - this->generic_construct(*lhs, lib::move(tmp)); - } - } - - private: -#ifndef MPARK_GENERIC_LAMBDAS - struct swapper { - template - inline void operator()(ThisAlt &this_alt, ThatAlt &that_alt) const { - using std::swap; - swap(this_alt.value, that_alt.value); - } - }; -#endif - - inline constexpr bool move_nothrow() const { - return this->valueless_by_exception() || - lib::array{ - {std::is_nothrow_move_constructible::value...} - }[this->index()]; - } - }; - -#undef MPARK_INHERITING_CTOR - - template - struct overload_leaf { - using F = lib::size_constant (*)(T); - operator F() const { return nullptr; } - }; - - template - struct overload_impl { - private: - template - struct impl; - - template - struct impl> : overload_leaf... {}; - - public: - using type = impl>; - }; - - template - using overload = typename overload_impl::type; - - template - using best_match = lib::invoke_result_t, T &&>; - - template - struct is_in_place_index : std::false_type {}; - - template - struct is_in_place_index> : std::true_type {}; - - template - struct is_in_place_type : std::false_type {}; - - template - struct is_in_place_type> : std::true_type {}; - - } // detail - - template - class variant { - static_assert(0 < sizeof...(Ts), - "variant must consist of at least one alternative."); - - static_assert(lib::all::value...>::value, - "variant can not have an array type as an alternative."); - - static_assert(lib::all::value...>::value, - "variant can not have a reference type as an alternative."); - - static_assert(lib::all::value...>::value, - "variant can not have a void type as an alternative."); - - public: - template < - typename Front = lib::type_pack_element_t<0, Ts...>, - lib::enable_if_t::value, int> = 0> - inline constexpr variant() noexcept( - std::is_nothrow_default_constructible::value) - : impl_(in_place_index_t<0>{}) {} - - variant(const variant &) = default; - variant(variant &&) = default; - - template < - typename Arg, - typename Decayed = lib::decay_t, - lib::enable_if_t::value, int> = 0, - lib::enable_if_t::value, int> = 0, - lib::enable_if_t::value, int> = 0, - std::size_t I = detail::best_match::value, - typename T = lib::type_pack_element_t, - lib::enable_if_t::value, int> = 0> - inline constexpr variant(Arg &&arg) noexcept( - std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, lib::forward(arg)) {} - - template < - std::size_t I, - typename... Args, - typename T = lib::type_pack_element_t, - lib::enable_if_t::value, int> = 0> - inline explicit constexpr variant( - in_place_index_t, - Args &&... args) noexcept(std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, lib::forward(args)...) {} - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = lib::type_pack_element_t, - lib::enable_if_t &, - Args...>::value, - int> = 0> - inline explicit constexpr variant( - in_place_index_t, - std::initializer_list il, - Args &&... args) noexcept(std:: - is_nothrow_constructible< - T, - std::initializer_list &, - Args...>::value) - : impl_(in_place_index_t{}, il, lib::forward(args)...) {} - - template < - typename T, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - lib::enable_if_t::value, int> = 0> - inline explicit constexpr variant( - in_place_type_t, - Args &&... args) noexcept(std::is_nothrow_constructible::value) - : impl_(in_place_index_t{}, lib::forward(args)...) {} - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - lib::enable_if_t &, - Args...>::value, - int> = 0> - inline explicit constexpr variant( - in_place_type_t, - std::initializer_list il, - Args &&... args) noexcept(std:: - is_nothrow_constructible< - T, - std::initializer_list &, - Args...>::value) - : impl_(in_place_index_t{}, il, lib::forward(args)...) {} - - ~variant() = default; - - variant &operator=(const variant &) = default; - variant &operator=(variant &&) = default; - - template , variant>::value, - int> = 0, - std::size_t I = detail::best_match::value, - typename T = lib::type_pack_element_t, - lib::enable_if_t<(std::is_assignable::value && - std::is_constructible::value), - int> = 0> - inline variant &operator=(Arg &&arg) noexcept( - (std::is_nothrow_assignable::value && - std::is_nothrow_constructible::value)) { - impl_.template assign(lib::forward(arg)); - return *this; - } - - template < - std::size_t I, - typename... Args, - typename T = lib::type_pack_element_t, - lib::enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) { - return impl_.template emplace(lib::forward(args)...); - } - - template < - std::size_t I, - typename Up, - typename... Args, - typename T = lib::type_pack_element_t, - lib::enable_if_t &, - Args...>::value, - int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) { - return impl_.template emplace(il, lib::forward(args)...); - } - - template < - typename T, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - lib::enable_if_t::value, int> = 0> - inline T &emplace(Args &&... args) { - return impl_.template emplace(lib::forward(args)...); - } - - template < - typename T, - typename Up, - typename... Args, - std::size_t I = detail::find_index_sfinae::value, - lib::enable_if_t &, - Args...>::value, - int> = 0> - inline T &emplace(std::initializer_list il, Args &&... args) { - return impl_.template emplace(il, lib::forward(args)...); - } - - inline constexpr bool valueless_by_exception() const noexcept { - return impl_.valueless_by_exception(); - } - - inline constexpr std::size_t index() const noexcept { - return impl_.index(); - } - - template , - Dummy>::value && - lib::dependent_type, - Dummy>::value)...>::value, - int> = 0> - inline void swap(variant &that) noexcept( - lib::all<(std::is_nothrow_move_constructible::value && - lib::is_nothrow_swappable::value)...>::value) { - impl_.swap(that.impl_); - } - - private: - detail::impl impl_; - - friend struct detail::access::variant; - friend struct detail::visitation::variant; - }; - - template - inline constexpr bool holds_alternative(const variant &v) noexcept { - return v.index() == I; - } - - template - inline constexpr bool holds_alternative(const variant &v) noexcept { - return holds_alternative::value>(v); - } - - namespace detail { - template - struct generic_get_impl { - constexpr generic_get_impl(int) noexcept {} - - constexpr AUTO_REFREF operator()(V &&v) const - AUTO_REFREF_RETURN( - access::variant::get_alt(lib::forward(v)).value) - }; - - template - inline constexpr AUTO_REFREF generic_get(V &&v) - AUTO_REFREF_RETURN(generic_get_impl( - holds_alternative(v) ? 0 : (throw_bad_variant_access(), 0))( - lib::forward(v))) - } // namespace detail - - template - inline constexpr variant_alternative_t> &get( - variant &v) { - return detail::generic_get(v); - } - - template - inline constexpr variant_alternative_t> &&get( - variant &&v) { - return detail::generic_get(lib::move(v)); - } - - template - inline constexpr const variant_alternative_t> &get( - const variant &v) { - return detail::generic_get(v); - } - - template - inline constexpr const variant_alternative_t> &&get( - const variant &&v) { - return detail::generic_get(lib::move(v)); - } - - template - inline constexpr T &get(variant &v) { - return get::value>(v); - } - - template - inline constexpr T &&get(variant &&v) { - return get::value>(lib::move(v)); - } - - template - inline constexpr const T &get(const variant &v) { - return get::value>(v); - } - - template - inline constexpr const T &&get(const variant &&v) { - return get::value>(lib::move(v)); - } - - namespace detail { - - template - inline constexpr /* auto * */ AUTO generic_get_if(V *v) noexcept - AUTO_RETURN(v && holds_alternative(*v) - ? lib::addressof(access::variant::get_alt(*v).value) - : nullptr) - - } // namespace detail - - template - inline constexpr lib::add_pointer_t>> - get_if(variant *v) noexcept { - return detail::generic_get_if(v); - } - - template - inline constexpr lib::add_pointer_t< - const variant_alternative_t>> - get_if(const variant *v) noexcept { - return detail::generic_get_if(v); - } - - template - inline constexpr lib::add_pointer_t - get_if(variant *v) noexcept { - return get_if::value>(v); - } - - template - inline constexpr lib::add_pointer_t - get_if(const variant *v) noexcept { - return get_if::value>(v); - } - - namespace detail { - template - struct convert_to_bool { - template - inline constexpr bool operator()(Lhs &&lhs, Rhs &&rhs) const { - static_assert(std::is_convertible, - bool>::value, - "relational operators must return a type" - " implicitly convertible to bool"); - return lib::invoke( - RelOp{}, lib::forward(lhs), lib::forward(rhs)); - } - }; - } // namespace detail - - template - inline constexpr bool operator==(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using equal_to = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.index() != rhs.index()) return false; - if (lhs.valueless_by_exception()) return true; - return variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs); -#else - return lhs.index() == rhs.index() && - (lhs.valueless_by_exception() || - variant::visit_value_at(lhs.index(), equal_to{}, lhs, rhs)); -#endif - } - - template - inline constexpr bool operator!=(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using not_equal_to = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.index() != rhs.index()) return true; - if (lhs.valueless_by_exception()) return false; - return variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs); -#else - return lhs.index() != rhs.index() || - (!lhs.valueless_by_exception() && - variant::visit_value_at(lhs.index(), not_equal_to{}, lhs, rhs)); -#endif - } - - template - inline constexpr bool operator<(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using less = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (rhs.valueless_by_exception()) return false; - if (lhs.valueless_by_exception()) return true; - if (lhs.index() < rhs.index()) return true; - if (lhs.index() > rhs.index()) return false; - return variant::visit_value_at(lhs.index(), less{}, lhs, rhs); -#else - return !rhs.valueless_by_exception() && - (lhs.valueless_by_exception() || lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less{}, lhs, rhs))); -#endif - } - - template - inline constexpr bool operator>(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using greater = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.valueless_by_exception()) return false; - if (rhs.valueless_by_exception()) return true; - if (lhs.index() > rhs.index()) return true; - if (lhs.index() < rhs.index()) return false; - return variant::visit_value_at(lhs.index(), greater{}, lhs, rhs); -#else - return !lhs.valueless_by_exception() && - (rhs.valueless_by_exception() || lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), greater{}, lhs, rhs))); -#endif - } - - template - inline constexpr bool operator<=(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using less_equal = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (lhs.valueless_by_exception()) return true; - if (rhs.valueless_by_exception()) return false; - if (lhs.index() < rhs.index()) return true; - if (lhs.index() > rhs.index()) return false; - return variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs); -#else - return lhs.valueless_by_exception() || - (!rhs.valueless_by_exception() && - (lhs.index() < rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at(lhs.index(), less_equal{}, lhs, rhs)))); -#endif - } - - template - inline constexpr bool operator>=(const variant &lhs, - const variant &rhs) { - using detail::visitation::variant; - using greater_equal = detail::convert_to_bool; -#ifdef MPARK_CPP14_CONSTEXPR - if (rhs.valueless_by_exception()) return true; - if (lhs.valueless_by_exception()) return false; - if (lhs.index() > rhs.index()) return true; - if (lhs.index() < rhs.index()) return false; - return variant::visit_value_at(lhs.index(), greater_equal{}, lhs, rhs); -#else - return rhs.valueless_by_exception() || - (!lhs.valueless_by_exception() && - (lhs.index() > rhs.index() || - (lhs.index() == rhs.index() && - variant::visit_value_at( - lhs.index(), greater_equal{}, lhs, rhs)))); -#endif - } - - struct monostate {}; - - inline constexpr bool operator<(monostate, monostate) noexcept { - return false; - } - - inline constexpr bool operator>(monostate, monostate) noexcept { - return false; - } - - inline constexpr bool operator<=(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator>=(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator==(monostate, monostate) noexcept { - return true; - } - - inline constexpr bool operator!=(monostate, monostate) noexcept { - return false; - } - -#ifdef MPARK_CPP14_CONSTEXPR - namespace detail { - - inline constexpr bool all(std::initializer_list bs) { - for (bool b : bs) { - if (!b) { - return false; - } - } - return true; - } - - } // namespace detail - - template - inline constexpr decltype(auto) visit(Visitor &&visitor, Vs &&... vs) { - return (detail::all({!vs.valueless_by_exception()...}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value( - lib::forward(visitor), lib::forward(vs)...); - } -#else - namespace detail { - - template - inline constexpr bool all_impl(const lib::array &bs, - std::size_t idx) { - return idx >= N || (bs[idx] && all_impl(bs, idx + 1)); - } - - template - inline constexpr bool all(const lib::array &bs) { - return all_impl(bs, 0); - } - - } // namespace detail - - template - inline constexpr DECLTYPE_AUTO visit(Visitor &&visitor, Vs &&... vs) - DECLTYPE_AUTO_RETURN( - (detail::all( - lib::array{{!vs.valueless_by_exception()...}}) - ? (void)0 - : throw_bad_variant_access()), - detail::visitation::variant::visit_value(lib::forward(visitor), - lib::forward(vs)...)) -#endif - - template - inline auto swap(variant &lhs, - variant &rhs) noexcept(noexcept(lhs.swap(rhs))) - -> decltype(lhs.swap(rhs)) { - lhs.swap(rhs); - } - - namespace detail { - - template - using enabled_type = T; - - namespace hash { - - template - constexpr bool meets_requirements() noexcept { - return std::is_copy_constructible::value && - std::is_move_constructible::value && - lib::is_invocable_r::value; - } - - template - constexpr bool is_enabled() noexcept { - using H = std::hash; - return meets_requirements() && - std::is_default_constructible::value && - std::is_copy_assignable::value && - std::is_move_assignable::value; - } - - } // namespace hash - - } // namespace detail - -#undef AUTO -#undef AUTO_RETURN - -#undef AUTO_REFREF -#undef AUTO_REFREF_RETURN - -#undef DECLTYPE_AUTO -#undef DECLTYPE_AUTO_RETURN - -} // namespace mpark - -namespace std { - - template - struct hash, - mpark::lib::enable_if_t>()...>::value>>> { - using argument_type = mpark::variant; - using result_type = std::size_t; - - inline result_type operator()(const argument_type &v) const { - using mpark::detail::visitation::variant; - std::size_t result = - v.valueless_by_exception() - ? 299792458 // Random value chosen by the universe upon creation - : variant::visit_alt( -#ifdef MPARK_GENERIC_LAMBDAS - [](const auto &alt) { - using alt_type = mpark::lib::decay_t; - using value_type = mpark::lib::remove_const_t< - typename alt_type::value_type>; - return hash{}(alt.value); - } -#else - hasher{} -#endif - , - v); - return hash_combine(result, hash{}(v.index())); - } - - private: -#ifndef MPARK_GENERIC_LAMBDAS - struct hasher { - template - inline std::size_t operator()(const Alt &alt) const { - using alt_type = mpark::lib::decay_t; - using value_type = - mpark::lib::remove_const_t; - return hash{}(alt.value); - } - }; -#endif - - static std::size_t hash_combine(std::size_t lhs, std::size_t rhs) { - return lhs ^= rhs + 0x9e3779b9 + (lhs << 6) + (lhs >> 2); - } - }; - - template <> - struct hash { - using argument_type = mpark::monostate; - using result_type = std::size_t; - - inline result_type operator()(const argument_type &) const noexcept { - return 66740831; // return a fundamentally attractive random value. - } - }; - -} // namespace std diff --git a/include/srsran/adt/byte_buffer.h b/include/srsran/adt/byte_buffer.h index 30f033237a..57defd1827 100644 --- a/include/srsran/adt/byte_buffer.h +++ b/include/srsran/adt/byte_buffer.h @@ -635,7 +635,7 @@ class byte_buffer_writer }; /// Converts a string of hexadecimal digits (e.g. "01FA02") to a byte buffer. -byte_buffer make_byte_buffer(const std::string& hex_str); +expected make_byte_buffer(const std::string& hex_str); /// Performs a segment-wise copy of the byte_buffer into a span object. /// The length is limited by the length of src and dst, whichever is smaller. diff --git a/include/srsran/adt/interval.h b/include/srsran/adt/interval.h index 9e8becb666..8675ff8cf9 100644 --- a/include/srsran/adt/interval.h +++ b/include/srsran/adt/interval.h @@ -53,9 +53,9 @@ class interval srsran_assert(start_ <= stop_, "Invalid interval [{}, {})", start_, stop_); } - T start() const { return start_; } + constexpr T start() const { return start_; } - T stop() const { return stop_; } + constexpr T stop() const { return stop_; } bool empty() const { return not RightClosed and stop_ == start_; } diff --git a/include/srsran/adt/optional.h b/include/srsran/adt/optional.h index 14700e844f..53a960853a 100644 --- a/include/srsran/adt/optional.h +++ b/include/srsran/adt/optional.h @@ -26,393 +26,11 @@ #include #include -namespace srsran { - -/// Tag to disambiguate optional ctor overloads. Introduced only in C++17. -struct in_place_t {}; - -/// Empty class type to represent empty optional objects. -struct nullopt_t { - explicit constexpr nullopt_t(int /**/) {} -}; - -/// Initializer for empty optional objects. -constexpr nullopt_t nullopt{0}; - -namespace detail { - -struct non_trivial_oper_tag {}; - -/// Storage of an optional internal value and empty/has_value flag. -template -struct base_optional_storage { - using storageT = std::remove_const_t; - - // Storage for trivially destructible type. - template ::value> - union storage_type { - constexpr storage_type() noexcept : dummy() {} - template - explicit storage_type(in_place_t, Args&&... args) : val(std::forward(args)...) - { - } - - char dummy; - U val; - }; - - // Storage for non-trivially destructible type. - template - union storage_type { - constexpr storage_type() noexcept : dummy() {} - template - explicit storage_type(in_place_t, Args&&... args) : val(std::forward(args)...) - { - } - - // User-defined destructor in case T is not trivially destructible. - ~storage_type() {} - - char dummy; - U val; - }; - - constexpr base_optional_storage() noexcept : payload(), has_val(false) {} - template - explicit base_optional_storage(in_place_t tag, Args&&... args) : - payload(tag, std::forward(args)...), has_val(true) - { - } - explicit base_optional_storage(const base_optional_storage& other, non_trivial_oper_tag /**/) noexcept : - has_val(other.has_val) - { - if (other.has_val) { - construct_(other.payload.val); - } - } - explicit base_optional_storage(base_optional_storage&& other, non_trivial_oper_tag /**/) noexcept : - has_val(other.has_val) - { - if (other.has_val) { - construct_(std::move(other.payload.val)); - } - } - - base_optional_storage(const base_optional_storage&) = default; - base_optional_storage(base_optional_storage&&) noexcept = default; - base_optional_storage& operator=(const base_optional_storage&) = default; - base_optional_storage& operator=(base_optional_storage&&) noexcept = default; - - template - constexpr void construct_(Args&&... args) noexcept(std::is_nothrow_constructible::value) - { - new (&payload.val) storageT(std::forward(args)...); - has_val = true; - } - - constexpr void destroy_() noexcept - { - has_val = false; - payload.val.~storageT(); - } - - constexpr void reset_() noexcept - { - if (has_val) { - destroy_(); - } - } - - constexpr void copy_assign_(const base_optional_storage& other) - { - if (this == &other) { - return; - } - if (other.has_val and has_val) { - this->payload.val = other.payload.val; - } else { - if (other.has_val) { - this->construct_(other.payload.val); - } else { - this->reset_(); - } - } - } - - constexpr void move_assign_(base_optional_storage&& other) noexcept( - std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) - { - if (other.has_val and has_val) { - this->payload.val = std::move(other.payload.val); - } else { - if (other.has_val) { - this->construct_(std::move(other.payload.val)); - } else { - this->reset_(); - } - } - } - - storage_type payload; - bool has_val = false; -}; - -/// Specialization of optional storage based on triviality traits of dtor/move/copy. -template ::value, - bool isTriviallyCopyable = - std::is_trivially_copy_constructible::value and std::is_trivially_copy_assignable::value, - bool isTriviallyMovable = - std::is_trivially_move_constructible::value and std::is_trivially_move_assignable::value> -class optional_storage; - -// Specialization for trivial dtor/copy/move. -template -class optional_storage : public base_optional_storage -{ -public: - using base_optional_storage::base_optional_storage; -}; - -// Specialization for non-trivial copy. -template -class optional_storage : public base_optional_storage -{ -public: - using base_optional_storage::base_optional_storage; - - optional_storage() = default; - ~optional_storage() = default; - optional_storage(const optional_storage& other) noexcept(std::is_nothrow_copy_constructible::value) : - base_optional_storage(other, non_trivial_oper_tag{}) - { - } - optional_storage(optional_storage&&) noexcept = default; - optional_storage& operator=(optional_storage&&) noexcept = default; - - optional_storage& operator=(const optional_storage& other) - { - this->copy_assign_(other); - return *this; - } -}; - -// Specialization for non-trivial move. -template -class optional_storage : public base_optional_storage -{ -public: - using base_optional_storage::base_optional_storage; - - optional_storage() = default; - ~optional_storage() = default; - optional_storage(const optional_storage&) = default; - optional_storage(optional_storage&& other) noexcept(std::is_nothrow_move_constructible::value) : - base_optional_storage(std::move(other), non_trivial_oper_tag{}) - { - } - optional_storage& operator=(const optional_storage&) = default; - - constexpr optional_storage& operator=(optional_storage&& other) noexcept( - std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) - { - this->move_assign_(std::move(other)); - return *this; - } -}; - -// Specialization for non-trivial move and copy. -template -class optional_storage : public base_optional_storage -{ -public: - using base_optional_storage::base_optional_storage; - - optional_storage() = default; - ~optional_storage() = default; - optional_storage(const optional_storage& other) noexcept(std::is_nothrow_copy_constructible::value) : - base_optional_storage(other, non_trivial_oper_tag{}) - { - } - optional_storage(optional_storage&& other) noexcept(std::is_nothrow_move_constructible::value) : - base_optional_storage(std::move(other), non_trivial_oper_tag{}) - { - } - - constexpr optional_storage& operator=(const optional_storage& other) - { - this->copy_assign_(other); - return *this; - } - - constexpr optional_storage& operator=(optional_storage&& other) noexcept( - std::is_nothrow_move_constructible::value and std::is_nothrow_move_assignable::value) - { - this->move_assign_(std::move(other)); - return *this; - } -}; - -// Specialization for non-trivial dtor. -template -class optional_storage : public optional_storage -{ -public: - using optional_storage::optional_storage; - optional_storage() = default; - optional_storage(const optional_storage&) = default; - optional_storage(optional_storage&&) noexcept(std::is_nothrow_move_constructible::value) = default; - optional_storage& operator=(const optional_storage&) = default; - optional_storage& operator=(optional_storage&&) noexcept(std::is_nothrow_move_assignable::value) = default; - - ~optional_storage() { this->reset_(); } -}; - -} // namespace detail - -/// \brief Optional objects. -/// -/// An instance of this class may either contain a value of type \c T (and has_value() == true) or be empty -/// (and has_value() == false). -/// -/// The class handles the safe construction, destruction, move and copy of the contained object. -/// \tparam T Type of stored object. -template -class optional -{ - // SFINAE helpers. - template - using is_self = std::is_same, std::remove_const_t>>; - -public: - using value_type = T; - - constexpr optional() = default; - constexpr optional(nullopt_t /**/) : optional() {} - template ::value, int> = 0> - constexpr optional(U&& u) : storage(in_place_t{}, std::forward(u)) - { - } - - /// Checks the state of the optional. - /// return true if the optional is not empty. False otherwise. - constexpr bool has_value() const noexcept { return storage.has_val; } - constexpr explicit operator bool() const noexcept { return has_value(); } - - constexpr T& value() & noexcept - { - srsran_assert(has_value(), "Invalid optional access"); - return storage.payload.val; - } - constexpr T&& value() && noexcept - { - srsran_assert(has_value(), "Invalid optional access"); - return std::move(storage.payload.val); - } - constexpr const T& value() const& noexcept - { - srsran_assert(has_value(), "Invalid optional access"); - return storage.payload.val; - } - constexpr const T&& value() const&& noexcept - { - srsran_assert(has_value(), "Invalid optional access"); - return std::move(storage.payload.val); - } - constexpr T* operator->() noexcept { return &value(); } - constexpr const T* operator->() const noexcept { return &value(); } - constexpr T& operator*() & noexcept { return value(); } - constexpr T&& operator*() && noexcept { return value(); } - constexpr const T& operator*() const& noexcept { return value(); } - constexpr const T&& operator*() const&& noexcept { return value(); } - - optional& operator=(nullopt_t /**/) noexcept - { - reset(); - return *this; - } - - /// Constructs a new object of type T inside the optional's storage, and sets the optional state to not empty. - template - constexpr T& emplace(Args&&... args) noexcept(std::is_nothrow_constructible::value) - { - storage.reset_(); - storage.construct_(std::forward(args)...); - return value(); - } - - /// Resets the internal value stored in optional and sets the optional state to empty. - constexpr void reset() noexcept { storage.reset_(); } - -private: - detail::optional_storage storage; -}; - -template -bool operator==(const optional& lhs, const optional& rhs) noexcept -{ - return lhs.has_value() == rhs.has_value() and (not lhs.has_value() or lhs.value() == rhs.value()); -} - -template -bool operator==(const optional& lhs, const T& rhs) noexcept -{ - return lhs.has_value() and lhs.value() == rhs; -} - -template -bool operator==(const T& lhs, const optional& rhs) noexcept -{ - return rhs.has_value() and lhs == rhs.value(); -} - -template -bool operator!=(const optional& lhs, const optional& rhs) noexcept -{ - return not(lhs == rhs); -} - -template -bool operator!=(const optional& lhs, const T& rhs) noexcept -{ - return not(lhs == rhs); -} - -template -bool operator!=(const T& lhs, const optional& rhs) noexcept -{ - return not(lhs == rhs); -} - -template -bool operator<(const optional& lhs, const optional& rhs) noexcept -{ - return rhs.has_value() and ((lhs.has_value() and lhs.value() < rhs.value()) or (not lhs.has_value())); -} - -} // namespace srsran +//: TODO: This header file will get eventually moved to support after our optional implementation goes away. namespace fmt { -/// Default formatter for optional -template -struct formatter> { - template - auto parse(ParseContext& ctx) -> decltype(ctx.begin()) - { - return ctx.begin(); - } - - template - auto format(const srsran::optional& optval, FormatContext& ctx) -> decltype(std::declval().out()) - { - if (!optval.has_value()) { - return format_to(ctx.out(), "{{na}}"); - } - return format_to(ctx.out(), "{}", optval.value()); - } -}; - +/// Default formatter for std::optional template struct formatter> { template diff --git a/include/srsran/adt/tiny_optional.h b/include/srsran/adt/tiny_optional.h index 3289637b1e..c9f02e3c7d 100644 --- a/include/srsran/adt/tiny_optional.h +++ b/include/srsran/adt/tiny_optional.h @@ -25,7 +25,9 @@ /// \file /// \brief Implementation of tiny_optional class as a more memory-efficient alternative to optional. -#include "srsran/adt/optional.h" +#include "srsran/support/srsran_assert.h" +#include +#include namespace srsran { @@ -33,17 +35,17 @@ namespace srsran { template struct tiny_optional_traits; -/// \brief Specialization for tiny_optional, where an instance of T equal to AbsentValue corresponds -/// to an empty optional. +/// \brief Specialization for tiny_optional, where an instance of T equal to AbsentValue corresponds to +/// an empty optional. template struct tiny_optional_traits { - constexpr static T empty_value() noexcept { return AbsentValue; } + static constexpr T empty_value() noexcept { return AbsentValue; } }; /// \brief Specialization for tiny_optional for unique_ptr, where nullptr corresponds to an empty optional. template struct tiny_optional_traits> { - constexpr static std::unique_ptr empty_value() noexcept { return nullptr; } + static constexpr std::unique_ptr empty_value() noexcept { return nullptr; } }; namespace detail { @@ -54,13 +56,13 @@ class base_tiny_optional { using traits = tiny_optional_traits; - // SFINAE helpers. + /// SFINAE helpers. template using is_self = std::is_same, std::remove_const_t>>; public: constexpr base_tiny_optional() = default; - constexpr base_tiny_optional(nullopt_t /**/) : base_tiny_optional() {} + constexpr base_tiny_optional(std::nullopt_t /**/) : base_tiny_optional() {} template ::value, int> = 0> constexpr base_tiny_optional(U&& u) : val(std::forward(u)) { @@ -93,16 +95,17 @@ class base_tiny_optional srsran_assert(has_value(), "Invalid optional access"); return std::move(this->val); } + constexpr T* operator->() noexcept { return &value(); } constexpr const T* operator->() const noexcept { return &value(); } constexpr T& operator*() & noexcept { return value(); } - constexpr T&& operator*() && noexcept { return value(); } + constexpr T&& operator*() && noexcept { return std::move(value()); } constexpr const T& operator*() const& noexcept { return value(); } - constexpr const T&& operator*() const&& noexcept { return value(); } + constexpr const T&& operator*() const&& noexcept { return std::move(value()); } /// Constructs a new object of type T inside the optional's storage, and sets the optional state to not empty. template - constexpr T& emplace(Args2&&... args) noexcept(std::is_nothrow_constructible::value) + constexpr T& emplace(Args2&&... args) noexcept(std::is_nothrow_constructible_v) { this->val = T(std::forward(args)...); return value(); @@ -113,7 +116,7 @@ class base_tiny_optional return has_value() == rhs.has_value() and (has_value() ? val == rhs.val : true); } - constexpr bool operator!=(const base_tiny_optional& rhs) const { return !(rhs == *this); } + constexpr bool operator!=(const base_tiny_optional& rhs) const { return not(rhs == *this); } private: T val = traits::empty_value(); @@ -133,11 +136,11 @@ using is_complete_type = decltype(is_complete_impl(std::declval())); template class tiny_optional : public std::conditional_t>::value, detail::base_tiny_optional, - optional> + std::optional> { using base_type = typename std::conditional_t>::value, detail::base_tiny_optional, - optional>; + std::optional>; public: using value_type = T; diff --git a/include/srsran/adt/variant.h b/include/srsran/adt/variant.h deleted file mode 100644 index 50498aff1e..0000000000 --- a/include/srsran/adt/variant.h +++ /dev/null @@ -1,75 +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 "variant/variant.hpp" - -namespace srsran { - -template -using variant = mpark::variant; - -/// \brief Reads the value of the variant given the index or the type (if the type is unique). -/// \param[in] v Variant to examine. -/// \return Reference to the value stored in the variant. -template -inline constexpr const T& variant_get(const variant& v) -{ - return mpark::get(v); -} - -/// \brief Reads the value of the variant given the index or the type (if the type is unique). -/// \param[in] v Variant to examine. -/// \return Non-const Reference to the value stored in the variant. -template -inline constexpr T& variant_get(variant& v) -{ - return mpark::get(v); -} - -/// \brief Checks if the variant v holds the alternative T. -/// \param[in] v Variant to examine. -/// \return True if the variant currently holds the alternative T, false otherwise. -template -inline constexpr bool variant_holds_alternative(const variant& v) noexcept -{ - return mpark::holds_alternative(v); -} - -/// \brief Reads the value of the variant given the index. -/// \param[in] v Variant to examine. -/// \return Reference to the value stored in the variant. -template -inline constexpr const auto& variant_get(const variant& v) -{ - return mpark::get(v); -} - -/// Apply a visitor to the variant. -template -inline constexpr auto variant_visit(Visitor&& visitor, Vs&&... vs) -{ - return mpark::visit(std::forward(visitor), std::forward(vs)...); -} - -} // namespace srsran diff --git a/include/srsran/asn1/asn1_utils.h b/include/srsran/asn1/asn1_utils.h index bf4bcd11f0..a7e59a680b 100644 --- a/include/srsran/asn1/asn1_utils.h +++ b/include/srsran/asn1/asn1_utils.h @@ -22,12 +22,12 @@ #pragma once -#include "srsran/adt/any.h" #include "srsran/adt/byte_buffer.h" #include "srsran/adt/span.h" #include "srsran/srslog/srslog.h" #include "srsran/support/compiler.h" #include "srsran/support/srsran_assert.h" +#include #include #include #include @@ -1398,18 +1398,18 @@ struct choice_buffer_ptr { return *this; } - bool has_value() const { return not obj.empty(); } + bool has_value() const { return obj.has_value(); } template bool holds_choice() const { - return srsran::any_cast(&obj) != nullptr; + return std::any_cast(&obj) != nullptr; } template T& get() { - T* ret = srsran::any_cast(&obj); + T* ret = std::any_cast(&obj); srsran_assert(ret != nullptr, "Invalid choice type"); return *ret; } @@ -1417,15 +1417,15 @@ struct choice_buffer_ptr { template const T& get() const { - const T* ret = srsran::any_cast(&obj); + const T* ret = std::any_cast(&obj); srsran_assert(ret != nullptr, "Invalid choice type"); return *ret; } - void reset() { obj.clear(); } + void reset() { obj.reset(); } private: - srsran::any obj; + std::any obj; }; /********************* diff --git a/include/srsran/cu_up/cu_up_configuration.h b/include/srsran/cu_up/cu_up_configuration.h index 906162fdc9..e9f9edf93e 100644 --- a/include/srsran/cu_up/cu_up_configuration.h +++ b/include/srsran/cu_up/cu_up_configuration.h @@ -24,8 +24,8 @@ #include "srsran/cu_up/cu_up_executor_pool.h" #include "srsran/e1ap/common/e1ap_common.h" -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" #include "srsran/e1ap/cu_up/e1ap_cu_up.h" +#include "srsran/e1ap/gateways/e1_connection_client.h" #include "srsran/f1u/cu_up/f1u_gateway.h" #include "srsran/gtpu/gtpu_config.h" #include "srsran/gtpu/ngu_gateway.h" @@ -73,8 +73,8 @@ struct n3_interface_config { }; struct e1ap_config_params { - e1ap_connection_client* e1ap_conn_client = nullptr; - e1ap_connection_manager* e1ap_conn_mng = nullptr; + e1_connection_client* e1_conn_client = nullptr; + e1ap_connection_manager* e1ap_conn_mng = nullptr; }; /// Configuration passed to CU-UP. diff --git a/include/srsran/du_high/du_high_configuration.h b/include/srsran/du_high/du_high_configuration.h index c750723d4f..d1fd6886bd 100644 --- a/include/srsran/du_high/du_high_configuration.h +++ b/include/srsran/du_high/du_high_configuration.h @@ -15,6 +15,7 @@ #include "srsran/mac/mac_config.h" #include "srsran/pcap/dlt_pcap.h" #include "srsran/pcap/rlc_pcap.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/rlc/rlc_metrics.h" #include "srsran/scheduler/config/scheduler_expert_config.h" #include "srsran/scheduler/scheduler_metrics.h" @@ -40,7 +41,7 @@ struct du_high_configuration { e2_connection_client* e2_client = nullptr; e2_du_metrics_interface* e2_du_metric_iface = nullptr; std::string gnb_du_name; - uint64_t gnb_du_id; + gnb_du_id_t gnb_du_id; transport_layer_address du_bind_addr; std::vector cells; std::map srbs; diff --git a/include/srsran/du_low/du_low.h b/include/srsran/du_low/du_low.h index fb44a2f85e..3cf5e12c84 100644 --- a/include/srsran/du_low/du_low.h +++ b/include/srsran/du_low/du_low.h @@ -22,6 +22,8 @@ #pragma once +#include "srsran/adt/span.h" + namespace srsran { class upper_phy; @@ -38,6 +40,9 @@ class du_low /// Returns the upper PHY for the given cell of this DU low. virtual upper_phy& get_upper_phy(unsigned cell_id) = 0; + + /// Returns a span of the upper PHYs managed by this DU low. + virtual span get_all_upper_phys() = 0; }; } // namespace srsran diff --git a/include/srsran/du_manager/du_manager_params.h b/include/srsran/du_manager/du_manager_params.h index 59454cedd5..8c965476e4 100644 --- a/include/srsran/du_manager/du_manager_params.h +++ b/include/srsran/du_manager/du_manager_params.h @@ -31,6 +31,7 @@ #include "srsran/f1u/du/f1u_gateway.h" #include "srsran/mac/mac.h" #include "srsran/pcap/rlc_pcap.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/rlc/rlc_metrics.h" #include "srsran/scheduler/config/scheduler_expert_config.h" #include @@ -44,7 +45,7 @@ namespace srs_du { struct du_manager_params { struct ran_params { std::string gnb_du_name; - uint64_t gnb_du_id; + gnb_du_id_t gnb_du_id; uint8_t rrc_version; transport_layer_address du_bind_addr; std::vector cells; diff --git a/include/srsran/e1ap/common/e1ap_types.h b/include/srsran/e1ap/common/e1ap_types.h index 15464e47ae..519785b303 100644 --- a/include/srsran/e1ap/common/e1ap_types.h +++ b/include/srsran/e1ap/common/e1ap_types.h @@ -42,13 +42,13 @@ namespace srsran { constexpr static uint64_t MAX_NOF_CU_CP_E1AP_UES = ((uint64_t)1 << 32); enum class gnb_cu_cp_ue_e1ap_id_t : uint64_t { min = 0, max = MAX_NOF_CU_CP_E1AP_UES - 1, invalid = 0x1ffffffff }; -constexpr inline uint64_t gnb_cu_cp_ue_e1ap_id_to_uint(gnb_cu_cp_ue_e1ap_id_t id) +constexpr uint64_t gnb_cu_cp_ue_e1ap_id_to_uint(gnb_cu_cp_ue_e1ap_id_t id) { return static_cast(id); } /// Convert integer to GNB-CU-CP-UE-E1AP-ID type. -constexpr inline gnb_cu_cp_ue_e1ap_id_t int_to_gnb_cu_cp_ue_e1ap_id(uint64_t idx) +constexpr gnb_cu_cp_ue_e1ap_id_t int_to_gnb_cu_cp_ue_e1ap_id(uint64_t idx) { return static_cast(idx); } @@ -58,13 +58,13 @@ constexpr inline gnb_cu_cp_ue_e1ap_id_t int_to_gnb_cu_cp_ue_e1ap_id(uint64_t idx constexpr static uint64_t MAX_NOF_CU_UP_E1AP_UES = ((uint64_t)1 << 32); enum class gnb_cu_up_ue_e1ap_id_t : uint64_t { min = 0, max = MAX_NOF_CU_CP_E1AP_UES - 1, invalid = 0x1ffffffff }; -constexpr inline uint64_t gnb_cu_up_ue_e1ap_id_to_uint(gnb_cu_up_ue_e1ap_id_t id) +constexpr uint64_t gnb_cu_up_ue_e1ap_id_to_uint(gnb_cu_up_ue_e1ap_id_t id) { return static_cast(id); } /// Convert integer to GNB-DU-UE-E1AP-ID type. -constexpr inline gnb_cu_up_ue_e1ap_id_t int_to_gnb_cu_up_ue_e1ap_id(uint64_t idx) +constexpr gnb_cu_up_ue_e1ap_id_t int_to_gnb_cu_up_ue_e1ap_id(uint64_t idx) { return static_cast(idx); } @@ -296,10 +296,10 @@ struct e1ap_pdu_session_res_to_modify_item { std::optional security_ind; std::optional pdu_session_res_dl_ambr; std::optional ng_ul_up_tnl_info; - optional pdu_session_data_forwarding_info_request; - optional pdu_session_data_forwarding_info; - optional pdu_session_inactivity_timer; - optional network_instance; + std::optional pdu_session_data_forwarding_info_request; + std::optional pdu_session_data_forwarding_info; + std::optional pdu_session_inactivity_timer; + std::optional network_instance; slotted_id_vector drb_to_setup_list_ng_ran; slotted_id_vector drb_to_modify_list_ng_ran; std::vector drb_to_rem_list_ng_ran; @@ -318,18 +318,18 @@ struct e1ap_drb_modified_item_ng_ran { std::vector ul_up_transport_params; slotted_id_vector flow_setup_list; slotted_id_vector flow_failed_list; - optional pdcp_sn_status_info; + std::optional pdcp_sn_status_info; }; struct e1ap_pdu_session_resource_modified_item { pdu_session_id_t pdu_session_id = pdu_session_id_t::invalid; - optional ng_dl_up_tnl_info; + std::optional ng_dl_up_tnl_info; slotted_id_vector drb_setup_list_ng_ran; slotted_id_vector drb_failed_list_ng_ran; slotted_id_vector drb_modified_list_ng_ran; slotted_id_vector drb_failed_to_modify_list_ng_ran; - optional security_result; - optional pdu_session_data_forwarding_info_resp; + std::optional security_result; + std::optional pdu_session_data_forwarding_info_resp; }; } // namespace srsran diff --git a/include/srsran/e1ap/cu_up/e1ap_cu_up_factory.h b/include/srsran/e1ap/cu_up/e1ap_cu_up_factory.h index a5e4706362..d21c6af2cc 100644 --- a/include/srsran/e1ap/cu_up/e1ap_cu_up_factory.h +++ b/include/srsran/e1ap/cu_up/e1ap_cu_up_factory.h @@ -23,7 +23,7 @@ #pragma once #include "e1ap_cu_up.h" -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" +#include "srsran/e1ap/gateways/e1_connection_client.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/timers.h" #include @@ -32,10 +32,10 @@ namespace srsran { namespace srs_cu_up { /// Creates an instance of an E1AP interface, notifying outgoing packets on the specified listener object. -std::unique_ptr create_e1ap(e1ap_connection_client& e1ap_client_handler_, - e1ap_cu_up_notifier& cu_up_notifier_, - timer_manager& timers_, - task_executor& cu_up_exec_); +std::unique_ptr create_e1ap(e1_connection_client& e1_client_handler_, + e1ap_cu_up_notifier& cu_up_notifier_, + timer_manager& timers_, + task_executor& cu_up_exec_); } // namespace srs_cu_up } // namespace srsran diff --git a/include/srsran/e1ap/cu_up/e1ap_connection_client.h b/include/srsran/e1ap/gateways/e1_connection_client.h similarity index 93% rename from include/srsran/e1ap/cu_up/e1ap_connection_client.h rename to include/srsran/e1ap/gateways/e1_connection_client.h index 64087a7913..86329555d2 100644 --- a/include/srsran/e1ap/cu_up/e1ap_connection_client.h +++ b/include/srsran/e1ap/gateways/e1_connection_client.h @@ -28,11 +28,11 @@ namespace srsran { namespace srs_cu_up { -/// Interface used by the CU-UP to establish a new connection with a CU-CP. -class e1ap_connection_client +/// Interface used by the CU-UP to establish a new connection with a CU-CP via the E1 interface. +class e1_connection_client { public: - virtual ~e1ap_connection_client() = default; + virtual ~e1_connection_client() = default; /// Establish a new connection with a CU-CP. /// diff --git a/apps/gnb/adapters/e1ap_gateway_local_connector.h b/include/srsran/e1ap/gateways/e1_connection_server.h similarity index 54% rename from apps/gnb/adapters/e1ap_gateway_local_connector.h rename to include/srsran/e1ap/gateways/e1_connection_server.h index 4ec3b5b450..6aea152a88 100644 --- a/apps/gnb/adapters/e1ap_gateway_local_connector.h +++ b/include/srsran/e1ap/gateways/e1_connection_server.h @@ -22,28 +22,27 @@ #pragma once -#include "srsran/cu_cp/cu_cp_e1_handler.h" -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" +#include "srsran/adt/optional.h" namespace srsran { +namespace srs_cu_cp { -class dlt_pcap; +class cu_cp_e1_handler; -/// Implementation of a CU-UP and CU-CP E1AP gateway for the case that the CU-UP and CU-CP are co-located. -class e1ap_gateway_local_connector final : public srs_cu_up::e1ap_connection_client +/// Connection server responsible for handling new connection requests/drops coming from CU-UPs via the E1 interface +/// and converting them into CU-CP commands. +class e1_connection_server { public: - explicit e1ap_gateway_local_connector(dlt_pcap& e1ap_pcap_writer_); + virtual ~e1_connection_server() = default; - void attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_cp_du_mng_); + /// Attach a CU-CP handler to the E1 connection server. + virtual void attach_cu_cp(cu_cp_e1_handler& cu_e1_handler_) = 0; - // CU-UP interface. - std::unique_ptr - handle_cu_up_connection_request(std::unique_ptr cu_up_rx_pdu_notifier) override; - -private: - dlt_pcap& e1ap_pcap_writer; - srs_cu_cp::cu_cp_e1_handler* cu_cp_cu_up_mng = nullptr; + /// \brief Get port on which the E1 Server is listening for new connections. + /// \return The port number on which the E1 Server is listening for new connections. + virtual std::optional get_listen_port() const = 0; }; +} // namespace srs_cu_cp } // namespace srsran diff --git a/include/srsran/e1ap/gateways/e1_local_connector_factory.h b/include/srsran/e1ap/gateways/e1_local_connector_factory.h new file mode 100644 index 0000000000..f3880cf109 --- /dev/null +++ b/include/srsran/e1ap/gateways/e1_local_connector_factory.h @@ -0,0 +1,44 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/e1ap/gateways/e1_connection_client.h" +#include "srsran/e1ap/gateways/e1_connection_server.h" + +namespace srsran { + +class dlt_pcap; + +class e1_local_connector : public srs_cu_up::e1_connection_client, public srs_cu_cp::e1_connection_server +{}; + +struct e1_local_connector_config { + /// PCAP writer for the E1AP messages. + dlt_pcap& pcap; +}; + +/// Creates a local connector between CU-CP and CU-UP E1 interfaces, avoiding the need to pack/unpack the exchanged +/// E1AP PDUs or any socket send/recv. +std::unique_ptr create_e1_local_connector(const e1_local_connector_config& cfg); + +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/e2/e2sm/e2sm.h b/include/srsran/e2/e2sm/e2sm.h index 49e6955157..dc80f50a28 100644 --- a/include/srsran/e2/e2sm/e2sm.h +++ b/include/srsran/e2/e2sm/e2sm.h @@ -23,12 +23,12 @@ #pragma once #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" #include "srsran/asn1/e2ap/e2ap.h" #include "srsran/asn1/e2sm/e2sm_kpm_ies.h" #include "srsran/asn1/e2sm/e2sm_rc_ies.h" #include "srsran/e2/e2_messages.h" #include "srsran/support/async/async_task.h" +#include namespace srsran { @@ -41,28 +41,28 @@ struct e2sm_event_trigger_definition { }; struct e2sm_action_definition { - e2sm_service_model_t service_model; - variant action_definition; + e2sm_service_model_t service_model; + std::variant action_definition; }; struct e2sm_ric_control_request { - e2sm_service_model_t service_model; - bool ric_call_process_id_present = false; - bool ric_ctrl_ack_request_present = false; - uint64_t ric_call_process_id; - variant request_ctrl_hdr; - variant request_ctrl_msg; - bool ric_ctrl_ack_request; + e2sm_service_model_t service_model; + bool ric_call_process_id_present = false; + bool ric_ctrl_ack_request_present = false; + uint64_t ric_call_process_id; + std::variant request_ctrl_hdr; + std::variant request_ctrl_msg; + bool ric_ctrl_ack_request; }; struct e2sm_ric_control_response { - e2sm_service_model_t service_model; - bool success; - bool ric_call_process_id_present = false; - bool ric_ctrl_outcome_present = false; - uint64_t ric_call_process_id; - variant ric_ctrl_outcome; - asn1::e2ap::cause_c cause; + e2sm_service_model_t service_model; + bool success; + bool ric_call_process_id_present = false; + bool ric_ctrl_outcome_present = false; + uint64_t ric_call_process_id; + std::variant ric_ctrl_outcome; + asn1::e2ap::cause_c cause; }; /// RIC control action executor maps an control action request to the proper stack functions. diff --git a/include/srsran/f1ap/cu_cp/du_setup_notifier.h b/include/srsran/f1ap/cu_cp/du_setup_notifier.h index 3c57c28284..1bfe4328f0 100644 --- a/include/srsran/f1ap/cu_cp/du_setup_notifier.h +++ b/include/srsran/f1ap/cu_cp/du_setup_notifier.h @@ -31,6 +31,7 @@ #include "srsran/ran/pci.h" #include #include +#include #include namespace srsran { @@ -61,10 +62,10 @@ struct du_setup_result { std::string cause_str; }; - variant result; + std::variant result; /// Whether the DU setup request was accepted by the CU-CP. - bool is_accepted() const { return variant_holds_alternative(result); } + bool is_accepted() const { return std::holds_alternative(result); } }; /// \brief Interface used to handle F1AP interface management procedures as defined in TS 38.473 section 8.2. diff --git a/include/srsran/f1ap/du/f1ap_du_connection_manager.h b/include/srsran/f1ap/du/f1ap_du_connection_manager.h index 97125bf3da..b5861c8b9c 100644 --- a/include/srsran/f1ap/du/f1ap_du_connection_manager.h +++ b/include/srsran/f1ap/du/f1ap_du_connection_manager.h @@ -25,6 +25,7 @@ #include "srsran/adt/byte_buffer.h" #include "srsran/ran/carrier_configuration.h" #include "srsran/ran/duplex_mode.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/ran/nr_cgi.h" #include "srsran/ran/pci.h" #include "srsran/support/async/async_task.h" @@ -48,7 +49,7 @@ struct f1_cell_setup_params { /// \brief Message that initiates a F1 Setup procedure. struct f1_setup_request_message { - uint64_t gnb_du_id; + gnb_du_id_t gnb_du_id; std::string gnb_du_name; uint8_t rrc_version; std::vector served_cells; diff --git a/include/srsran/f1u/cu_up/f1u_gateway.h b/include/srsran/f1u/cu_up/f1u_gateway.h index a6c51f9d52..6bc32bdbcb 100644 --- a/include/srsran/f1u/cu_up/f1u_gateway.h +++ b/include/srsran/f1u/cu_up/f1u_gateway.h @@ -27,7 +27,7 @@ #include "srsran/f1u/cu_up/f1u_tx_pdu_notifier.h" #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" -#include "srsran/support/timers.h" +#include "srsran/support/executors/task_executor.h" namespace srsran { @@ -66,9 +66,7 @@ class f1u_cu_up_gateway : public srs_cu_up::f1u_bearer_disconnector const srs_cu_up::f1u_config& config, const up_transport_layer_info& ul_up_tnl_info, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, - task_executor& ul_exec, - timer_factory ue_dl_timer_factory, - unique_timer& ue_inactivity_timer) = 0; + task_executor& ul_exec) = 0; virtual void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) = 0; diff --git a/include/srsran/f1u/cu_up/split_connector/f1u_split_connector.h b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector.h new file mode 100644 index 0000000000..893903af91 --- /dev/null +++ b/include/srsran/f1u/cu_up/split_connector/f1u_split_connector.h @@ -0,0 +1,150 @@ +/* + * + * 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/f1u/cu_up/f1u_bearer_logger.h" +#include "srsran/f1u/cu_up/f1u_gateway.h" +#include "srsran/gtpu/gtpu_config.h" +#include "srsran/gtpu/gtpu_demux.h" +#include "srsran/gtpu/gtpu_tunnel_common_tx.h" +#include "srsran/gtpu/gtpu_tunnel_nru.h" +#include "srsran/gtpu/gtpu_tunnel_nru_rx.h" +#include "srsran/gtpu/ngu_gateway.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/srslog/srslog.h" +#include +#include +#include + +namespace srsran::srs_cu_up { + +class gtpu_tx_udp_gw_adapter; +class gtpu_rx_f1u_adapter; +class network_gateway_data_gtpu_demux_adapter; + +/// \brief Object used to represent a bearer at the CU F1-U gateway +/// On the co-located case this is done by connecting both entities directly. +/// +/// It will keep a notifier to the DU NR-U RX and provide the methods to pass +/// an SDU to it. +class f1u_split_gateway_cu_bearer final : public f1u_cu_up_gateway_bearer +{ +public: + f1u_split_gateway_cu_bearer(uint32_t ue_index_, + drb_id_t drb_id, + const up_transport_layer_info& ul_tnl_info_, + f1u_cu_up_gateway_bearer_rx_notifier& cu_rx_, + ngu_tnl_pdu_session& udp_session, + task_executor& ul_exec_, + srs_cu_up::f1u_bearer_disconnector& disconnector_); + + ~f1u_split_gateway_cu_bearer() override; + + void stop() override; + + void on_new_pdu(nru_dl_message msg) override + { + if (tunnel_tx == nullptr) { + logger.log_debug("DL GTPU tunnel not connected. Discarding SDU."); + return; + } + tunnel_tx->handle_sdu(std::move(msg)); + } + + void attach_tunnel_rx(std::unique_ptr tunnel_rx_) + { + tunnel_rx = std::move(tunnel_rx_); + } + + void attach_tunnel_tx(std::unique_ptr tunnel_tx_) + { + tunnel_tx = std::move(tunnel_tx_); + } + + std::unique_ptr gtpu_to_network_adapter; + std::unique_ptr gtpu_to_f1u_adapter; + + gtpu_tunnel_common_rx_upper_layer_interface* get_tunnel_rx_interface() { return tunnel_rx.get(); } + + /// Holds the RX executor associated with the F1-U bearer. + task_executor& ul_exec; + uint32_t ue_index; + +private: + bool stopped = false; + srs_cu_up::f1u_bearer_logger logger; + srs_cu_up::f1u_bearer_disconnector& disconnector; + up_transport_layer_info ul_tnl_info; + std::unique_ptr tunnel_rx; + std::unique_ptr tunnel_tx; + +public: + /// Holds notifier that will point to NR-U bearer on the UL path + f1u_cu_up_gateway_bearer_rx_notifier& cu_rx; + + /// Holds the DL UP TNL info associated with the F1-U bearer. + std::optional dl_tnl_info; +}; + +/// \brief Object used to connect the DU and CU-UP F1-U bearers +/// On the co-located case this is done by connecting both entities directly. +/// +/// Note that CU and DU bearer creation and removal can be performed from different threads and are therefore +/// protected by a common mutex. +class f1u_split_connector final : public f1u_cu_up_gateway +{ +public: + f1u_split_connector(ngu_gateway* udp_gw_, gtpu_demux* demux_, dlt_pcap& gtpu_pcap_, uint16_t peer_port_ = GTPU_PORT); + ~f1u_split_connector() override; + + f1u_cu_up_gateway* get_f1u_cu_up_gateway() { return this; } + + std::optional get_bind_port() { return udp_session->get_bind_port(); } + + std::unique_ptr create_cu_bearer(uint32_t ue_index, + drb_id_t drb_id, + const srs_cu_up::f1u_config& config, + const up_transport_layer_info& ul_up_tnl_info, + f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, + task_executor& ul_exec) override; + + void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, + const up_transport_layer_info& dl_up_tnl_info) override; + + void disconnect_cu_bearer(const up_transport_layer_info& ul_up_tnl_info) override; + +private: + srslog::basic_logger& logger_cu; + // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) + std::unordered_map cu_map; + std::mutex map_mutex; // shared mutex for access to cu_map + + uint16_t peer_port; + ngu_gateway* udp_gw; + std::unique_ptr udp_session; + gtpu_demux* demux; + std::unique_ptr gw_data_gtpu_demux_adapter; + dlt_pcap& gtpu_pcap; +}; + +} // namespace srsran::srs_cu_up diff --git a/include/srsran/f1u/du/f1u_gateway.h b/include/srsran/f1u/du/f1u_gateway.h index 8d2306ee2a..4ad2404207 100644 --- a/include/srsran/f1u/du/f1u_gateway.h +++ b/include/srsran/f1u/du/f1u_gateway.h @@ -25,6 +25,7 @@ #include "srsran/f1u/du/f1u_bearer.h" #include "srsran/f1u/du/f1u_config.h" #include "srsran/f1u/du/f1u_tx_pdu_notifier.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" #include "srsran/support/timers.h" @@ -68,6 +69,8 @@ class f1u_du_gateway : public srs_du::f1u_bearer_disconnector srs_du::f1u_du_gateway_bearer_rx_notifier& du_rx, timer_factory timers, task_executor& ue_executor) = 0; + + virtual expected get_du_bind_address(gnb_du_id_t gnb_du_id) = 0; }; } // namespace srsran::srs_du diff --git a/include/srsran/f1u/du/split_connector/f1u_split_connector.h b/include/srsran/f1u/du/split_connector/f1u_split_connector.h new file mode 100644 index 0000000000..415364f2a1 --- /dev/null +++ b/include/srsran/f1u/du/split_connector/f1u_split_connector.h @@ -0,0 +1,228 @@ +/* + * + * 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/f1u/du/f1u_bearer_logger.h" +#include "srsran/f1u/du/f1u_gateway.h" +#include "srsran/gtpu/gtpu_demux.h" +#include "srsran/gtpu/gtpu_tunnel_common_tx.h" +#include "srsran/gtpu/gtpu_tunnel_nru.h" +#include "srsran/gtpu/gtpu_tunnel_nru_factory.h" +#include "srsran/gtpu/gtpu_tunnel_nru_rx.h" +#include "srsran/gtpu/ngu_gateway.h" +#include "srsran/pcap/dlt_pcap.h" +#include "srsran/srslog/srslog.h" +#include +#include + +namespace srsran::srs_du { + +class gtpu_tx_udp_gw_adapter : public gtpu_tunnel_common_tx_upper_layer_notifier +{ +public: + /// \brief Interface for the GTP-U to pass PDUs to the IO gateway + /// \param sdu PDU to be transmitted. + void on_new_pdu(byte_buffer buf, const sockaddr_storage& addr) override + { + if (handler != nullptr) { + handler->handle_pdu(std::move(buf), addr); + } + } + + void connect(srs_cu_up::ngu_tnl_pdu_session& handler_) { handler = &handler_; } + + void disconnect() { handler = nullptr; } + + srs_cu_up::ngu_tnl_pdu_session* handler; +}; + +class gtpu_rx_f1u_adapter : public srsran::gtpu_tunnel_nru_rx_lower_layer_notifier +{ +public: + /// \brief Interface for the GTP-U to pass a SDU (i.e. NR-U DL message) into the lower layer. + /// \param dl_message NR-U DL message with optional T-PDU. + void on_new_sdu(nru_dl_message dl_message) override + { + if (handler != nullptr) { + handler->on_new_pdu(std::move(dl_message)); + } + } + + /// \brief Interface for the GTP-U to pass a SDU (i.e. NR-U UL message) into the lower layer. + /// \param ul_message NR-U UL message with optional T-PDU. + void on_new_sdu(nru_ul_message ul_message) override {} + + void connect(f1u_du_gateway_bearer_rx_notifier& handler_) { handler = &handler_; } + + void disconnect() { handler = nullptr; } + + f1u_du_gateway_bearer_rx_notifier* handler; +}; + +/// Adapter between Network Gateway (Data) and GTP-U demux +class network_gateway_data_gtpu_demux_adapter : public srsran::network_gateway_data_notifier_with_src_addr +{ +public: + network_gateway_data_gtpu_demux_adapter() = default; + ~network_gateway_data_gtpu_demux_adapter() override = default; + + void connect_gtpu_demux(gtpu_demux_rx_upper_layer_interface& gtpu_demux_) { gtpu_demux = >pu_demux_; } + + void on_new_pdu(byte_buffer pdu, const sockaddr_storage& src_addr) override + { + srsran_assert(gtpu_demux != nullptr, "GTP-U handler must not be nullptr"); + gtpu_demux->handle_pdu(std::move(pdu), src_addr); + } + +private: + gtpu_demux_rx_upper_layer_interface* gtpu_demux = nullptr; +}; + +/// \brief Object used to represent a bearer at the CU F1-U gateway +/// On the co-located case this is done by connecting both entities directly. +/// +/// It will keep a notifier to the DU NR-U RX and provide the methods to pass +/// an SDU to it. +class f1u_split_gateway_du_bearer : public f1u_du_gateway_bearer +{ +public: + f1u_split_gateway_du_bearer(uint32_t ue_index, + drb_id_t drb_id, + const up_transport_layer_info& dl_tnl_info_, + srs_du::f1u_du_gateway_bearer_rx_notifier& du_rx_, + const up_transport_layer_info& ul_up_tnl_info_, + srs_du::f1u_bearer_disconnector& disconnector_, + dlt_pcap& gtpu_pcap, + uint16_t peer_port) : + logger("DU-F1-U", {ue_index, drb_id, dl_tnl_info_}), + disconnector(disconnector_), + dl_tnl_info(dl_tnl_info_), + ul_tnl_info(ul_up_tnl_info_), + du_rx(du_rx_) + { + gtpu_to_f1u_adapter.connect(du_rx); + + gtpu_tunnel_nru_creation_message msg{}; + // msg.ue_index = 0; TODO + msg.cfg.rx.local_teid = dl_tnl_info.gtp_teid; + msg.cfg.tx.peer_teid = ul_tnl_info.gtp_teid; + msg.cfg.tx.peer_addr = ul_tnl_info.tp_address.to_string(); + msg.cfg.tx.peer_port = peer_port; + msg.gtpu_pcap = >pu_pcap; + msg.tx_upper = >pu_to_network_adapter; + msg.rx_lower = >pu_to_f1u_adapter; + + tunnel = srsran::create_gtpu_tunnel_nru(msg); + } + + ~f1u_split_gateway_du_bearer() override { stop(); } + + void stop() override { disconnector.remove_du_bearer(dl_tnl_info); } + + void on_new_pdu(nru_ul_message msg) override + { + if (tunnel == nullptr) { + logger.log_debug("DL GTPU tunnel not connected. Discarding SDU."); + return; + } + tunnel->get_tx_lower_layer_interface()->handle_sdu(std::move(msg)); + } + + gtpu_tunnel_common_rx_upper_layer_interface* get_tunnel_rx_interface() + { + return tunnel->get_rx_upper_layer_interface(); + } + + /// Holds the RX executor associated with the F1-U bearer. + // task_executor& dl_exec; + + gtpu_tx_udp_gw_adapter gtpu_to_network_adapter; + gtpu_rx_f1u_adapter gtpu_to_f1u_adapter; + +private: + f1u_bearer_logger logger; + f1u_bearer_disconnector& disconnector; + up_transport_layer_info dl_tnl_info; + up_transport_layer_info ul_tnl_info; + std::unique_ptr tunnel; + +public: + /// Holds notifier that will point to NR-U bearer on the DL path + f1u_du_gateway_bearer_rx_notifier& du_rx; +}; + +/// \brief Object used to connect the DU and CU-UP F1-U bearers +/// On the co-located case this is done by connecting both entities directly. +/// +/// Note that CU and DU bearer creation and removal can be performed from different threads and are therefore +/// protected by a common mutex. +class f1u_split_connector final : public f1u_du_gateway +{ +public: + f1u_split_connector(srs_cu_up::ngu_gateway* udp_gw_, + gtpu_demux* demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_ = GTPU_PORT) : + logger_du(srslog::fetch_basic_logger("DU-F1-U")), + udp_gw(udp_gw_), + demux(demux_), + gtpu_pcap(gtpu_pcap_), + peer_port(peer_port_) + { + udp_session = udp_gw->create(gw_data_gtpu_demux_adapter); + gw_data_gtpu_demux_adapter.connect_gtpu_demux(*demux); + } + + f1u_du_gateway* get_f1u_du_gateway() { return this; } + + std::optional get_bind_port() { return udp_session->get_bind_port(); } + + std::unique_ptr create_du_bearer(uint32_t ue_index, + drb_id_t drb_id, + srs_du::f1u_config config, + const up_transport_layer_info& dl_up_tnl_info, + const up_transport_layer_info& ul_up_tnl_info, + srs_du::f1u_du_gateway_bearer_rx_notifier& du_rx, + timer_factory timers, + task_executor& ue_executor) override; + + void remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) override; + + expected get_du_bind_address(gnb_du_id_t gnb_du_id) override; + +private: + srslog::basic_logger& logger_du; + // Key is the UL UP TNL Info (CU-CP address and UL TEID reserved by CU-CP) + std::unordered_map du_map; + std::mutex map_mutex; // shared mutex for access to cu_map + + srs_cu_up::ngu_gateway* udp_gw; + std::unique_ptr udp_session; + gtpu_demux* demux; + network_gateway_data_gtpu_demux_adapter gw_data_gtpu_demux_adapter; + dlt_pcap& gtpu_pcap; + + uint16_t peer_port; +}; + +} // namespace srsran::srs_du diff --git a/include/srsran/f1u/local_connector/f1u_local_connector.h b/include/srsran/f1u/local_connector/f1u_local_connector.h index 343ebb30b5..78a1a1af25 100644 --- a/include/srsran/f1u/local_connector/f1u_local_connector.h +++ b/include/srsran/f1u/local_connector/f1u_local_connector.h @@ -192,9 +192,7 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u const srs_cu_up::f1u_config& config, const up_transport_layer_info& ul_up_tnl_info, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, - task_executor& ul_exec, - timer_factory ue_dl_timer_factory, - unique_timer& ue_inactivity_timer) override; + task_executor& ul_exec) override; void attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, const up_transport_layer_info& dl_up_tnl_info) override; @@ -212,6 +210,11 @@ class f1u_local_connector final : public srs_du::f1u_du_gateway, public f1u_cu_u void remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) override; + expected get_du_bind_address(gnb_du_id_t gnb_du_id) override + { + return fmt::format("127.0.0.{}", 1 + static_cast(gnb_du_id)); + } + private: srslog::basic_logger& logger_cu; srslog::basic_logger& logger_du; diff --git a/include/srsran/fapi/validator_report.h b/include/srsran/fapi/validator_report.h index a92886253c..95eeaca507 100644 --- a/include/srsran/fapi/validator_report.h +++ b/include/srsran/fapi/validator_report.h @@ -36,11 +36,11 @@ struct validator_report { /// Error report information structure. Contains the information that helps to identify the source of the failure. struct error_report { - int32_t value; - const char* property_name; - message_type_id message_type; - optional> expected_value_range; - optional pdu_type; + int32_t value; + const char* property_name; + message_type_id message_type; + std::optional> expected_value_range; + std::optional pdu_type; error_report(int32_t value_, std::pair range, diff --git a/include/srsran/fapi_adaptor/precoding_matrix_mapper.h b/include/srsran/fapi_adaptor/precoding_matrix_mapper.h index d61c4f9982..f1b643b7e1 100644 --- a/include/srsran/fapi_adaptor/precoding_matrix_mapper.h +++ b/include/srsran/fapi_adaptor/precoding_matrix_mapper.h @@ -33,7 +33,7 @@ struct mac_pdsch_precoding_info { /// \brief CSI-RS report. /// /// This field is empty in case of omnidirectional precoding. - optional report; + std::optional report; }; struct mac_pdcch_precoding_info {}; diff --git a/include/srsran/gtpu/gtpu_teid.h b/include/srsran/gtpu/gtpu_teid.h index 4f26da90de..3a34bbaf8c 100644 --- a/include/srsran/gtpu/gtpu_teid.h +++ b/include/srsran/gtpu/gtpu_teid.h @@ -52,6 +52,12 @@ constexpr gtpu_teid_t int_to_gtpu_teid(uint32_t teid_val) /// \brief TEID for path management messages via GTP-U, e.g. echo request, echo response,... constexpr gtpu_teid_t GTPU_PATH_MANAGEMENT_TEID = int_to_gtpu_teid(0); +/// \brief Smallest TEID value that is not used for path management. +constexpr gtpu_teid_t GTPU_TEID_MIN = int_to_gtpu_teid(1); + +/// \brief Largest TEID value that is not used for path management. +constexpr gtpu_teid_t GTPU_TEID_MAX = int_to_gtpu_teid(std::numeric_limits::max()); + } // namespace srsran namespace fmt { diff --git a/include/srsran/gtpu/gtpu_teid_pool.h b/include/srsran/gtpu/gtpu_teid_pool.h index db305416ae..8bd500b473 100644 --- a/include/srsran/gtpu/gtpu_teid_pool.h +++ b/include/srsran/gtpu/gtpu_teid_pool.h @@ -25,14 +25,13 @@ #include "srsran/adt/expected.h" #include "srsran/gtpu/gtpu_teid.h" #include "srsran/support/compiler.h" -#include namespace srsran { /// \brief GTP-U TEID pool /// /// This class provides a TEID pool that users can request unused TEIDs. -/// This allows the CU-UP/DU to allocate unused local TEIDs safely event +/// This allows the CU-UP/DU to allocate unused local TEIDs safely even /// in the event of TEID wrap-around. class gtpu_teid_pool { @@ -45,6 +44,6 @@ class gtpu_teid_pool virtual bool full() const = 0; - virtual uint32_t get_max_teids() = 0; + virtual uint32_t get_max_nof_teids() = 0; }; } // namespace srsran diff --git a/include/srsran/gtpu/gtpu_tunnel_nru_factory.h b/include/srsran/gtpu/gtpu_tunnel_nru_factory.h index 9f557959b8..5fd35636ec 100644 --- a/include/srsran/gtpu/gtpu_tunnel_nru_factory.h +++ b/include/srsran/gtpu/gtpu_tunnel_nru_factory.h @@ -42,7 +42,28 @@ struct gtpu_tunnel_nru_creation_message { gtpu_tunnel_common_tx_upper_layer_notifier* tx_upper; }; -/// Creates an instance of a GTP-U entity. +/// Creates an instance of a GTP-U entity at DU. std::unique_ptr create_gtpu_tunnel_nru(gtpu_tunnel_nru_creation_message& msg); +struct gtpu_tunnel_nru_rx_creation_message { + srs_cu_up::ue_index_t ue_index; + gtpu_tunnel_nru_config::gtpu_tunnel_nru_rx_config rx_cfg; + gtpu_tunnel_nru_rx_lower_layer_notifier* rx_lower; +}; + +/// Create an instance of a GTP-U Rx entity at CU-UP. +std::unique_ptr +create_gtpu_tunnel_nru_rx(gtpu_tunnel_nru_rx_creation_message& msg); + +struct gtpu_tunnel_nru_tx_creation_message { + srs_cu_up::ue_index_t ue_index; + gtpu_tunnel_nru_config::gtpu_tunnel_nru_tx_config tx_cfg; + dlt_pcap* gtpu_pcap; + gtpu_tunnel_common_tx_upper_layer_notifier* tx_upper; +}; + +/// Create an instance of a GTP-U Tx entity at CU-UP. +std::unique_ptr +create_gtpu_tunnel_nru_tx(gtpu_tunnel_nru_tx_creation_message& msg); + } // namespace srsran diff --git a/include/srsran/gtpu/ngu_gateway.h b/include/srsran/gtpu/ngu_gateway.h index a37a068adc..831ba40553 100644 --- a/include/srsran/gtpu/ngu_gateway.h +++ b/include/srsran/gtpu/ngu_gateway.h @@ -41,7 +41,13 @@ namespace srs_cu_up { class ngu_tnl_pdu_session : public udp_network_gateway_data_handler, public network_gateway_data_notifier_with_src_addr { public: - virtual ~ngu_tnl_pdu_session() = default; + ~ngu_tnl_pdu_session() override = default; + + /// \brief Get the address to which the socket is bound. + /// + /// In case the gateway was configured to use a hostname, + /// this function can be used to get the actual IP address in string form. + virtual bool get_bind_address(std::string& ip_address) = 0; /// Get bind port currently being used by the NG-U TNL session for the reception of PDUs. /// \return If a UDP link is being used, returns the respective bind port. If the connection is local, it returns diff --git a/include/srsran/mac/mac_cell_control_information_handler.h b/include/srsran/mac/mac_cell_control_information_handler.h index b90e3042d8..213660d477 100644 --- a/include/srsran/mac/mac_cell_control_information_handler.h +++ b/include/srsran/mac/mac_cell_control_information_handler.h @@ -25,13 +25,13 @@ #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/optional.h" #include "srsran/adt/static_vector.h" -#include "srsran/adt/variant.h" #include "srsran/ran/phy_time_unit.h" #include "srsran/ran/rnti.h" #include "srsran/ran/slot_pdu_capacity_constants.h" #include "srsran/ran/slot_point.h" #include "srsran/ran/uci/uci_constants.h" #include "srsran/ran/uci/uci_mapping.h" +#include namespace srsran { @@ -110,6 +110,8 @@ struct mac_uci_pdu { bounded_bitset payload; }; + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// Metric of channel quality in dB. std::optional ul_sinr_dB; /// Timing Advance Offset measured for the UE. @@ -141,6 +143,8 @@ struct mac_uci_pdu { static_vector harqs; }; + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// Metric of channel quality in dB. std::optional ul_sinr_dB; /// Timing Advance Offset measured for the UE. @@ -208,6 +212,8 @@ struct mac_uci_pdu { bounded_bitset payload; }; + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// Metric of channel quality in dB. std::optional ul_sinr_dB; /// Timing Advance Offset measured for the UE. @@ -229,7 +235,7 @@ struct mac_uci_pdu { /// RNTI value corresponding to the UE that generated this PDU. rnti_t rnti; /// UCI PDU multiplexed either in the PUSCH or encoded in the PUCCH. - variant pdu; + std::variant pdu; }; /// List of UCI indication PDUs for a given slot. diff --git a/include/srsran/ngap/gateways/n2_connection_client.h b/include/srsran/ngap/gateways/n2_connection_client.h new file mode 100644 index 0000000000..36ac2b9307 --- /dev/null +++ b/include/srsran/ngap/gateways/n2_connection_client.h @@ -0,0 +1,45 @@ +/* + * + * 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/ngap/ngap.h" + +namespace srsran { +namespace srs_cu_cp { + +class ngap_message_handler; +class ngap_event_handler; + +/// Handler of N2 connection between CU-CP and AMF. +class n2_connection_client : public ngap_message_notifier +{ +public: + /// \brief Connect the CU-CP to the N2 connection client. + virtual void connect_cu_cp(ngap_message_handler& msg_handler_, ngap_event_handler& ev_handler_) = 0; + + /// \brief Disconnect the CU-CP from the N2 connection client. + virtual void disconnect() = 0; +}; + +} // namespace srs_cu_cp +} // namespace srsran \ No newline at end of file diff --git a/apps/gnb/adapters/ngap_adapter.h b/include/srsran/ngap/gateways/n2_connection_client_factory.h similarity index 69% rename from apps/gnb/adapters/ngap_adapter.h rename to include/srsran/ngap/gateways/n2_connection_client_factory.h index c43ba2bfd1..1b18a55959 100644 --- a/apps/gnb/adapters/ngap_adapter.h +++ b/include/srsran/ngap/gateways/n2_connection_client_factory.h @@ -22,24 +22,19 @@ #pragma once -#include "lib/ngap/ngap_asn1_packer.h" -#include "srsran/ngap/ngap.h" -#include "srsran/support/io/io_broker.h" +#include "srsran/gateways/sctp_network_gateway.h" +#include "srsran/ngap/gateways/n2_connection_client.h" +#include namespace srsran { -namespace srs_cu_cp { -/// Handler of NG connection between CU-CP and AMF. -class ngap_gateway_connector : public ngap_message_notifier -{ -public: - virtual void connect_cu_cp(ngap_message_handler& msg_handler_, ngap_event_handler& ev_handler_) = 0; +class io_broker; +class dlt_pcap; - virtual void disconnect() = 0; -}; +namespace srs_cu_cp { /// Parameters for the NG gateway instantiation. -struct ngap_gateway_params { +struct n2_connection_client_config { /// Parameters for a local AMF stub connection. struct no_core {}; @@ -53,10 +48,11 @@ struct ngap_gateway_params { dlt_pcap& pcap; /// Mode of operation. - variant mode; + std::variant mode; }; -std::unique_ptr create_ngap_gateway(const ngap_gateway_params& params); +/// Create an N2 connection client. +std::unique_ptr create_n2_connection_client(const n2_connection_client_config& params); } // namespace srs_cu_cp -} // namespace srsran +} // namespace srsran \ No newline at end of file diff --git a/include/srsran/ngap/ngap_setup.h b/include/srsran/ngap/ngap_setup.h index b3b763f7a4..59796b04ee 100644 --- a/include/srsran/ngap/ngap_setup.h +++ b/include/srsran/ngap/ngap_setup.h @@ -25,6 +25,7 @@ #include "ngap_types.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/ran/crit_diagnostics.h" +#include namespace srsran { namespace srs_cu_cp { @@ -78,7 +79,7 @@ struct ngap_ng_setup_failure { std::optional crit_diagnostics; }; -using ngap_ng_setup_result = variant; +using ngap_ng_setup_result = std::variant; } // namespace srs_cu_cp } // namespace srsran diff --git a/include/srsran/ofh/ecpri/ecpri_packet_decoder.h b/include/srsran/ofh/ecpri/ecpri_packet_decoder.h index 3e61b78382..02b78c505f 100644 --- a/include/srsran/ofh/ecpri/ecpri_packet_decoder.h +++ b/include/srsran/ofh/ecpri/ecpri_packet_decoder.h @@ -23,8 +23,8 @@ #pragma once #include "srsran/adt/span.h" -#include "srsran/adt/variant.h" #include "srsran/ofh/ecpri/ecpri_packet_properties.h" +#include namespace srsran { namespace ecpri { @@ -34,7 +34,7 @@ struct packet_parameters { /// Common header. common_header header; /// eCPRI type parameters. - variant type_params; + std::variant type_params; }; /// eCPRI packet decoder interface. diff --git a/include/srsran/ofh/ethernet/ethernet_frame_notifier.h b/include/srsran/ofh/ethernet/ethernet_frame_notifier.h index db8f99ed61..8ef7eb883f 100644 --- a/include/srsran/ofh/ethernet/ethernet_frame_notifier.h +++ b/include/srsran/ofh/ethernet/ethernet_frame_notifier.h @@ -22,6 +22,7 @@ #pragma once +#include "ethernet_unique_buffer.h" #include "srsran/adt/span.h" namespace srsran { @@ -35,7 +36,7 @@ class frame_notifier virtual ~frame_notifier() = default; /// Notifies the reception of an Ethernet frame coming from the underlying Ethernet link. - virtual void on_new_frame(span payload) = 0; + virtual void on_new_frame(unique_rx_buffer buffer) = 0; }; } // namespace ether diff --git a/include/srsran/ofh/ethernet/ethernet_unique_buffer.h b/include/srsran/ofh/ethernet/ethernet_unique_buffer.h new file mode 100644 index 0000000000..fff905857b --- /dev/null +++ b/include/srsran/ofh/ethernet/ethernet_unique_buffer.h @@ -0,0 +1,137 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/adt/span.h" +#include "srsran/support/units.h" + +namespace srsran { +namespace ether { + +namespace detail { + +/// Base vtable for move/destroy operations over the object stored in "unique_rx_buffer". +class storage_helper +{ +public: + constexpr storage_helper() = default; + virtual ~storage_helper() = default; + virtual void move(void* src, void* dest) const = 0; + virtual void destroy(void* src) const = 0; + virtual bool empty() const = 0; +}; + +/// Specialization of move/destroy operations for when the "unique_rx_buffer" stores an object in its internal storage. +template +class storage_helper_impl : public storage_helper +{ +public: + constexpr storage_helper_impl() = default; + void move(void* src, void* dest) const override + { + ::new (dest) T(std::move(*static_cast(src))); + static_cast(src)->~T(); + } + void destroy(void* src) const override { static_cast(src)->~T(); } + bool empty() const override { return false; } +}; + +/// Specialization of move/destroy operations for when the "unique_rx_buffer" is empty. +class empty_storage : public storage_helper +{ +public: + constexpr empty_storage() = default; + void move(void* src, void* dest) const override {} + void destroy(void* src) const override {} + bool empty() const override { return true; } +}; + +} // namespace detail + +/// Receive buffer interface. +class rx_buffer +{ +public: + virtual ~rx_buffer() = default; + + /// Returns a span of bytes received from the NIC. + virtual span data() const = 0; +}; + +/// Receive buffer wrapper enforcing unique ownership of the stored buffer. +class unique_rx_buffer +{ + static constexpr units::bytes storage_capacity{32}; + static const inline detail::empty_storage empty_object; + + using storage_t = std::aligned_storage_t; + +public: + /// Default constructor initializes internal storage with an empty object. + constexpr unique_rx_buffer() noexcept : storage_ptr(&empty_object), buffer_ptr(nullptr) {} + + /// Copy constructor and copy assignment are deleted. + unique_rx_buffer(const unique_rx_buffer& other) = delete; + unique_rx_buffer& operator=(unique_rx_buffer& other) = delete; + + /// Constructor receives an R-value reference to an object implementing rx_buffer interface. + template + unique_rx_buffer(T&& rx_buffer_wrapper) noexcept(std::is_nothrow_move_constructible::value) + { + static_assert(sizeof(rx_buffer_wrapper) <= storage_capacity.value(), + "Pre-allocated storage does not have enough space to store passed rx buffer"); + + const static detail::storage_helper_impl helper{}; + storage_ptr = &helper; + ::new (&buffer) T(std::forward(rx_buffer_wrapper)); + } + + /// Move constructor. + unique_rx_buffer(unique_rx_buffer&& other) noexcept : storage_ptr(other.storage_ptr) + { + other.storage_ptr = &empty_object; + storage_ptr->move(&other.buffer, &buffer); + } + + /// Returns the view over stored data buffer. + span data() const + { + if (storage_ptr->empty()) { + return {}; + } + return static_cast(static_cast(&buffer))->data(); + } + + /// Default destructor. + ~unique_rx_buffer() { storage_ptr->destroy(&buffer); }; + +private: + const detail::storage_helper* storage_ptr; + union { + mutable storage_t buffer; + void* buffer_ptr; + }; +}; + +} // namespace ether +} // namespace srsran diff --git a/include/srsran/ofh/ofh_sector_config.h b/include/srsran/ofh/ofh_sector_config.h index 970af92a10..b78bf4a644 100644 --- a/include/srsran/ofh/ofh_sector_config.h +++ b/include/srsran/ofh/ofh_sector_config.h @@ -127,10 +127,10 @@ struct sector_dependencies { srslog::basic_logger* logger = nullptr; /// Downlink task executor. task_executor* downlink_executor; - /// Transmitter task executor. - task_executor* transmitter_executor = nullptr; - /// Receiver task executor. - task_executor* receiver_executor = nullptr; + /// Message transmitter and receiver task executor. + task_executor* txrx_executor = nullptr; + /// Uplink task executor. + task_executor* uplink_executor = nullptr; /// User-Plane received symbol notifier. std::shared_ptr notifier; /// Optional Ethernet gateway. diff --git a/include/srsran/phy/support/resource_grid_reader.h b/include/srsran/phy/support/resource_grid_reader.h index b7de623acd..5e18e96b80 100644 --- a/include/srsran/phy/support/resource_grid_reader.h +++ b/include/srsran/phy/support/resource_grid_reader.h @@ -57,21 +57,6 @@ class resource_grid_reader : public resource_grid_base /// \return \c true if the resource grid is empty. Otherwise, \c false. virtual bool is_empty() const = 0; - /// \brief Gets a number of resource elements in the resource grid at the given port and symbol using a mask to - /// indicate which subcarriers are allocated and which are not. - /// - /// \param[out] symbols Destination symbol buffer. - /// \param[in] port Port index. - /// \param[in] l Symbol index. - /// \param[in] k_init Initial subcarrier index. - /// \param[in] mask Boolean mask denoting the subcarriers to be read (if \c true), starting from \c k_init. - /// \return A view to the unused entries of \c symbols. - /// \note The number of elements of \c mask shall be equal to or greater than the resource grid number of subcarriers. - /// \note The number of elements of \c symbol shall be equal to or greater than the number of true elements in - /// \c mask. - virtual span - get(span symbols, unsigned port, unsigned l, unsigned k_init, span mask) const = 0; - /// \brief Gets a number of resource elements in the resource grid at the given port and symbol using a bounded bitset /// to indicate which subcarriers are allocated and which are not. /// diff --git a/include/srsran/phy/support/resource_grid_reader_empty.h b/include/srsran/phy/support/resource_grid_reader_empty.h index 7787336c31..4b42043eb2 100644 --- a/include/srsran/phy/support/resource_grid_reader_empty.h +++ b/include/srsran/phy/support/resource_grid_reader_empty.h @@ -51,13 +51,6 @@ class resource_grid_reader_empty : public resource_grid_reader // See interface for documentation. bool is_empty() const override { return true; } - // See interface for documentation. - span get(span symbols, unsigned /**/, unsigned /**/, unsigned /**/, span /**/) const override - { - srsvec::zero(symbols); - return {}; - } - // See interface for documentation. span get(span symbols, unsigned /**/, diff --git a/include/srsran/phy/support/resource_grid_writer.h b/include/srsran/phy/support/resource_grid_writer.h index c3fb5b1452..51043a392d 100644 --- a/include/srsran/phy/support/resource_grid_writer.h +++ b/include/srsran/phy/support/resource_grid_writer.h @@ -40,21 +40,6 @@ class resource_grid_writer : public resource_grid_base /// Default destructor virtual ~resource_grid_writer() = default; - /// \brief Puts a number of resource elements in the resource grid at the given port and symbol using a mask to - /// indicate which subcarriers are allocated and which are not. - /// - /// \param[in] port Port index. - /// \param[in] l Symbol index. - /// \param[in] k_init Initial subcarrier index. - /// \param[in] mask Boolean mask denoting the subcarriers to be written (if \c true), starting from \c k_init. - /// \param[in] symbols Symbols to be written into the resource grid. - /// \return A view to the unused entries of \c symbols. - /// \note The number of elements of \c mask shall be equal to or greater than the resource grid number of subcarriers. - /// \note The number of elements of \c symbols shall be equal to or greater than the number of true elements in - /// \c mask. - virtual span - put(unsigned port, unsigned l, unsigned k_init, span mask, span symbols) = 0; - /// \brief Puts a number of resource elements in the resource grid at the given port and symbol using a bounded bitset /// to indicate which subcarriers are allocated and which are not. /// diff --git a/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h b/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h index dd76048add..f71c716a67 100644 --- a/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h +++ b/include/srsran/phy/upper/channel_processors/channel_processor_formatters.h @@ -286,17 +286,39 @@ struct formatter { /// \brief Custom formatter for \c pucch_processor::format0_configuration. template <> struct formatter { + /// Helper used to parse formatting options and format fields. + srsran::delimited_formatter helper; + + /// Default constructor. + formatter() = default; + template auto parse(ParseContext& ctx) -> decltype(ctx.begin()) { - return ctx.begin(); + return helper.parse(ctx); } template auto format(const srsran::pucch_processor::format0_configuration& config, FormatContext& ctx) -> decltype(std::declval().out()) { - return format_to(ctx.out(), "format0_configuration"); + if (config.context.has_value()) { + helper.format_always(ctx, config.context.value()); + } + helper.format_always(ctx, "format=0"); + helper.format_if_verbose(ctx, "bwp=[{}, {})", config.bwp_start_rb, config.bwp_start_rb + config.bwp_size_rb); + helper.format_if_verbose(ctx, "slot={}", config.slot); + helper.format_always(ctx, "prb1={}", config.starting_prb); + helper.format_always( + ctx, "prb2={}", config.second_hop_prb.has_value() ? std::to_string(config.second_hop_prb.value()) : "na"); + helper.format_always( + ctx, "symb=[{}, {})", config.start_symbol_index, config.start_symbol_index + config.nof_symbols); + helper.format_always(ctx, "cs={}", config.initial_cyclic_shift); + helper.format_if_verbose(ctx, "n_id={}", config.n_id); + helper.format_if_verbose(ctx, "sr_opportunity={}", config.sr_opportunity); + helper.format_if_verbose(ctx, "ports={}", srsran::span(config.ports)); + + return ctx.out(); } }; diff --git a/include/srsran/phy/upper/channel_processors/pucch_detector.h b/include/srsran/phy/upper/channel_processors/pucch_detector.h index fc2244433a..83e021e674 100644 --- a/include/srsran/phy/upper/channel_processors/pucch_detector.h +++ b/include/srsran/phy/upper/channel_processors/pucch_detector.h @@ -41,7 +41,44 @@ class pucch_detector public: /// Collects PUCCH Format 0 detector parameters. struct format0_configuration { - // Add here PUCCH demodulator parameters... + /// Slot and numerology. + slot_point slot; + /// Cyclic prefix. + cyclic_prefix cp; + /// \brief PRB index used for the PUCCH transmission within the BWP {0, ..., 274}. + /// + /// Index of the PRB prior to frequency hopping or for no frequency hopping as per TS38.213 Section 9.2.1. + unsigned starting_prb; + /// \brief PRB index used for the PUCCH transmission within the BWP after frequency hopping {0, ..., 274}. + /// + /// Index of the PRB posterior to frequency hopping as per TS38.213 Section 9.2.1, if intra-slot frequency hopping + /// is enabled, empty otherwise. + std::optional second_hop_prb; + /// Index of the first OFDM symbol allocated to the PUCCH {0, ..., 13}. + unsigned start_symbol_index; + /// Number of OFDM symbols allocated to the PUCCH {1, 2}. + unsigned nof_symbols; + /// \brief Cyclic shift initial index {0, ..., 11}. + /// + /// Index used to retrieve the cyclic shift for generating the low-PAPR sequence. Specifically, it corresponds to + /// parameter \f$m_0\f$ in the formula for the cyclic shift \f$\alpha\f$ in TS38.211 Section 6.3.2.2.2. + unsigned initial_cyclic_shift; + /// \brief Sequence hopping identifier {0, ..., 1023}. + /// + /// Corresponds to parameter \f$n_{\textup{ID}}\f$ in TS38.211 Section 6.3.2.2.1. + /// + /// It must be set to the higher layer parameter \e hoppingID (see TS38.331 Section 6.3.2 — Information + /// Element \e PUCCH-ConfigCommon) if it is configured. Otherwise, it must be equal to the physical cell identifier + /// \f$N_{\textup{ID}}^{\textup{cell}}\f$. + unsigned n_id; + /// \brief Number of expected HARQ-ACK bits {0, 1, 2}. + /// + /// This parameter should be set to zero when trying to detect a positive scheduling request only. + unsigned nof_harq_ack; + /// Set to \c true if the PUCCH is used for reporting scheduling request. + bool sr_opportunity; + /// Port indexes used for the PUCCH reception. + static_vector ports; }; /// Collects PUCCH Format 1 detector parameters. diff --git a/include/srsran/phy/upper/channel_processors/pucch_processor.h b/include/srsran/phy/upper/channel_processors/pucch_processor.h index 2ce0ab9615..d90e65ee48 100644 --- a/include/srsran/phy/upper/channel_processors/pucch_processor.h +++ b/include/srsran/phy/upper/channel_processors/pucch_processor.h @@ -45,16 +45,50 @@ class pucch_processor public: /// Collects specific PUCCH Format 0 parameters. struct format0_configuration { - /// Cyclic prefix configuration for the slot. + /// Context information. + std::optional context; + /// Slot and numerology. + slot_point slot; + /// Cyclic prefix. cyclic_prefix cp; - /// Initial cyclic shift {0, ..., 11}. - unsigned initial_cyclic_shift; - /// Number of symbols for the PUCCH transmission {1, 2}. - unsigned nof_symbols; - /// Start symbol index {0, ..., 13}. + /// Number of contiguous PRBs allocated to the BWP {1, ..., 275}. + unsigned bwp_size_rb; + /// BWP start RB index from Point A {0, ..., 274}. + unsigned bwp_start_rb; + /// \brief PRB index used for the PUCCH transmission within the BWP {0, ..., 274}. + /// + /// Index of the PRB prior to frequency hopping or for no frequency hopping as per TS38.213 Section 9.2.1. + unsigned starting_prb; + /// \brief PRB index used for the PUCCH transmission within the BWP after frequency hopping {0, ..., 274}. + /// + /// Index of the PRB posterior to frequency hopping as per TS38.213 Section 9.2.1, if intra-slot frequency hopping + /// is enabled, empty otherwise. + std::optional second_hop_prb; + /// Index of the first OFDM symbol allocated to the PUCCH {0, ..., 13}. unsigned start_symbol_index; - /// Slot and numerology, for logging. - slot_point slot; + /// Number of OFDM symbols allocated to the PUCCH {1, 2}. + unsigned nof_symbols; + /// \brief Cyclic shift initial index {0, ..., 11}. + /// + /// Index used to retrieve the cyclic shift for generating the low-PAPR sequence. Specifically, it corresponds to + /// parameter \f$m_0\f$ in the formula for the cyclic shift \f$\alpha\f$ in TS38.211 Section 6.3.2.2.2. + unsigned initial_cyclic_shift; + /// \brief Sequence hopping identifier {0, ..., 1023}. + /// + /// Corresponds to parameter \f$n_{\textup{ID}}\f$ in TS38.211 Section 6.3.2.2.1. + /// + /// It must be set to the higher layer parameter \e hoppingID (see TS38.331 Section 6.3.2 — Information + /// Element \e PUCCH-ConfigCommon) if it is configured. Otherwise, it must be equal to the physical cell identifier + /// \f$N_{\textup{ID}}^{\textup{cell}}\f$. + unsigned n_id; + /// \brief Number of expected HARQ-ACK bits {0, 1, 2}. + /// + /// This parameter should be set to zero when trying to detect a positive scheduling request only. + unsigned nof_harq_ack; + /// Set to \c true if the PUCCH is used for reporting scheduling request. + bool sr_opportunity; + /// Port indexes used for the PUCCH reception. + static_vector ports; }; /// Collects PUCCH Format 1 parameters. diff --git a/include/srsran/phy/upper/channel_processors/pucch_uci_message.h b/include/srsran/phy/upper/channel_processors/pucch_uci_message.h index 404b049016..22cc430851 100644 --- a/include/srsran/phy/upper/channel_processors/pucch_uci_message.h +++ b/include/srsran/phy/upper/channel_processors/pucch_uci_message.h @@ -70,6 +70,35 @@ class pucch_uci_message uci_constants::MAX_NOF_PAYLOAD_BITS); } + /// \brief Creates a UCI message from initializer lists. + /// \param[in] harq_ack HARQ-ACK feedback bits. + /// \param[in] csi_part1 CSI Part 1 bits. + /// \param[in] csi_part2 CSI Part 2 bits. + /// \param[in] sr SR bits. + pucch_uci_message(const std::initializer_list& sr, + const std::initializer_list& harq_ack, + const std::initializer_list& csi_part1, + const std::initializer_list& csi_part2) : + nof_sr_bits(sr.size()), + nof_harq_ack_bits(harq_ack.size()), + nof_csi_part1_bits(csi_part1.size()), + nof_csi_part2_bits(csi_part2.size()) + { + unsigned i = 0; + for (uint8_t value : harq_ack) { + data[i++] = value; + } + for (uint8_t value : sr) { + data[i++] = value; + } + for (uint8_t value : csi_part1) { + data[i++] = value; + } + for (uint8_t value : csi_part2) { + data[i++] = value; + } + } + /// Sets the message status. void set_status(uci_status status_) { status = status_; } diff --git a/include/srsran/phy/upper/channel_processors/pusch/factories.h b/include/srsran/phy/upper/channel_processors/pusch/factories.h index 15ad50de11..24d51e44ff 100644 --- a/include/srsran/phy/upper/channel_processors/pusch/factories.h +++ b/include/srsran/phy/upper/channel_processors/pusch/factories.h @@ -47,6 +47,15 @@ class pusch_decoder_factory virtual std::unique_ptr create() = 0; }; +/// \brief Creates a factory for the empty PUSCH decoder. +/// +/// See \ref pusch_decoder_empty_impl for more details on the implementation. +/// +/// \param[in] nof_prb Maximum number of PRB. +/// \param[in] nof_layers Maximum number of layers. +/// \return An instance of PUSCH decoder factory. +std::shared_ptr create_pusch_decoder_empty_factory(unsigned nof_prb, unsigned nof_layers); + struct pusch_decoder_factory_sw_configuration { std::shared_ptr crc_factory; std::shared_ptr decoder_factory; @@ -109,9 +118,35 @@ struct pusch_processor_factory_sw_configuration { std::shared_ptr create_pusch_processor_factory_sw(pusch_processor_factory_sw_configuration& config); -std::shared_ptr create_pusch_processor_pool(std::shared_ptr factory, - unsigned max_nof_processors, - bool blocking = false); +/// \brief Collection of parameters necessary to build a PUSCH processor pool. +/// +/// The PUSCH processor pool uses the regular factory for the asynchronous PUSCH decoding. When the regular PUSCH +/// processors are all reserved, it uses the processors created from the UCI only factory. +/// +/// The UCI factory processors are executed synchronously and perform the exact same tasks as a regular PUSCH processor +/// except the shared channel decoding is skipped. +struct pusch_processor_pool_factory_config { + /// PUSCH processor factory for regular processing. + std::shared_ptr factory; + /// PUSCH processor factory for UCI only. + std::shared_ptr uci_factory; + /// \brief Number of regular PUSCH processors. + /// + /// This is the maximum number of PUSCH processors that can be in the process of decoding shared channel. The number + /// of regular processors is independent from the number of threads. + unsigned nof_regular_processors; + /// \brief Number of UCI PUSCH processors. + /// + /// This is the number of PUSCH processors that process only UCI in case all the regular PUSCH processors are + /// reserved. The number of UCI processors must be equal to the number of threads that will use the PUSCH processor + /// pool. + unsigned nof_uci_processors; + /// Set to true for the pool to wait for processors to be unlocked. + bool blocking = false; +}; + +/// Creates a PUSCH processor pool. +std::shared_ptr create_pusch_processor_pool(pusch_processor_pool_factory_config& config); class ulsch_demultiplex_factory { diff --git a/include/srsran/phy/upper/uplink_slot_pdu_repository.h b/include/srsran/phy/upper/uplink_slot_pdu_repository.h index 8300519e82..8a81e00046 100644 --- a/include/srsran/phy/upper/uplink_slot_pdu_repository.h +++ b/include/srsran/phy/upper/uplink_slot_pdu_repository.h @@ -22,16 +22,16 @@ #pragma once -#include "srsran/adt/variant.h" #include "srsran/phy/upper/uplink_processor.h" #include "srsran/ran/slot_pdu_capacity_constants.h" +#include #include namespace srsran { /// Defines an entry of the uplink slot PDU repository. using uplink_slot_pdu_entry = - variant; + std::variant; /// \brief Uplink slot PDU repository. /// diff --git a/include/srsran/phy/upper/upper_phy.h b/include/srsran/phy/upper/upper_phy.h index 0345442ad2..2610ff92aa 100644 --- a/include/srsran/phy/upper/upper_phy.h +++ b/include/srsran/phy/upper/upper_phy.h @@ -52,6 +52,9 @@ class upper_phy /// Default destructor. virtual ~upper_phy() = default; + /// Returns the sector identifier of this upper PHY. + virtual unsigned get_sector_id() const = 0; + /// Returns a reference to the receive symbol handler of this upper PHY. virtual upper_phy_rx_symbol_handler& get_rx_symbol_handler() = 0; diff --git a/include/srsran/phy/upper/upper_phy_factories.h b/include/srsran/phy/upper/upper_phy_factories.h index 9236074e61..c1666483ff 100644 --- a/include/srsran/phy/upper/upper_phy_factories.h +++ b/include/srsran/phy/upper/upper_phy_factories.h @@ -30,6 +30,7 @@ #include "srsran/phy/upper/uplink_processor.h" #include "srsran/phy/upper/upper_phy.h" #include +#include namespace srsran { @@ -171,9 +172,9 @@ struct downlink_processor_factory_sw_config { /// - \c generic: for using unoptimized PDSCH processing, or /// - \c concurrent: for using a processor that processes code blocks in parallel, or /// - \c lite: for using a memory optimized processor. - variant + std::variant pdsch_processor; /// Number of concurrent threads processing downlink transmissions. unsigned nof_concurrent_threads; diff --git a/include/srsran/ran/cause/e1ap_cause.h b/include/srsran/ran/cause/e1ap_cause.h index 8bbd1e191f..ad31b1c1a8 100644 --- a/include/srsran/ran/cause/e1ap_cause.h +++ b/include/srsran/ran/cause/e1ap_cause.h @@ -23,8 +23,8 @@ #pragma once #include "common.h" -#include "srsran/adt/variant.h" #include "fmt/format.h" +#include namespace srsran { @@ -67,7 +67,7 @@ enum class e1ap_cause_radio_network_t : uint8_t { enum class e1ap_cause_transport_t : uint8_t { unspecified = 0, transport_res_unavailable, unknown_tnl_address_for_iab }; -using e1ap_cause_t = variant; +using e1ap_cause_t = std::variant; } // namespace srsran @@ -85,15 +85,16 @@ struct formatter { template auto format(srsran::e1ap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); - } else { - return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "radio_network-id{}", *result); } + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "transport-id{}", *result); + } + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "protocol-id{}", *result); + } + return format_to(ctx.out(), "misc-id{}", std::get(o)); } }; diff --git a/include/srsran/ran/cause/f1ap_cause.h b/include/srsran/ran/cause/f1ap_cause.h index 8cd3bb028a..d548a8ad8d 100644 --- a/include/srsran/ran/cause/f1ap_cause.h +++ b/include/srsran/ran/cause/f1ap_cause.h @@ -23,8 +23,8 @@ #pragma once #include "common.h" -#include "srsran/adt/variant.h" #include "fmt/format.h" +#include namespace srsran { @@ -79,7 +79,7 @@ enum class f1ap_cause_transport_t : uint8_t { unknown_up_tnl_info_for_iab }; -using f1ap_cause_t = variant; +using f1ap_cause_t = std::variant; } // namespace srsran @@ -97,15 +97,16 @@ struct formatter { template auto format(srsran::f1ap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); - } else { - return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + if (const auto* cause = std::get_if(&o)) { + return format_to(ctx.out(), "radio_network-id{}", *cause); } + if (const auto* cause = std::get_if(&o)) { + return format_to(ctx.out(), "transport-id{}", *cause); + } + if (const auto* cause = std::get_if(&o)) { + return format_to(ctx.out(), "protocol-id{}", *cause); + } + return format_to(ctx.out(), "misc-id{}", std::get(o)); } }; diff --git a/include/srsran/ran/cause/ngap_cause.h b/include/srsran/ran/cause/ngap_cause.h index 7c5e6f9086..5964d33201 100644 --- a/include/srsran/ran/cause/ngap_cause.h +++ b/include/srsran/ran/cause/ngap_cause.h @@ -23,8 +23,8 @@ #pragma once #include "common.h" -#include "srsran/adt/variant.h" #include "fmt/format.h" +#include namespace srsran { @@ -105,7 +105,7 @@ enum class ngap_cause_misc_t : uint8_t { }; using ngap_cause_t = - variant; + std::variant; } // namespace srsran @@ -123,17 +123,19 @@ struct formatter { template auto format(srsran::ngap_cause_t o, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "radio_network-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "transport-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "nas-id{}", srsran::variant_get(o)); - } else if (srsran::variant_holds_alternative(o)) { - return format_to(ctx.out(), "protocol-id{}", srsran::variant_get(o)); - } else { - return format_to(ctx.out(), "misc-id{}", srsran::variant_get(o)); + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "radio_network-id{}", *result); } + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "transport-id{}", *result); + } + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "nas-id{}", *result); + } + if (const auto* result = std::get_if(&o)) { + return format_to(ctx.out(), "protocol-id{}", *result); + } + return format_to(ctx.out(), "misc-id{}", std::get(o)); } }; diff --git a/include/srsran/ran/csi_report/csi_report_data.h b/include/srsran/ran/csi_report/csi_report_data.h index f2001e74f5..6cabe8039b 100644 --- a/include/srsran/ran/csi_report/csi_report_data.h +++ b/include/srsran/ran/csi_report/csi_report_data.h @@ -24,7 +24,7 @@ #include "srsran/adt/bounded_integer.h" #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" +#include namespace srsran { @@ -55,7 +55,7 @@ struct csi_report_pmi { }; /// Actual PMI value. - variant type; + std::variant type; }; /// Collects Chanel State Information (CSI) report fields. diff --git a/include/srsran/ran/csi_report/csi_report_formatters.h b/include/srsran/ran/csi_report/csi_report_formatters.h index 03e78c3e2c..f1da3f91df 100644 --- a/include/srsran/ran/csi_report/csi_report_formatters.h +++ b/include/srsran/ran/csi_report/csi_report_formatters.h @@ -74,18 +74,13 @@ struct fmt::formatter { template auto format(const srsran::csi_report_pmi& pmi, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(pmi.type)) { - srsran::csi_report_pmi::two_antenna_port value = - srsran::variant_get(pmi.type); - helper.format_always(ctx, "pmi={}", value.pmi); - } - - if (srsran::variant_holds_alternative(pmi.type)) { - srsran::csi_report_pmi::typeI_single_panel_4ports_mode1 value = - srsran::variant_get(pmi.type); - helper.format_always(ctx, "i_1_1={}", value.i_1_1); - helper.format_always(ctx, "i_1_3={}", value.i_1_3); - helper.format_always(ctx, "i_2={}", value.i_2); + if (const auto* two_ports_pmi = std::get_if(&pmi.type)) { + helper.format_always(ctx, "pmi={}", two_ports_pmi->pmi); + } else if (const auto* four_ports_pmi = + std::get_if(&pmi.type)) { + helper.format_always(ctx, "i_1_1={}", four_ports_pmi->i_1_1); + helper.format_always(ctx, "i_1_3={}", four_ports_pmi->i_1_3); + helper.format_always(ctx, "i_2={}", four_ports_pmi->i_2); } return ctx.out(); diff --git a/include/srsran/ran/csi_rs/codebook_config.h b/include/srsran/ran/csi_rs/codebook_config.h index cb40193475..a5d2f028b8 100644 --- a/include/srsran/ran/csi_rs/codebook_config.h +++ b/include/srsran/ran/csi_rs/codebook_config.h @@ -24,7 +24,7 @@ #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" +#include namespace srsran { @@ -73,7 +73,8 @@ struct codebook_config { bool operator!=(const more_than_two_antenna_ports& rhs) const { return !(rhs == *this); } }; - variant nof_antenna_ports; + char dummy; + std::variant nof_antenna_ports; /// Restriction for RI for typeI-SinglePanel-RI-Restriction. bounded_bitset<8> typei_single_panel_ri_restriction; @@ -114,7 +115,8 @@ struct codebook_config { bool operator!=(const multi_panel& rhs) const { return !(rhs == *this); } }; - variant sub_type; + char dummy; + std::variant sub_type; /// CodebookMode as specified in TS 38.214, clause 5.2.2.2.2. Value {1,...,2}. unsigned codebook_mode; @@ -160,8 +162,9 @@ struct codebook_config { /// \brief See TS 38.331, \c typeII-PortSelection in \c CodebookConfig. struct typeii_port_selection { + char dummy; /// The size of the port selection codebook (parameter d). See TS 38.214 clause 5.2.2.2.6. Values {1, 2, 3, 4}. - optional port_selection_sampling_size; + std::optional port_selection_sampling_size; /// Restriction for RI for TypeII-PortSelection-RI-Restriction. bounded_bitset<2> typeii_port_selection_ri_restriction; @@ -173,7 +176,7 @@ struct codebook_config { bool operator!=(const typeii_port_selection& rhs) const { return !(rhs == *this); } }; - variant sub_type; + std::variant sub_type; /// The size of the PSK alphabet, QPSK or 8-PSK. Values {4, 8}. unsigned phase_alphabet_size; /// If subband amplitude reporting is activated (true). @@ -189,7 +192,7 @@ struct codebook_config { bool operator!=(const type2& rhs) const { return !(rhs == *this); } }; - variant codebook_type; + std::variant codebook_type; bool operator==(const codebook_config& rhs) const { return codebook_type == rhs.codebook_type; } bool operator!=(const codebook_config& rhs) const { return !(rhs == *this); } diff --git a/include/srsran/ran/csi_rs/csi_meas_config.h b/include/srsran/ran/csi_rs/csi_meas_config.h index 8f6f076b38..382cd92875 100644 --- a/include/srsran/ran/csi_rs/csi_meas_config.h +++ b/include/srsran/ran/csi_rs/csi_meas_config.h @@ -30,8 +30,8 @@ #include "frequency_allocation_type.h" #include "srsran/adt/optional.h" #include "srsran/adt/static_vector.h" -#include "srsran/adt/variant.h" #include "srsran/ran/tci.h" +#include namespace srsran { @@ -48,7 +48,7 @@ struct csi_rs_resource_mapping { /// The time domain location reference \f$l_0\f$ in TS 38.211, clause 7.4.1.5.3. Values {0,...,13}. unsigned first_ofdm_symbol_in_td; /// The time domain location reference \f$l_1\f$ in TS 38.211, clause 7.4.1.5.3. Values {2,...,12}. - optional first_ofdm_symbol_in_td2; + std::optional first_ofdm_symbol_in_td2; /// CDM configuration. csi_rs_cdm_type cdm; /// Frequency density configuration. @@ -120,15 +120,15 @@ struct nzp_csi_rs_resource { /// Power offset of PDSCH RE to NZP CSI-RS RE. Value in dB. Values {-8,...,15}. See TS 38.214, clause 5.2.2.3.1. int8_t pwr_ctrl_offset; /// Power offset of NZP CSI-RS RE to SSS RE. Value in dB. Values {-3, 0, 3, 6}. See TS 38.214, clause 5.2.2.3.1. - optional pwr_ctrl_offset_ss_db; + std::optional pwr_ctrl_offset_ss_db; /// Scrambling ID. See TS 38.214, clause 5.2.2.3.1. unsigned scrambling_id; /// Present only for periodic and semi-persistent NZP-CSI-RS-Resources. - optional csi_res_period; + std::optional csi_res_period; /// Present only for periodic and semi-persistent NZP-CSI-RS-Resources. Values {0,...,(periodicity_in_slots - 1)}. - optional csi_res_offset; + std::optional csi_res_offset; /// Present only for periodic NZP-CSI-RS-Resources (as indicated in CSI-ResourceConfig). - optional qcl_info_periodic_csi_rs; + std::optional qcl_info_periodic_csi_rs; bool operator==(const nzp_csi_rs_resource& rhs) const { @@ -150,10 +150,10 @@ struct nzp_csi_rs_resource_set { static_vector nzp_csi_rs_res; /// Indicates whether repetition is on/off. If false, UE may not assume that the NZP-CSI-RS resources within the /// resource set are transmitted with the same downlink spatial domain transmission filter. - optional is_repetition_on; + std::optional is_repetition_on; /// Offset X between the slot containing the DCI that triggers a set of aperiodic NZP CSI-RS resources and the slot in /// which the CSI-RS resource set is transmitted. Values {0,...,6}. - optional aperiodic_trigger_offset; + std::optional aperiodic_trigger_offset; /// Indicates that the antenna port for all NZP-CSI-RS resources in the CSI-RS resource set is same. bool is_trs_info_present{false}; @@ -191,14 +191,14 @@ struct csi_im_resource { bool operator!=(const csi_im_resource_element_pattern& rhs) const { return !(rhs == *this); } }; - csi_im_res_id_t res_id; - optional csi_im_res_element_pattern; + csi_im_res_id_t res_id; + std::optional csi_im_res_element_pattern; /// RBs that the CSI resource spans, related to the CRB 0. crb_interval freq_band_rbs; /// Present only for periodic and semi-persistent NZP-CSI-RS-Resources. - optional csi_res_period; + std::optional csi_res_period; /// Present only for periodic and semi-persistent NZP-CSI-RS-Resources. Values {0,...,(periodicity_in_slots - 1)}. - optional csi_res_offset; + std::optional csi_res_offset; bool operator==(const csi_im_resource& rhs) const { @@ -259,18 +259,18 @@ struct csi_associated_report_config_info { bool operator!=(const nzp_csi_rs& rhs) const { return !(rhs == *this); } }; - csi_report_config_id_t report_cfg_id; - variant res_for_channel; + csi_report_config_id_t report_cfg_id; + std::variant res_for_channel; /// CSI-IM-ResourceSet for interference measurement. Values /// {1,...,MAX_NOF_CSI_IM_RESOURCE_SETS_PER_CSI_RESOURCE_CONFIG}. /// Field is present if the CSI-ReportConfig identified by reportConfigId is configured with /// csi-IM-ResourcesForInterference. - optional csi_im_resources_for_interference; + std::optional csi_im_resources_for_interference; /// NZP-CSI-RS-ResourceSet for interference measurement. Values /// {1,...,MAX_NOF_NZP_CSI_RS_RESOURCE_SETS_PER_CSI_RESOURCE_CONFIG}. /// Field is present if the CSI-ReportConfig identified by reportConfigId is configured with /// nzp-CSI-RS-ResourcesForInterference. - optional nzp_csi_rs_resources_for_interference; + std::optional nzp_csi_rs_resources_for_interference; bool operator==(const csi_associated_report_config_info& rhs) const { @@ -333,9 +333,9 @@ struct csi_meas_config { /// Configured CSI report settings as specified in TS 38.214 clause 5.2.1.1. Maximum size: 48. std::vector csi_report_cfg_list; /// Size of CSI request field in DCI (bits). See TS 38.214, clause 5.2.1.5.1. - optional report_trigger_size; - optional aperiodic_trigger_state_list; - optional semi_persistent_on_pusch_trigger_state_list; + std::optional report_trigger_size; + std::optional aperiodic_trigger_state_list; + std::optional semi_persistent_on_pusch_trigger_state_list; bool operator==(const csi_meas_config& rhs) const { @@ -356,8 +356,8 @@ struct zp_csi_rs_resource { /// \brief OFDM symbol and subcarrier occupancy of the ZP-CSI-RS resource within a slot. csi_rs_resource_mapping res_mapping; /// Periodicity and slot offset for periodic/semi-persistent ZP-CSI-RS. - optional period; - optional offset; + std::optional period; + std::optional offset; bool operator==(const zp_csi_rs_resource& other) const { diff --git a/include/srsran/ran/csi_rs/csi_report_config.h b/include/srsran/ran/csi_rs/csi_report_config.h index c47ac66905..d6168c954e 100644 --- a/include/srsran/ran/csi_rs/csi_report_config.h +++ b/include/srsran/ran/csi_rs/csi_report_config.h @@ -25,9 +25,9 @@ #include "codebook_config.h" #include "csi_resource_config.h" #include "csi_rs_constants.h" -#include "srsran/adt/variant.h" #include "srsran/ran/pusch/pusch_configuration.h" #include "srsran/ran/pusch/pusch_constants.h" +#include namespace srsran { @@ -59,7 +59,7 @@ enum class csi_report_periodicity { slots320 = 320 }; -inline unsigned csi_report_periodicity_to_uint(csi_report_periodicity period) +constexpr unsigned csi_report_periodicity_to_uint(csi_report_periodicity period) { return static_cast(period); } @@ -127,6 +127,7 @@ struct csi_report_config { } bool operator!=(const semi_persistent_report_on_pusch& rhs) const { return !(rhs == *this); } }; + struct aperiodic_report { /// Timing offset Y for aperiodic reporting using PUSCH. This field lists the allowed offset values. This list /// must have the same number of entries as the pusch-TimeDomainAllocationList in PUSCH-Config. A particular value @@ -168,12 +169,16 @@ struct csi_report_config { /// \brief Reporting configuration in the frequency domain. /// \remark See TS 38.331, \c reportFreqConfiguration. struct report_frequency_config { + // This user provided constructor is added here to fix a Clang compilation error related to the use of nested types + // with std::optional. + report_frequency_config() {} + cqi_format_indicator cqi_format_ind{cqi_format_indicator::none}; pmi_format_indicator pmi_format_ind{pmi_format_indicator::none}; /// Indicates a contiguous or non-contiguous subset of subbands in the bandwidth part which CSI shall be reported /// for. This field is absent if there are less than 24 PRBs. Values {3,...,19}. See TS 38.331, \c /// csi-ReportingBand. - optional nof_csi_reporting_subbands; + std::optional nof_csi_reporting_subbands; /// Used in conjunction with \c nof_csi_reporting_subbands, where each bit in the bit-string represents one subband. bounded_bitset csi_reporting_subbands_bitmap; @@ -200,7 +205,7 @@ struct csi_report_config { port_index_type_t port_index_type; /// Used only for \c port_index_type_t > \c port_index_1. - optional rank1_x; + std::optional rank1_x; /// Max. size of vector is 2. std::vector rank2_x; /// \c {rank3_x, rank4_x} are used only for \c port_index_type_t > \c port_index_2. @@ -230,31 +235,31 @@ struct csi_report_config { csi_report_config_id_t report_cfg_id; /// Indicates in which serving cell the CSI-ResourceConfig indicated below are to be found. If the field is absent, /// the resources are on the same serving cell as this report configuration. - optional carrier; + std::optional carrier; /// Resources for channel measurement. csi_res_config_id_t res_for_channel_meas; /// CSI IM resources for interference measurement. - optional csi_im_res_for_interference; + std::optional csi_im_res_for_interference; /// NZP CSI RS resources for interference measurement. - optional nzp_csi_rs_res_for_interference; + std::optional nzp_csi_rs_res_for_interference; /// Time domain behavior of reporting configuration. - variant + std::variant report_cfg_type; report_quantity_type_t report_qty_type; /// Relevant only when \c report_quantity_type_t is of type \c cri_ri_i1_cqi. csi_ri_i1_cqi_pdsch_bundle_size_for_csi pdsch_bundle_size_for_csi{csi_ri_i1_cqi_pdsch_bundle_size_for_csi::none}; - optional report_freq_cfg; + std::optional report_freq_cfg; /// Time domain measurement restriction for the channel (signal) measurements. See TS 38.214, clause 5.2.1.1. bool is_time_restrictions_for_channel_meas_configured{false}; /// Time domain measurement restriction for interference measurements. See TS 38.214, clause 5.2.1.1. bool is_time_restrictions_for_interference_meas_configured{false}; /// Codebook configuration for Type-1 or Type-2 including codebook subset restriction. - optional codebook_cfg; + std::optional codebook_cfg; /// Turning on/off group beam based reporting. bool is_group_based_beam_reporting_enabled; /// Value is relevant only when \c is_group_based_beam_reporting_enabled is false. Values {1, 2, 3, 4}. - optional nof_reported_rs; - optional cqi_table; + std::optional nof_reported_rs; + std::optional cqi_table; /// If csi-ReportingBand is absent, the UE shall ignore this field. subband_size_t subband_size; /// Port indication for RI/CQI calculation. For each CSI-RS resource in the linked ResourceConfig for channel diff --git a/include/srsran/ran/csi_rs/csi_resource_config.h b/include/srsran/ran/csi_rs/csi_resource_config.h index 5053787618..eaa1ad9c95 100644 --- a/include/srsran/ran/csi_rs/csi_resource_config.h +++ b/include/srsran/ran/csi_rs/csi_resource_config.h @@ -25,8 +25,8 @@ #include "csi_rs_constants.h" #include "csi_rs_id.h" #include "srsran/adt/static_vector.h" -#include "srsran/adt/variant.h" #include "srsran/scheduler/config/bwp_configuration.h" +#include namespace srsran { @@ -68,8 +68,8 @@ struct csi_resource_config { /// \remark See TS 38.214, clause 5.2.1.2 and TS 38.331, \c CSI-ResourceConfig. enum class resource_type { aperiodic, semiPersistent, periodic }; - csi_res_config_id_t res_cfg_id; - variant csi_rs_res_set_list; + csi_res_config_id_t res_cfg_id; + std::variant csi_rs_res_set_list; /// The DL BWP which the CSI-RS associated with this CSI-ResourceConfig are located in. bwp_id_t bwp_id; resource_type res_type; diff --git a/include/srsran/ran/ntn.h b/include/srsran/ran/ntn.h index 8800d0e37c..66e9b05f9e 100644 --- a/include/srsran/ran/ntn.h +++ b/include/srsran/ran/ntn.h @@ -23,9 +23,9 @@ #pragma once #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" #include #include +#include namespace srsran { @@ -59,7 +59,7 @@ struct epoch_time_t { }; struct ntn_config { - // SIB 19 values + /// SIB 19 values /// Reference location of the serving cell provided via NTN quasi-Earth fixed system. (TS 38.304) std::optional reference_location; /// Distance from the serving cell reference location, as defined in TS 38.304. Each step represents 50m. @@ -74,7 +74,7 @@ struct ntn_config { std::optional k_mac; /// This field provides satellite ephemeris either in format of position and velocity state vector or in format of /// orbital parameters. - variant ephemeris_info; + std::variant ephemeris_info; /// Network-controlled common timing advanced value and it may include any timing offset considered necessary by the /// network. std::optional ta_info; diff --git a/include/srsran/ran/pdcch/search_space.h b/include/srsran/ran/pdcch/search_space.h index 9e37e2b8ac..428c5f56b3 100644 --- a/include/srsran/ran/pdcch/search_space.h +++ b/include/srsran/ran/pdcch/search_space.h @@ -22,12 +22,12 @@ #pragma once -#include "srsran/adt/variant.h" #include "srsran/ran/frame_types.h" #include "srsran/ran/nr_band.h" #include "srsran/ran/pdcch/coreset.h" #include "srsran/ran/slot_point.h" #include "srsran/scheduler/sched_consts.h" +#include namespace srsran { @@ -89,14 +89,14 @@ struct search_space_configuration { unsigned search_space0_index); /// Constructor for non-SearchSpace#0 SearchSpaces. - explicit search_space_configuration(search_space_id id_, - coreset_id cs_id_, - std::array nof_candidates_, - variant dci_fmt_, - unsigned monitoring_slot_periodicity_, - unsigned monitoring_slot_offset_, - subcarrier_spacing scs_common, - unsigned duration_, + explicit search_space_configuration(search_space_id id_, + coreset_id cs_id_, + std::array nof_candidates_, + std::variant dci_fmt_, + unsigned monitoring_slot_periodicity_, + unsigned monitoring_slot_offset_, + subcarrier_spacing scs_common, + unsigned duration_, monitoring_symbols_within_slot_t monitoring_symbols_within_slot_); bool operator==(const search_space_configuration& rhs) const @@ -132,7 +132,7 @@ struct search_space_configuration { bool is_search_space0() const { return id == search_space_id(0); } /// \brief Returns whether SearchSpace if of Common SearchSpace(CSS) or UE Specific SearchSpace(USS). - bool is_common_search_space() const { return variant_holds_alternative(dci_fmt); } + bool is_common_search_space() const { return std::holds_alternative(dci_fmt); } /// \brief Sets the nof. PDCCH candidates for non-SearchSpace#0 SearchSpaces. void set_non_ss0_nof_candidates(std::array nof_candidates_) @@ -145,14 +145,14 @@ struct search_space_configuration { span get_nof_candidates() const { return nof_candidates; } /// \brief Sets the DCI format(s) monitored in non-SearchSpace#0 SearchSpaces. - void set_non_ss0_monitored_dci_formats(variant dci_fmt_) + void set_non_ss0_monitored_dci_formats(std::variant dci_fmt_) { srsran_assert(not is_search_space0(), "Invalid access to DCI format(s) monitored of SearchSpace#0"); dci_fmt = dci_fmt_; } /// \brief Returns DCI format(s) monitored in the SearchSpace. - const variant& get_monitored_dci_formats() const { return dci_fmt; } + const std::variant& get_monitored_dci_formats() const { return dci_fmt; } /// \brief Sets the periodicity in number of slots for non-SearchSpace#0 SearchSpaces void set_non_ss0_monitoring_slot_periodicity(unsigned periodicity) @@ -236,7 +236,7 @@ struct search_space_configuration { /// is L=1U << x. The possible values for each element are {0, 1, 2, 3, 4, 5, 6, 8}. std::array nof_candidates; /// DCI formats applicable to SearchSpace. - variant dci_fmt; + std::variant dci_fmt; /// SearchSpace periodicity in nof. slots for PDCCH monitoring. /// For SearchSpace == 0, set based on tables in TS 38.213, Section 13. /// For SearchSpace != 0, possible values: {1, 2, 4, 5, 8, 10, 16, 20, 40, 80, 160, 320, 640, 1280, 2560}. diff --git a/include/srsran/ran/pdsch/pdsch_prb_bundling.h b/include/srsran/ran/pdsch/pdsch_prb_bundling.h index c6bcbc5b34..a5acce1f5c 100644 --- a/include/srsran/ran/pdsch/pdsch_prb_bundling.h +++ b/include/srsran/ran/pdsch/pdsch_prb_bundling.h @@ -22,7 +22,7 @@ #pragma once -#include "srsran/adt/variant.h" +#include namespace srsran { @@ -49,7 +49,7 @@ struct prb_bundling { bool operator!=(const dynamic_bundling& rhs) const { return !(rhs == *this); } }; - variant bundling; + std::variant bundling; bool operator==(const prb_bundling& rhs) const { return bundling == rhs.bundling; } bool operator!=(const prb_bundling& rhs) const { return !(rhs == *this); } diff --git a/include/srsran/ran/pdsch/pdsch_rate_match_pattern.h b/include/srsran/ran/pdsch/pdsch_rate_match_pattern.h index c43b67e034..0ba5340079 100644 --- a/include/srsran/ran/pdsch/pdsch_rate_match_pattern.h +++ b/include/srsran/ran/pdsch/pdsch_rate_match_pattern.h @@ -50,8 +50,8 @@ struct rate_match_pattern { /// \brief Type of symbols in resource block. enum class symbols_in_rb_type { one_slot, two_slot }; - symbols_in_rb_type type; - variant symbols; + symbols_in_rb_type type; + std::variant symbols; bool operator==(const symbols_in_rb& rhs) const { return type == rhs.type && symbols == rhs.symbols; } bool operator!=(const symbols_in_rb& rhs) const { return !(rhs == *this); } @@ -74,13 +74,13 @@ struct rate_match_pattern { enum class periodicity_and_pattern_type { n2, n4, n5, n8, n10, n20, n40 }; periodicity_and_pattern_type type; - variant + std::variant prd_and_patt; bool operator==(const periodicity_and_pattern& rhs) const @@ -115,7 +115,7 @@ struct rate_match_pattern { /// In frequency domain, the resource is determined by the frequency domain resource of the CORESET with the /// corresponding CORESET ID. Time domain resource is determined by the parameters of the associated search space of /// the CORESET. - variant pattern_type; + std::variant pattern_type; /// The field is mandatory present if the RateMatchPattern is defined on cell level. The field is absent when the /// RateMatchPattern is defined on BWP level. If the RateMatchPattern is defined on BWP level, the UE applies the SCS /// of the BWP. diff --git a/include/srsran/ran/prach/prach_preamble_information.h b/include/srsran/ran/prach/prach_preamble_information.h index 899d4155ff..08e2612d7a 100644 --- a/include/srsran/ran/prach/prach_preamble_information.h +++ b/include/srsran/ran/prach/prach_preamble_information.h @@ -38,12 +38,21 @@ struct prach_preamble_information { unsigned sequence_length; /// Parameter \f$\Delta f^{RA}\f$. prach_subcarrier_spacing scs; - /// Parameter \f$N_u\f$. Expressed in units of the reference symbol time \f$\kappa\f$. - phy_time_unit symbol_length; + /// \brief Number of OFDM symbols. + /// + /// It is given by the first multiplier that appears in the \f$N_u\f$ column, in TS38.211 Tables 6.3.3.1-1 and + /// 6.3.3.1-2. + unsigned nof_symbols; /// Parameter \f$N_{CP}^{RA}\f$. Expressed in units of the reference symbol time \f$\kappa\f$. phy_time_unit cp_length; /// Flag: true if the preamble supports the restricted sets A and B. bool support_restricted_sets; + + /// Parameter \f$N_u\f$, expressed in units of the reference symbol time \f$\kappa\f$. + phy_time_unit symbol_length() const + { + return phy_time_unit::from_seconds(static_cast(nof_symbols) / static_cast(ra_scs_to_Hz(scs))); + } }; /// \brief Get long PRACH preamble information as per TS38.211 Table 6.3.3.1-1. diff --git a/include/srsran/ran/pucch/pucch_configuration.h b/include/srsran/ran/pucch/pucch_configuration.h index adfd2d594e..6e9b23ed5a 100644 --- a/include/srsran/ran/pucch/pucch_configuration.h +++ b/include/srsran/ran/pucch/pucch_configuration.h @@ -28,9 +28,9 @@ #include "pucch_mapping.h" #include "srsran/adt/optional.h" #include "srsran/adt/static_vector.h" -#include "srsran/adt/variant.h" #include "srsran/ran/sr_configuration.h" #include +#include #include namespace srsran { @@ -161,11 +161,11 @@ struct pucch_format_4_cfg { /// \c PUCCH-Resource, in \c PUCCH-Config, TS 38.331. struct pucch_resource { - pucch_res_id_t res_id = {0, 0}; - unsigned starting_prb; - std::optional second_hop_prb; - pucch_format format; - variant format_params; + pucch_res_id_t res_id = {0, 0}; + unsigned starting_prb; + std::optional second_hop_prb; + pucch_format format; + std::variant format_params; bool operator==(const pucch_resource& rhs) const { @@ -184,7 +184,7 @@ struct pucch_resource_set { /// \c resourceList. static_vector pucch_res_id_list; /// \c maxPayloadSize. - optional max_payload_size; + std::optional max_payload_size; bool operator==(const pucch_resource_set& rhs) const { @@ -202,10 +202,10 @@ struct pucch_config { /// List of \c PUCCH-Resource. static_vector pucch_res_list; /// \c format1 .. \c format4, which contain the parameters that are common to a given PUCCH Format. - optional format_1_common_param; - optional format_2_common_param; - optional format_3_common_param; - optional format_4_common_param; + std::optional format_1_common_param; + std::optional format_2_common_param; + std::optional format_3_common_param; + std::optional format_4_common_param; /// List of \c SchedulingRequestResourceConfig. static_vector sr_res_list; diff --git a/include/srsran/ran/pucch/pucch_constants.h b/include/srsran/ran/pucch/pucch_constants.h index 733e6e6b9a..19dea48edf 100644 --- a/include/srsran/ran/pucch/pucch_constants.h +++ b/include/srsran/ran/pucch/pucch_constants.h @@ -22,15 +22,28 @@ #pragma once +#include "srsran/adt/interval.h" #include "srsran/ran/resource_block.h" namespace srsran { namespace pucch_constants { +/// PUCCH hopping identifier, parameter \f$n_{ID}\f$ range. +static constexpr interval n_id_range(0, 1024); + /// PUCCH does not make use of spatial multiplexing. static constexpr unsigned MAX_LAYERS = 1; +/// PUCCH Format 0 number of OFDM symbols range. +static constexpr interval format0_nof_symbols_range(1, 2); + +/// PUCCH Format 0 range for number of HARQ-ACK feedback bits. +static constexpr interval format0_nof_harq_ack_range(0, 2); + +/// PUCCH Format 0 initial cyclic shift range. +static constexpr interval format0_initial_cyclic_shift_range(0, 12); + /// Maximum number of symbols (without DM-RS) that PUCCH Format 1 can transmit. static constexpr unsigned FORMAT1_N_MAX = 7; diff --git a/include/srsran/ran/pucch/srs_configuration.h b/include/srsran/ran/pucch/srs_configuration.h index bb6f1d7f6a..cb5213cde1 100644 --- a/include/srsran/ran/pucch/srs_configuration.h +++ b/include/srsran/ran/pucch/srs_configuration.h @@ -22,9 +22,9 @@ #pragma once -#include "srsran/adt/variant.h" #include "srsran/ran/alpha.h" #include "srsran/ran/csi_rs/csi_rs_id.h" +#include namespace srsran { @@ -61,7 +61,7 @@ struct srs_config { std::optional csi_rs; /// An offset in number of slots between the triggering DCI and the actual transmission of this SRS-ResourceSet. /// If the field is absent the UE applies no offset (value 0). Values {1,..,32}. - optional slot_offset; + std::optional slot_offset; /// An additional list of DCI "code points" upon which the UE shall transmit SRS according to this SRS resource /// set configuration. Values {1,..,maxNrofSRS-TriggerStates-1}, \c max_nof_srs_trigger_states_minus_1. static_vector aperiodic_srs_res_trigger_list; @@ -75,8 +75,10 @@ struct srs_config { }; struct semi_persistent_resource_type { + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// This field is optionally present, in case of non-codebook based transmission. - optional associated_csi_rs; + std::optional associated_csi_rs; bool operator==(const semi_persistent_resource_type& rhs) const { @@ -86,8 +88,10 @@ struct srs_config { }; struct periodic_resource_type { + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// This field is optionally present, in case of non-codebook based transmission. - optional associated_csi_rs; + std::optional associated_csi_rs; bool operator==(const periodic_resource_type& rhs) const { return associated_csi_rs == rhs.associated_csi_rs; } bool operator!=(const periodic_resource_type& rhs) const { return !(rhs == *this); } @@ -108,7 +112,7 @@ struct srs_config { static_vector srs_res_id_list; /// Time domain behavior of SRS resource configuration. The network configures SRS resources in the same resource /// set with the same time domain behavior on periodic, aperiodic and semi-persistent SRS. - variant res_type; + std::variant res_type; /// Indicates if the SRS resource set is used for beam management, codebook based or non-codebook based /// transmission or antenna switching. usage srs_res_set_usage; @@ -117,13 +121,13 @@ struct srs_config { /// P0 value for SRS power control. The value is in dBm. Only even values (step size 2) are allowed. Values /// {-202,..,24}. This field is mandatory present upon configuration of SRS-ResourceSet or SRS-Resource and /// optionally present otherwise. - optional p0; + std::optional p0; /// Indicates whether hsrs,c(i) = fc(i,1) or hsrs,c(i) = fc(i,2) (if twoPUSCH-PC-AdjustmentStates are configured) or /// separate close loop is configured for SRS. This parameter is applicable only for Uls on which UE also transmits /// PUSCH. If not set, the UE applies the value sameAs-Fci1. srs_pwr_ctrl_adjustment_states pwr_ctrl_adj_states{srs_pwr_ctrl_adjustment_states::not_set}; /// A reference signal (e.g. a CSI-RS config or a SS block) to be used for SRS path loss estimation. - optional> pathloss_ref_rs; + std::optional> pathloss_ref_rs; bool operator==(const srs_resource_set& rhs) const { @@ -214,7 +218,7 @@ struct srs_config { /// \brief Configuration of the spatial relation between a reference RS and the target SRS. struct srs_spatial_relation_info { - optional serv_cell_id; + std::optional serv_cell_id; struct srs_ref_signal { srs_res_id res_id; @@ -224,7 +228,7 @@ struct srs_config { bool operator!=(const srs_ref_signal& rhs) const { return !(rhs == *this); } }; - variant reference_signal; + std::variant reference_signal; bool operator==(const srs_spatial_relation_info& rhs) const { @@ -259,8 +263,8 @@ struct srs_config { /// Set/Valid only if resource type is periodic. srs_periodicity_and_offset per_res_type_periodicity_and_offset; /// Sequence ID used to initialize pseudo random group and sequence hopping. Values {0,...,1023}. - uint16_t sequence_id; - optional spatial_relation_info; + uint16_t sequence_id; + std::optional spatial_relation_info; bool operator==(const srs_resource& rhs) const { diff --git a/include/srsran/ran/pusch/pusch_configuration.h b/include/srsran/ran/pusch/pusch_configuration.h index cda8abfd00..b9b2a67a6f 100644 --- a/include/srsran/ran/pusch/pusch_configuration.h +++ b/include/srsran/ran/pusch/pusch_configuration.h @@ -68,8 +68,8 @@ struct pusch_config { /// \brief Reference Signals (e.g. a CSI-RS config or a SS block) to be used for PUSCH path loss estimation. /// \remark See TS 38.213, clause 7.1 and TS 38.331, "PUSCH-PathlossReferenceRS". struct pusch_pathloss_ref_rs { - pusch_pathloss_ref_rs_id id; - variant rs; + pusch_pathloss_ref_rs_id id; + std::variant rs; bool operator==(const pusch_pathloss_ref_rs& rhs) const { return id == rhs.id && rs == rhs.rs; } bool operator!=(const pusch_pathloss_ref_rs& rhs) const { return !(rhs == *this); } @@ -114,7 +114,7 @@ struct pusch_config { alpha msg3_alpha{alpha::not_set}; /// P0 value for UL grant-free/SPS based PUSCH. Value in dBm. Only even values (step size 2) allowed. See TS 38.213, /// clause 7.1. Values {-202,..,24}. - optional p0_nominal_without_grant; + std::optional p0_nominal_without_grant; /// Configuration {p0-pusch, alpha} sets for PUSCH (except msg3), i.e. { {p0,alpha,index1}, {p0,alpha,index2},...}. /// When no set is configured, the UE uses the P0-nominal for msg3 PUSCH, P0-UE is set to 0 and alpha is set /// according to msg3-Alpha configured for msg3 PUSCH. @@ -156,17 +156,17 @@ struct pusch_config { /// Identifier used to initalite data scrambling (c_init) for PUSCH. If the field is absent, the UE applies the /// physical cell ID. See TS 38.211, clause 6.3.1.1. - optional data_scrambling_id_pusch; + std::optional data_scrambling_id_pusch; /// UE uses codebook based or non-codebook based transmission (see TS 38.214, clause 6.1.1). If the field is /// not set, the UE transmits PUSCH on one antenna port. tx_config tx_cfg{tx_config::not_set}; /// DMRS configuration for PUSCH transmissions using PUSCH (chosen dynamically via /// PUSCH-TimeDomainResourceAllocation). Only the fields dmrs-Type, dmrs-AdditionalPosition and maxLength may be set /// differently for mapping type A and B. The field dmrs-UplinkForPUSCH-MappingTypeA applies to DCI format 0_1. - optional pusch_mapping_type_a_dmrs; - optional pusch_mapping_type_b_dmrs; - optional pusch_pwr_ctrl; - resource_allocation res_alloc; + std::optional pusch_mapping_type_a_dmrs; + std::optional pusch_mapping_type_b_dmrs; + std::optional pusch_pwr_ctrl; + resource_allocation res_alloc; /// PUSCH time domain resource allocations. Size: (0..maxNrofUL-Allocations=16). std::vector pusch_td_alloc_list; /// Indicates which MCS table the UE shall use for PUSCH. @@ -178,10 +178,10 @@ struct pusch_config { codebook_subset cb_subset{codebook_subset::not_set}; /// Subset of PMIs addressed by TRIs from 1 to ULmaxRank. The field maxRank applies to DCI format 0_1. /// The field is mandatory present if txConfig is set to codebook and absent otherwise. Values {1,..,4}. - optional max_rank; + std::optional max_rank; /// \c uci-OnPUSCH. - optional uci_cfg; + std::optional uci_cfg; bool operator==(const pusch_config& rhs) const { diff --git a/include/srsran/ran/pusch/pusch_tpc.h b/include/srsran/ran/pusch/pusch_tpc.h index 52572cc7eb..f5b713a6ca 100644 --- a/include/srsran/ran/pusch/pusch_tpc.h +++ b/include/srsran/ran/pusch/pusch_tpc.h @@ -38,10 +38,10 @@ struct pusch_tpc_command_config { std::optional tpc_index; /// An index determining the position of the first bit of TPC command inside the DCI format 2-2 payload. /// Values {1..15}. Present only if serving cell is configured with a supplementary uplink (SUL). - optional tpc_index_sul; + std::optional tpc_index_sul; /// The serving cell to which the acquired power control commands are applicable. If the value is absent, the UE /// applies the TPC commands to the serving cell on which the command has been received. - optional target_cell; + std::optional target_cell; bool operator==(const pusch_tpc_command_config& rhs) const { diff --git a/include/srsran/ran/pusch/ulsch_info.h b/include/srsran/ran/pusch/ulsch_info.h index 2b03d36bc6..ac59b96063 100644 --- a/include/srsran/ran/pusch/ulsch_info.h +++ b/include/srsran/ran/pusch/ulsch_info.h @@ -80,7 +80,7 @@ struct ulsch_configuration { /// The parameters are described in TS38.212 Section 6.3.2.4.1. struct ulsch_information { /// Shared channel (SCH) parameters. - optional sch; + std::optional sch; /// Number of encoded and rate-matched UL-SCH data bits. Parameter \f$G^\textup{UL-SCH}\f$. units::bits nof_ul_sch_bits; /// Number of encoded and rate-matched HARQ-ACK data bits. Parameter \f$G^\textup{HARQ-ACK}\f$. diff --git a/include/srsran/ran/rrm.h b/include/srsran/ran/rrm.h index 99c9db8d26..41826345e6 100644 --- a/include/srsran/ran/rrm.h +++ b/include/srsran/ran/rrm.h @@ -25,6 +25,7 @@ #include "srsran/ran/s_nssai.h" namespace srsran { + /// O-RAN.WG3.E2SM-RC-R003-v3.00 Section 8.4.3.6 struct rrm_policy_member { std::string plmn_id; @@ -32,14 +33,14 @@ struct rrm_policy_member { }; struct rrm_policy_ratio_group { - // Used to identify the group to which the policy is applied. + /// Used to identify the group to which the policy is applied. rrm_policy_member pol_member; - // Sets the minimum percentage of PRBs to be allocated to this group. - optional min_prb_policy_ratio; - // Sets the maximum percentage of PRBs to be allocated to this group. - optional max_prb_policy_ratio; - // Sets the percentage of PRBs to be allocated to this group. - optional ded_prb_policy_ratio; + /// Sets the minimum percentage of PRBs to be allocated to this group. + std::optional min_prb_policy_ratio; + /// Sets the maximum percentage of PRBs to be allocated to this group. + std::optional max_prb_policy_ratio; + /// Sets the percentage of PRBs to be allocated to this group. + std::optional ded_prb_policy_ratio; }; } // namespace srsran diff --git a/include/srsran/ran/sib/system_info_config.h b/include/srsran/ran/sib/system_info_config.h index 4097f6189f..26b2270e38 100644 --- a/include/srsran/ran/sib/system_info_config.h +++ b/include/srsran/ran/sib/system_info_config.h @@ -23,8 +23,8 @@ #pragma once #include "srsran/adt/bounded_bitset.h" -#include "srsran/adt/variant.h" #include "srsran/ran/ntn.h" +#include #include namespace srsran { @@ -112,6 +112,8 @@ struct speed_state_reselection_params { }; struct sib2_info { + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; /// Number of SS blocks to average for cell measurement derivation. If the field is absent the UE uses the measurement /// quantity as specified in TS 38.304. std::optional nof_ssbs_to_average; @@ -175,24 +177,26 @@ struct sib2_info { }; struct sib19_info { - std::optional distance_thres; - std::optional ref_location; - optional cell_specific_koffset; - optional> ephemeris_info; - optional epoch_time; - optional k_mac; - optional ta_info; + // TODO: cpp17 transition workaround for a clang compiler issue + char dummy; + std::optional distance_thres; + std::optional ref_location; + std::optional cell_specific_koffset; + std::optional> ephemeris_info; + std::optional epoch_time; + std::optional k_mac; + std::optional ta_info; }; /// \brief Variant type that can hold different types of SIBs that go in a SI message. -using sib_info = variant; +using sib_info = std::variant; inline sib_type get_sib_info_type(const sib_info& sib) { - if (variant_holds_alternative(sib)) { + if (std::holds_alternative(sib)) { return sib_type::sib2; } - if (variant_holds_alternative(sib)) { + if (std::holds_alternative(sib)) { return sib_type::sib19; } return sib_type::sib_invalid; @@ -204,6 +208,11 @@ struct si_message_sched_info { std::vector sib_mapping_info; /// Periodicity of the SI-message in radio frames. Values: {8, 16, 32, 64, 128, 256, 512}. unsigned si_period_radio_frames = 32; + /// SI window position of the associated SI-message. The network provides si-WindowPosition in an ascending order, + /// i.e. si-WindowPosition in the subsequent entry in schedulingInfoList2 has always value higher than in the previous + /// entry of schedulingInfoList2. See TS 38.331, \c SchedulingInfo2-r17. Values: {1,...,256}. + /// \remark This field is only applicable for release 17 \c SI-SchedulingInfo. + std::optional si_window_position; }; /// This struct contains the information required for the generation of the SI messages sent by the network and the diff --git a/include/srsran/ran/srs/srs_channel_matrix.h b/include/srsran/ran/srs/srs_channel_matrix.h index 9f905a6a38..228dbd6ab0 100644 --- a/include/srsran/ran/srs/srs_channel_matrix.h +++ b/include/srsran/ran/srs/srs_channel_matrix.h @@ -28,6 +28,7 @@ #include "srsran/ran/srs/srs_constants.h" #include "srsran/srsvec/copy.h" #include "srsran/srsvec/sc_prod.h" +#include "srsran/srsvec/zero.h" #include "srsran/support/srsran_assert.h" #include #include @@ -51,6 +52,9 @@ class srs_channel_matrix srs_channel_matrix() = default; /// \brief Constructs a channel matrix with the desired number of transmit and receive ports. + /// + /// The channel coefficients are initialized to zero. + /// /// \param[in] nof_rx_ports Number of receive ports. /// \param[in] nof_tx_ports Number of transmit ports. /// \remark An assertion is triggered if the number of receive ports exceeds \ref srs_constants::max_nof_rx_ports. @@ -65,6 +69,8 @@ class srs_channel_matrix "The number of transmit ports (i.e., {}) exceeds the maximum (i.e., {}).", nof_tx_ports, srs_constants::max_nof_tx_ports); + // Initialize coefficients to zero. + srsvec::zero(data.get_data()); } /// \brief Constructs a channel matrix with the desired number of receive and transmit ports. diff --git a/include/srsran/ran/uci/uci_configuration.h b/include/srsran/ran/uci/uci_configuration.h index d593ade013..59a269fdca 100644 --- a/include/srsran/ran/uci/uci_configuration.h +++ b/include/srsran/ran/uci/uci_configuration.h @@ -24,8 +24,8 @@ #include "srsran/adt/static_vector.h" #include "srsran/adt/tiny_optional.h" -#include "srsran/adt/variant.h" #include +#include namespace srsran { @@ -89,8 +89,8 @@ struct uci_on_pusch { using beta_offsets_semi_static = beta_offsets; using beta_offsets_dynamic = static_vector; - std::optional> beta_offsets_cfg; - alpha_scaling_opt scaling; + std::optional> beta_offsets_cfg; + alpha_scaling_opt scaling; bool operator==(const uci_on_pusch& rhs) const { diff --git a/include/srsran/rlc/rlc_factory.h b/include/srsran/rlc/rlc_factory.h index 58b79f9c96..b3a6f4e2b8 100644 --- a/include/srsran/rlc/rlc_factory.h +++ b/include/srsran/rlc/rlc_factory.h @@ -23,6 +23,7 @@ #pragma once #include "srsran/pcap/rlc_pcap.h" +#include "srsran/ran/gnb_du_id.h" #include "srsran/rlc/rlc_config.h" #include "srsran/rlc/rlc_entity.h" #include "srsran/rlc/rlc_rx.h" @@ -34,7 +35,7 @@ namespace srsran { struct rlc_entity_creation_message { - uint32_t du_index; + gnb_du_id_t gnb_du_id; du_ue_index_t ue_index; rb_id_t rb_id; rlc_config config; diff --git a/include/srsran/rrc/rrc_ue.h b/include/srsran/rrc/rrc_ue.h index 6e3a7db305..20ade37de2 100644 --- a/include/srsran/rrc/rrc_ue.h +++ b/include/srsran/rrc/rrc_ue.h @@ -291,7 +291,8 @@ struct rrc_ue_reestablishment_context_response { security::security_context sec_context; std::optional capabilities; up_context up_ctx; - bool old_ue_fully_attached = false; + bool old_ue_fully_attached = false; + bool reestablishment_ongoing = false; }; /// Interface to notify about UE context updates. diff --git a/include/srsran/ru/ru_configuration.h b/include/srsran/ru/ru_configuration.h index 36b14efb7c..e5220aee47 100644 --- a/include/srsran/ru/ru_configuration.h +++ b/include/srsran/ru/ru_configuration.h @@ -22,16 +22,16 @@ #pragma once -#include "srsran/adt/variant.h" #include "srsran/ru/ru_dummy_configuration.h" #include "srsran/ru/ru_generic_configuration.h" #include "srsran/ru/ru_ofh_configuration.h" +#include namespace srsran { /// Radio Unit configuration. struct ru_configuration { - variant config; + std::variant config; }; } // namespace srsran diff --git a/include/srsran/ru/ru_ofh_configuration.h b/include/srsran/ru/ru_ofh_configuration.h index 881677e304..bcbb1c729c 100644 --- a/include/srsran/ru/ru_ofh_configuration.h +++ b/include/srsran/ru/ru_ofh_configuration.h @@ -129,10 +129,10 @@ struct ru_ofh_sector_dependencies { srslog::basic_logger* logger = nullptr; /// Downlink task executor. task_executor* downlink_executor; - /// Receiver task executor. - task_executor* receiver_executor = nullptr; - /// Transmitter task executor. - task_executor* transmitter_executor = nullptr; + /// Uplink task executor. + task_executor* uplink_executor = nullptr; + /// Message transmitter and receiver task executor. + task_executor* txrx_executor = nullptr; /// Optional Ethernet gateway. std::optional> eth_gateway; /// Optional Ethernet receiver. diff --git a/include/srsran/scheduler/config/bwp_configuration.h b/include/srsran/scheduler/config/bwp_configuration.h index 0e94b68427..0d348ad3d1 100644 --- a/include/srsran/scheduler/config/bwp_configuration.h +++ b/include/srsran/scheduler/config/bwp_configuration.h @@ -22,7 +22,6 @@ #pragma once -#include "dmrs.h" #include "srsran/adt/optional.h" #include "srsran/adt/slotted_array.h" #include "srsran/ran/band_helper.h" @@ -36,6 +35,7 @@ #include "srsran/ran/pucch/pucch_configuration.h" #include "srsran/ran/resource_allocation/ofdm_symbol_range.h" #include "srsran/ran/resource_block.h" +#include "srsran/scheduler/config/dmrs.h" #include "srsran/scheduler/vrb_alloc.h" #include diff --git a/include/srsran/scheduler/config/si_scheduling_config.h b/include/srsran/scheduler/config/si_scheduling_config.h index 6ad0675dad..771f0a5cc3 100644 --- a/include/srsran/scheduler/config/si_scheduling_config.h +++ b/include/srsran/scheduler/config/si_scheduling_config.h @@ -35,6 +35,9 @@ struct si_message_scheduling_config { units::bytes msg_len; /// Periodicity of the SI-message in radio frames. Values: {8, 16, 32, 64, 128, 256, 512}. unsigned period_radio_frames; + /// SI window position of the associated SI-message. See TS 38.331, \c SchedulingInfo2-r17. Values: {1,...,256}. + /// \remark This field is only applicable for release 17 \c SI-SchedulingInfo. + std::optional si_window_position; }; /// \brief Configuration of the SI message scheduling. diff --git a/include/srsran/scheduler/scheduler_feedback_handler.h b/include/srsran/scheduler/scheduler_feedback_handler.h index 0db4c6dd07..5437a60199 100644 --- a/include/srsran/scheduler/scheduler_feedback_handler.h +++ b/include/srsran/scheduler/scheduler_feedback_handler.h @@ -24,7 +24,6 @@ #include "srsran/adt/bounded_bitset.h" #include "srsran/adt/static_vector.h" -#include "srsran/adt/variant.h" #include "srsran/mac/bsr_format.h" #include "srsran/mac/lcid_dl_sch.h" #include "srsran/mac/phr_report.h" @@ -36,6 +35,7 @@ #include "srsran/ran/slot_point.h" #include "srsran/ran/uci/uci_constants.h" #include "srsran/scheduler/harq_id.h" +#include namespace srsran { @@ -128,7 +128,7 @@ struct uci_indication { /// RNTI value corresponding to the UE that generated this PDU. rnti_t crnti; /// UCI PDU multiplexed either in the PUSCH or encoded in the PUCCH. - variant pdu; + std::variant pdu; }; using uci_pdu_list = static_vector; diff --git a/include/srsran/scheduler/scheduler_metrics.h b/include/srsran/scheduler/scheduler_metrics.h index d755333395..9c43a19692 100644 --- a/include/srsran/scheduler/scheduler_metrics.h +++ b/include/srsran/scheduler/scheduler_metrics.h @@ -22,38 +22,39 @@ #pragma once -#include "srsran/adt/optional.h" #include "srsran/adt/span.h" #include "srsran/ran/pci.h" +#include "srsran/ran/phy_time_unit.h" #include "srsran/ran/rnti.h" #include "srsran/ran/sch/sch_mcs.h" +#include namespace srsran { /// \brief Snapshot of the metrics for a UE. struct scheduler_ue_metrics { - pci_t pci; - unsigned nof_prbs; - rnti_t rnti; - uint8_t cqi; - uint8_t ri; - sch_mcs_index dl_mcs; - double dl_prbs_used; - double dl_brate_kbps; - unsigned dl_nof_ok; - unsigned dl_nof_nok; - float pusch_snr_db; - float pusch_rsrp_db; - float pucch_snr_db; - sch_mcs_index ul_mcs; - double ul_prbs_used; - double ul_brate_kbps; - unsigned ul_nof_ok; - unsigned ul_nof_nok; - unsigned bsr; - unsigned dl_bs; - std::optional last_ta; - std::optional last_phr; + pci_t pci; + unsigned nof_prbs; + rnti_t rnti; + uint8_t cqi; + uint8_t ri; + sch_mcs_index dl_mcs; + double dl_prbs_used; + double dl_brate_kbps; + unsigned dl_nof_ok; + unsigned dl_nof_nok; + float pusch_snr_db; + float pusch_rsrp_db; + float pucch_snr_db; + sch_mcs_index ul_mcs; + double ul_prbs_used; + double ul_brate_kbps; + unsigned ul_nof_ok; + unsigned ul_nof_nok; + unsigned bsr; + unsigned dl_bs; + std::optional last_ta; + std::optional last_phr; }; /// \brief Notifier interface used by scheduler to report UE metrics. diff --git a/include/srsran/scheduler/scheduler_slot_handler.h b/include/srsran/scheduler/scheduler_slot_handler.h index 59a43403aa..d59f8569a2 100644 --- a/include/srsran/scheduler/scheduler_slot_handler.h +++ b/include/srsran/scheduler/scheduler_slot_handler.h @@ -220,7 +220,7 @@ struct dl_msg_lc_info { /// Number of scheduled bytes for this specific logical channel. {0..65535}. unsigned sched_bytes; /// Holds payload of CE except UE Contention Resolution Identity. - variant ce_payload; + std::variant ce_payload; }; struct dl_msg_tb_info { diff --git a/include/srsran/srsvec/accumulate.h b/include/srsran/srsvec/accumulate.h index bc0f956ff8..5cb6d1dbe4 100644 --- a/include/srsran/srsvec/accumulate.h +++ b/include/srsran/srsvec/accumulate.h @@ -22,12 +22,19 @@ #pragma once +#include "srsran/adt/complex.h" #include "srsran/adt/span.h" +#include namespace srsran { namespace srsvec { float accumulate(span x); +inline cf_t accumulate(span x) +{ + return std::accumulate(x.begin(), x.end(), cf_t()); +} + } // namespace srsvec } // namespace srsran diff --git a/include/srsran/support/async/protocol_transaction_manager.h b/include/srsran/support/async/protocol_transaction_manager.h index 3f992b70ab..28a17437f8 100644 --- a/include/srsran/support/async/protocol_transaction_manager.h +++ b/include/srsran/support/async/protocol_transaction_manager.h @@ -25,11 +25,11 @@ #include "async_event_source.h" #include "manual_event.h" #include "srsran/adt/expected.h" -#include "srsran/adt/variant.h" #include "srsran/support/compiler.h" #include "srsran/support/timers.h" #include #include +#include namespace srsran { @@ -353,8 +353,8 @@ class protocol_transaction_event_source private: friend class protocol_transaction_outcome_observer; - bool stopped = false; - async_event_source> ev_source; + bool stopped = false; + async_event_source> ev_source; }; /// \brief Observer of application protocol transaction outcome. @@ -364,7 +364,8 @@ class protocol_transaction_outcome_observer static_assert(not std::is_same::value, "Invalid Success Response"); static_assert(not std::is_same::value, "Invalid Success Response"); - using observer_type = async_single_event_observer>; + using observer_type = + async_single_event_observer>; public: using success_response_type = SuccessResp; @@ -390,35 +391,35 @@ class protocol_transaction_outcome_observer bool complete() const { return observer.complete(); } /// \brief Checks whether the result of transaction was successful. - bool successful() const { return complete() and variant_holds_alternative(observer.result()); } + bool successful() const { return complete() and std::holds_alternative(observer.result()); } /// \brief Checks whether the result of the transaction was a failure message. - bool failed() const { return complete() and variant_holds_alternative(observer.result()); } + bool failed() const { return complete() and std::holds_alternative(observer.result()); } /// \brief Checks if the protocol transaction could not be completed, due to abnormal conditions, cancellations or /// timeout. bool protocol_transaction_failed() const { - return complete() and variant_holds_alternative(observer.result()); + return complete() and std::holds_alternative(observer.result()); } /// \brief Checks whether there was a transaction timeout. bool timeout_expired() const { return protocol_transaction_failed() and - variant_get(observer.result()) == protocol_transaction_failure::timeout; + std::get(observer.result()) == protocol_transaction_failure::timeout; } /// \brief Result set by event source. const success_response_type& response() const { srsran_assert(successful(), "Trying to fetch incorrect transaction result"); - return variant_get(observer.result()); + return std::get(observer.result()); } const failure_response_type& failure() const { srsran_assert(failed(), "Trying to fetch incorrect transaction result"); - return variant_get(observer.result()); + return std::get(observer.result()); } /// Awaiter interface. diff --git a/include/srsran/support/executors/task_execution_manager.h b/include/srsran/support/executors/task_execution_manager.h index 218ea1b4e9..2ab39c28a0 100644 --- a/include/srsran/support/executors/task_execution_manager.h +++ b/include/srsran/support/executors/task_execution_manager.h @@ -25,9 +25,9 @@ #include "unique_thread.h" #include "srsran/adt/concurrent_queue.h" #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" #include "srsran/support/executors/task_executor.h" #include +#include namespace srsran { diff --git a/include/srsran/support/io/io_broker.h b/include/srsran/support/io/io_broker.h index dda988779b..e33da89854 100644 --- a/include/srsran/support/io/io_broker.h +++ b/include/srsran/support/io/io_broker.h @@ -23,7 +23,7 @@ #pragma once #include "srsran/support/executors/unique_thread.h" -#include +#include #include namespace srsran { @@ -50,34 +50,50 @@ class io_broker public: subscriber() = default; subscriber(io_broker& broker_, int fd_) : broker(&broker_), fd(fd_) {} - subscriber(subscriber&& other) noexcept : broker(other.broker), fd(other.fd.exchange(-1, std::memory_order_relaxed)) + subscriber(subscriber&& other) noexcept { + std::lock_guard lock(other.mutex); + broker = other.broker; + fd = std::exchange(other.fd, -1); } subscriber& operator=(subscriber&& other) noexcept { - reset(); + std::scoped_lock lock(mutex, other.mutex); + reset_nolock(); broker = other.broker; - fd = other.fd.exchange(-1, std::memory_order_relaxed); + fd = std::exchange(other.fd, -1); return *this; } ~subscriber() { reset(); } /// Checks whether the FD is connected to the broker. - bool registered() const { return fd.load(std::memory_order_relaxed) >= 0; } + bool registered() const + { + std::lock_guard lock(mutex); + return fd >= 0; + } /// Resets the handle, deregistering the FD from the broker. bool reset() { - int fd_tmp = fd.exchange(-1, std::memory_order_relaxed); + std::lock_guard lock(mutex); + return reset_nolock(); + } + + private: + bool reset_nolock() + { + int fd_tmp = std::exchange(fd, -1); if (fd_tmp >= 0) { return broker->unregister_fd(fd_tmp); } return false; } - private: - io_broker* broker = nullptr; - std::atomic fd{-1}; + // Mutex unique to the subscriber object. + mutable std::mutex mutex; + io_broker* broker = nullptr; + int fd = -1; }; /// Callback called when registered fd has data diff --git a/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h b/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h index a052fef5e1..09189255b8 100644 --- a/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h +++ b/include/srsran/support/memory_pool/fixed_size_memory_block_pool.h @@ -128,7 +128,24 @@ class fixed_size_memory_block_pool fixed_size_memory_block_pool& operator=(const fixed_size_memory_block_pool&) = delete; fixed_size_memory_block_pool& operator=(fixed_size_memory_block_pool&&) = delete; - ~fixed_size_memory_block_pool() {} + ~fixed_size_memory_block_pool() + { + if constexpr (DebugSanitizeAddress) { + unsigned rem_batches = nof_total_batches(); + free_memory_block_list list; + std::unordered_map addresses; + for (unsigned i = 0; i < rem_batches; i++) { + report_fatal_error_if_not(central_mem_cache.try_dequeue(list), "segments were lost {} < {}", i, rem_batches); + for (unsigned j = 0; j < block_batch_size; j++) { + void* p = list.try_pop(); + report_fatal_error_if_not(p != nullptr, "lost segment {} < {}", j, block_batch_size); + report_fatal_error_if_not(addresses.find((long int)p) == addresses.end(), "repeated segment detected"); + addresses.insert(std::make_pair((long int)p, true)); + } + } + report_fatal_error_if_not(not central_mem_cache.try_dequeue(list), "more batches than when initialized"); + } + } /// \brief Get instance of a memory pool singleton. static pool_type& get_instance(size_t nof_blocks = 0, size_t mem_block_size = 0) @@ -278,6 +295,11 @@ class fixed_size_memory_block_pool while (not local_cache.back().empty()) { parent->incomplete_batch.push(local_cache.back().try_pop()); if (parent->incomplete_batch.size() >= block_batch_size) { + // Note: Central_mem_cache uses a queue that uses atomic_fences. TSAN doesn't deal well with atomic + // fences, so we need to add a hint here. +#ifdef ENABLE_TSAN + __tsan_release((void*)parent->incomplete_batch.head); +#endif // The incomplete batch is now complete and can be pushed to the central cache. report_error_if_not(parent->central_mem_cache.enqueue(producer_token, parent->incomplete_batch), "Failed to push blocks to central cache"); diff --git a/include/srsran/support/sysinfo.h b/include/srsran/support/sysinfo.h index a9772727aa..2f72ecf0c4 100644 --- a/include/srsran/support/sysinfo.h +++ b/include/srsran/support/sysinfo.h @@ -23,6 +23,7 @@ #pragma once #include "srsran/srslog/logger.h" +#include namespace srsran { @@ -49,4 +50,10 @@ bool configure_cgroups(const srsran::os_sched_affinity_bitmask& isol_cpus); /// \brief Removes cgroups created by the gNB app. void cleanup_cgroups(); +/// \brief Check whether custom cgroups are configured in the system. +/// +/// \return Optional string containing list of custom cgroup paths ('housekeeping' and/or 'isolated') configured in the +/// system. +std::optional check_cgroups(); + } // namespace srsran diff --git a/include/srsran/support/timers.h b/include/srsran/support/timers.h index 52c2ec8d1e..aab9fe7df8 100644 --- a/include/srsran/support/timers.h +++ b/include/srsran/support/timers.h @@ -24,12 +24,13 @@ #include "srsran/adt/intrusive_list.h" #include "srsran/adt/unique_function.h" -#include "srsran/adt/variant.h" #include "srsran/support/executors/task_executor.h" #include "srsran/support/srsran_assert.h" #include #include #include +#include +#include namespace srsran { @@ -213,8 +214,9 @@ class timer_manager std::deque> failed_to_trigger_timers; /// Commands sent by the timer front-end to the backend. - std::mutex cmd_mutex; - std::vector>> pending_cmds, cmds_to_process; + std::mutex cmd_mutex; + std::vector>> pending_cmds; + std::vector>> cmds_to_process; }; /// \brief This class represents a timer which invokes a user-provided callback upon timer expiration. To setup a diff --git a/lib/asn1/asn1_utils.cpp b/lib/asn1/asn1_utils.cpp index 285f35add2..6d87a1589d 100644 --- a/lib/asn1/asn1_utils.cpp +++ b/lib/asn1/asn1_utils.cpp @@ -1476,8 +1476,9 @@ SRSASN_CODE ext_groups_unpacker_guard::unpack(cbit_ref& bref) { bref_tracker = &bref; // unpack nof of ext groups - HANDLE_CODE(unpack_norm_small_non_neg_whole_number(nof_unpacked_groups, bref)); - nof_unpacked_groups += 1; + uint32_t nof_ext_groups = 0; + HANDLE_CODE(unpack_norm_small_non_neg_whole_number(nof_ext_groups, bref)); + nof_unpacked_groups += nof_ext_groups + 1; resize(nof_unpacked_groups); // unpack each group presence flag diff --git a/lib/cu_cp/routines/amf_connection_setup_routine.cpp b/lib/cu_cp/routines/amf_connection_setup_routine.cpp index 8b3ee03a57..7612953cc4 100644 --- a/lib/cu_cp/routines/amf_connection_setup_routine.cpp +++ b/lib/cu_cp/routines/amf_connection_setup_routine.cpp @@ -39,7 +39,7 @@ void amf_connection_setup_routine::operator()(coro_context>& ct // Initiate NG Setup. CORO_AWAIT_VALUE(result_msg, send_ng_setup_request()); - CORO_RETURN(variant_holds_alternative(result_msg)); + CORO_RETURN(std::holds_alternative(result_msg)); } ngap_ng_setup_request amf_connection_setup_routine::fill_ng_setup_request() diff --git a/lib/cu_up/cu_up_impl.cpp b/lib/cu_up/cu_up_impl.cpp index 209e80d022..7fce307b98 100644 --- a/lib/cu_up/cu_up_impl.cpp +++ b/lib/cu_up/cu_up_impl.cpp @@ -35,7 +35,7 @@ void assert_cu_up_configuration_valid(const cu_up_configuration& cfg) { srsran_assert(cfg.ue_exec_pool != nullptr, "Invalid CU-UP UE executor pool"); srsran_assert(cfg.io_ul_executor != nullptr, "Invalid CU-UP IO UL executor"); - srsran_assert(cfg.e1ap.e1ap_conn_client != nullptr, "Invalid E1AP connection client"); + srsran_assert(cfg.e1ap.e1_conn_client != nullptr, "Invalid E1 connection client"); srsran_assert(cfg.f1u_gateway != nullptr, "Invalid F1-U connector"); srsran_assert(cfg.ngu_gw != nullptr, "Invalid N3 gateway"); srsran_assert(cfg.gtpu_pcap != nullptr, "Invalid GTP-U pcap"); @@ -85,7 +85,7 @@ cu_up::cu_up(const cu_up_configuration& config_) : cfg(config_), main_ctrl_loop( f1u_teid_allocator = create_gtpu_allocator(f1u_alloc_msg); /// > Create e1ap - e1ap = create_e1ap(*cfg.e1ap.e1ap_conn_client, e1ap_cu_up_ev_notifier, *cfg.timers, *cfg.ctrl_executor); + e1ap = create_e1ap(*cfg.e1ap.e1_conn_client, e1ap_cu_up_ev_notifier, *cfg.timers, *cfg.ctrl_executor); e1ap_cu_up_ev_notifier.connect_cu_up(*this); cfg.e1ap.e1ap_conn_mng = e1ap.get(); diff --git a/lib/cu_up/pdu_session_manager_impl.cpp b/lib/cu_up/pdu_session_manager_impl.cpp index ff69a48de1..070a8b0520 100644 --- a/lib/cu_up/pdu_session_manager_impl.cpp +++ b/lib/cu_up/pdu_session_manager_impl.cpp @@ -218,9 +218,7 @@ drb_setup_result pdu_session_manager_impl::handle_drb_to_setup_item(pdu_session& new_drb->f1u_cfg, f1u_ul_tunnel_addr, new_drb->f1u_gateway_rx_to_nru_adapter, - ue_ul_exec, - ue_dl_timer_factory, - ue_inactivity_timer); + ue_ul_exec); new_drb->f1u = srs_cu_up::create_f1u_bearer(ue_index, new_drb->drb_id, @@ -399,31 +397,23 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif auto& drb = drb_iter->second; if (new_tnl_info_required) { - if (not f1u_teid_allocator.release_teid(drb->f1u_ul_teid)) { - logger.log_error("Could not free old ul_teid={}", drb->f1u_ul_teid); - } - // Allocate new UL TEID for DRB expected ret = f1u_teid_allocator.request_teid(); if (not ret.has_value()) { - logger.log_error("Could not allocate ul_teid"); + logger.log_error("Could not allocate ul_teid for F1-U tunnel"); continue; } - drb->f1u_ul_teid = ret.value(); + gtpu_teid_t old_f1u_ul_teid = drb->f1u_ul_teid; // Release old UL TEID later after disconnected from F1-U gateway + drb->f1u_ul_teid = ret.value(); + logger.log_info("Replacing F1-U tunnel. old_ul_teid={} new_ul_teid={}", old_f1u_ul_teid, drb->f1u_ul_teid); // Create UL UP TNL address. up_transport_layer_info f1u_ul_tunnel_addr(transport_layer_address::create_from_string(net_config.f1u_bind_addr), drb->f1u_ul_teid); // create new F1-U and connect it. This will automatically disconnect the old F1-U. - drb->f1u_gw_bearer = f1u_gw.create_cu_bearer(ue_index, - drb->drb_id, - drb->f1u_cfg, - f1u_ul_tunnel_addr, - drb->f1u_gateway_rx_to_nru_adapter, - ue_ul_exec, - ue_dl_timer_factory, - ue_inactivity_timer); + drb->f1u_gw_bearer = f1u_gw.create_cu_bearer( + ue_index, drb->drb_id, drb->f1u_cfg, f1u_ul_tunnel_addr, drb->f1u_gateway_rx_to_nru_adapter, ue_ul_exec); drb->f1u = srs_cu_up::create_f1u_bearer(ue_index, drb->drb_id, @@ -439,10 +429,25 @@ pdu_session_manager_impl::modify_pdu_session(const e1ap_pdu_session_res_to_modif drb_iter->second->pdcp_to_f1u_adapter.disconnect_f1u(); drb_result.gtp_tunnel = f1u_ul_tunnel_addr; + + // Connect F1-U GW bearer RX adapter to NR-U bearer + drb_iter->second->f1u_gateway_rx_to_nru_adapter.connect_nru_bearer(drb_iter->second->f1u->get_rx_pdu_handler()); + + // Connect F1-U's "F1-U->PDCP adapter" directly to PDCP + drb_iter->second->f1u_to_pdcp_adapter.connect_pdcp(drb_iter->second->pdcp->get_rx_lower_interface(), + drb_iter->second->pdcp->get_tx_lower_interface()); + + // Release old UL TEID of DRB after F1-U bearer disconnected from F1-U gateway + if (not f1u_teid_allocator.release_teid(old_f1u_ul_teid)) { + logger.log_error("Could not free old F1-U ul_teid={}", old_f1u_ul_teid); + } } // F1-U apply modification if (!drb_to_mod.dl_up_params.empty()) { + logger.log_info("Attaching dl_teid={} to F1-U tunnel with ul_teid={}", + drb_to_mod.dl_up_params[0].up_tnl_info, + drb_iter->second->f1u_ul_teid); f1u_gw.attach_dl_teid( up_transport_layer_info(transport_layer_address::create_from_string(net_config.f1u_bind_addr), drb_iter->second->f1u_ul_teid), diff --git a/lib/du/CMakeLists.txt b/lib/du/CMakeLists.txt index c05a7bd35d..c7a4d38fdc 100644 --- a/lib/du/CMakeLists.txt +++ b/lib/du/CMakeLists.txt @@ -21,10 +21,14 @@ set(SOURCES du_cell_config_validation.cpp du_update_config_helpers.cpp) add_library(srsran_du_config_validators STATIC ${SOURCES}) -add_library(srsran_du STATIC - du_wrapper_impl.cpp - du_wrapper_factory.cpp +add_library(srsran_du_high_wrapper STATIC du_high_wrapper_factory.cpp du_high_wrapper_impl.cpp adapters/fapi_factory.cpp) -target_link_libraries(srsran_du PUBLIC srsran_du_config_validators srsran_du_high srsran_du_low) +target_link_libraries(srsran_du_high_wrapper PUBLIC srsran_du_config_validators srsran_du_high srsran_mac_fapi_adaptor srsran_fapi) + + +add_library(srsran_du_wrapper STATIC + du_wrapper_impl.cpp + du_wrapper_factory.cpp) +target_link_libraries(srsran_du_wrapper PUBLIC srsran_du_high_wrapper srsran_du_low_wrapper) diff --git a/lib/du/du_cell_config_validation.cpp b/lib/du/du_cell_config_validation.cpp index f0648cdf84..602fe02e73 100644 --- a/lib/du/du_cell_config_validation.cpp +++ b/lib/du/du_cell_config_validation.cpp @@ -187,8 +187,8 @@ static check_outcome check_dl_config_common(const du_cell_config& cell_cfg) for (const search_space_configuration& ss : bwp.pdcch_common.search_spaces) { HANDLE_ERROR(is_search_space_valid(ss)); CHECK_TRUE(ss.is_common_search_space(), "Common SearchSpace#{} type", ss.get_id()); - const auto dci_format_variant = ss.get_monitored_dci_formats(); - const auto dci_format = variant_get(dci_format_variant); + const auto& dci_format_variant = ss.get_monitored_dci_formats(); + const auto& dci_format = std::get(dci_format_variant); CHECK_TRUE(dci_format.f0_0_and_f1_0, "Common SearchSpace#{} must enable DCI format1_0 and format0_0", ss.get_id()); if (ss.get_coreset_id() == 0) { CHECK_TRUE(bwp.pdcch_common.coreset0.has_value(), @@ -245,10 +245,10 @@ static check_outcome is_nof_monitored_pdcch_candidates_per_slot_within_limit(con const bwp_downlink_dedicated& bwp_ded = cell_cfg.ue_ded_serv_cell_cfg.init_dl_bwp; for (const search_space_configuration& ss : bwp_ded.pdcch_cfg->search_spaces) { - const auto dci_format_variant = ss.get_monitored_dci_formats(); - const bool non_fallback_dci_fmt = - variant_holds_alternative(dci_format_variant) and - variant_get(dci_format_variant) == + const auto& dci_format_variant = ss.get_monitored_dci_formats(); + const bool non_fallback_dci_fmt = + std::holds_alternative(dci_format_variant) and + std::get(dci_format_variant) == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1; unsigned nof_monitored_pdcch_candidates; @@ -287,10 +287,10 @@ static check_outcome is_nof_monitored_pdcch_candidates_per_slot_within_limit(con } for (const search_space_configuration& ss : bwp_cmn.pdcch_common.search_spaces) { - const auto dci_format_variant = ss.get_monitored_dci_formats(); - const bool non_fallback_dci_fmt = - variant_holds_alternative(dci_format_variant) and - variant_get(dci_format_variant) == + const auto& dci_format_variant = ss.get_monitored_dci_formats(); + const bool non_fallback_dci_fmt = + std::holds_alternative(dci_format_variant) and + std::get(dci_format_variant) == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1; unsigned nof_monitored_pdcch_candidates; @@ -362,7 +362,7 @@ static check_outcome check_dl_config_dedicated(const du_cell_config& cell_cfg) for (const search_space_configuration& ss : bwp.pdcch_cfg->search_spaces) { const bool fallback_dci_format_in_ss2 = ss.is_common_search_space() or - not(variant_get(ss.get_monitored_dci_formats()) == + not(std::get(ss.get_monitored_dci_formats()) == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1); if (fallback_dci_format_in_ss2) { @@ -502,7 +502,7 @@ static check_outcome check_ul_config_dedicated(const du_cell_config& cell_cfg) const search_space_configuration& ss2 = cell_cfg.ue_ded_serv_cell_cfg.init_dl_bwp.pdcch_cfg->search_spaces.back(); const bool fallback_dci_format_in_ss2 = ss2.is_common_search_space() or - not(variant_get(ss2.get_monitored_dci_formats()) == + not(std::get(ss2.get_monitored_dci_formats()) == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1); if (fallback_dci_format_in_ss2) { CHECK_TRUE(bwp.pusch_cfg->mcs_table != pusch_mcs_table::qam256, @@ -543,7 +543,7 @@ static check_outcome check_tdd_ul_dl_config(const du_cell_config& cell_cfg) const std::optional& common_coreset = common_pdcch_cfg.common_coreset; CHECK_TRUE(coreset0.has_value(), "CORESET#0 not configured"); - const pdcch_type0_css_occasion_pattern1_description ss0_occasion = pdcch_type0_css_occasions_get_pattern1( + const pdcch_type0_css_occasion_pattern1_description& ss0_occasion = pdcch_type0_css_occasions_get_pattern1( pdcch_type0_css_occasion_pattern1_configuration{.is_fr2 = false, .ss_zero_index = static_cast(cell_cfg.searchspace0_idx), .nof_symb_coreset = coreset0->duration}); diff --git a/lib/du/du_update_config_helpers.cpp b/lib/du/du_update_config_helpers.cpp index 5360af166f..a427d268d4 100644 --- a/lib/du/du_update_config_helpers.cpp +++ b/lib/du/du_update_config_helpers.cpp @@ -25,11 +25,11 @@ using namespace srsran; -// Helper that computes the greatest RB index used by the PUCCH resources on the BWP's left side. Note that the PUCCH -// resources are located at in 2 separate blocks, at both sides of the BWP, i.e., 1 block on the left side (where -// indices is 0, 1, 2, ...) and 1 block on the right side (where indices are ..., N_BWP_RBs -3, N_BWP_RBs-2, -// N_BWP_RBs-1) of the BWP. This function considers only the block of PUCCH resources on the BWP's left side and returns -// the RB with the greatest index of this block. +/// Helper that computes the greatest RB index used by the PUCCH resources on the BWP's left side. Note that the PUCCH +/// resources are located at in 2 separate blocks, at both sides of the BWP, i.e., 1 block on the left side (where +/// indices is 0, 1, 2, ...) and 1 block on the right side (where indices are ..., N_BWP_RBs -3, N_BWP_RBs-2, +/// N_BWP_RBs-1) of the BWP. This function considers only the block of PUCCH resources on the BWP's left side and +/// returns the RB with the greatest index of this block. static unsigned greatest_used_rb_on_bwp_left_side(const pucch_resource& res, unsigned bwp_size) { // Return true if the given PRB is on the BWP's left side (i.e., if the PRB index is less than the BWP's size measured @@ -37,9 +37,9 @@ static unsigned greatest_used_rb_on_bwp_left_side(const pucch_resource& res, uns auto is_on_bwp_left_side = [bwp_size](unsigned prb) { return prb < bwp_size / 2; }; srsran_assert((res.format == srsran::pucch_format::FORMAT_1 and - variant_holds_alternative(res.format_params)) or + std::holds_alternative(res.format_params)) or (res.format == srsran::pucch_format::FORMAT_2 or - variant_holds_alternative(res.format_params)), + std::holds_alternative(res.format_params)), "Only PUCCH Format 1 and 2 currently supported."); unsigned max_rb_idx_on_left_side = 0; @@ -57,7 +57,7 @@ static unsigned greatest_used_rb_on_bwp_left_side(const pucch_resource& res, uns } if (res.format == srsran::pucch_format::FORMAT_2) { // Check if first hop and second hop separately. - const unsigned nof_prbs = variant_get(res.format_params).nof_prbs; + const unsigned nof_prbs = std::get(res.format_params).nof_prbs; if (is_on_bwp_left_side(res.starting_prb + nof_prbs)) { max_rb_idx_on_left_side = std::max(res.starting_prb + nof_prbs, max_rb_idx_on_left_side); @@ -77,7 +77,7 @@ unsigned srsran::config_helpers::compute_prach_frequency_start(const pucch_build const unsigned pucch_to_prach_guardband = is_long_prach ? 0U : 3U; // 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( + 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_sr_resources, user_params.nof_ue_pucch_f2_res_harq.to_uint() * user_params.nof_cell_harq_pucch_res_sets + @@ -111,15 +111,15 @@ void srsran::config_helpers::compute_nof_sr_csi_pucch_res(pucch_builder_params& const unsigned max_pucch_grants_per_sr_csi = max_pucch_grants_per_slot - 1U; if (csi_period_msec.has_value()) { - const unsigned required_nof_sr_resources = static_cast( + const unsigned required_nof_sr_resources = std::ceil(static_cast(max_pucch_grants_per_sr_csi * csi_period_msec.value()) / - (static_cast(sr_period_msec) + static_cast(csi_period_msec.value())))); + (static_cast(sr_period_msec) + static_cast(csi_period_msec.value()))); user_params.nof_sr_resources = std::min(required_nof_sr_resources, user_params.nof_sr_resources); - const unsigned required_nof_csi_resources = static_cast( + const unsigned required_nof_csi_resources = std::ceil(static_cast(max_pucch_grants_per_sr_csi * sr_period_msec) / - (static_cast(sr_period_msec) + static_cast(csi_period_msec.value())))); + (static_cast(sr_period_msec) + static_cast(csi_period_msec.value()))); user_params.nof_csi_resources = std::min(required_nof_csi_resources, user_params.nof_csi_resources); } else { diff --git a/lib/du/du_wrapper_factory.cpp b/lib/du/du_wrapper_factory.cpp index c6b44942cf..ff5d87ecd4 100644 --- a/lib/du/du_wrapper_factory.cpp +++ b/lib/du/du_wrapper_factory.cpp @@ -36,7 +36,7 @@ std::unique_ptr srsran::make_du_wrapper(const du_wrapper_config& du_ // Create DU high wrapper. du_high_wrapper_dependencies high_wrapper_dependencies; - for (unsigned i = 0, e = du_cfg.du_low_cfg.prach_ports.size(); i != e; ++i) { + for (unsigned i = 0, e = du_cfg.du_low_cfg.du_low_cfg.cells.size(); i != e; ++i) { high_wrapper_dependencies.sectors.push_back( {&dependencies.du_lo->get_slot_message_gateway(i), &dependencies.du_lo->get_slot_last_message_notifier(i)}); } diff --git a/lib/du_high/CMakeLists.txt b/lib/du_high/CMakeLists.txt index 841cdf21ed..c9429991d1 100644 --- a/lib/du_high/CMakeLists.txt +++ b/lib/du_high/CMakeLists.txt @@ -18,5 +18,9 @@ # and at http://www.gnu.org/licenses/. # -add_library(srsran_du_high du_high_impl.cpp adapters/mac_test_mode_adapter.cpp du_high_factory.cpp) +add_library(srsran_du_high + du_high_impl.cpp + adapters/mac_test_mode_adapter.cpp + adapters/f1ap_test_mode_adapter.cpp + du_high_factory.cpp) target_link_libraries(srsran_du_high srslog srsran_support srsran_du_manager srsran_mac srsran_f1ap_du srsran_e2) diff --git a/lib/du_high/adapters/f1ap_test_mode_adapter.cpp b/lib/du_high/adapters/f1ap_test_mode_adapter.cpp new file mode 100644 index 0000000000..cc3264c0e7 --- /dev/null +++ b/lib/du_high/adapters/f1ap_test_mode_adapter.cpp @@ -0,0 +1,256 @@ +/* + * + * 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 "f1ap_test_mode_adapter.h" +#include "srsran/asn1/f1ap/common.h" +#include "srsran/asn1/f1ap/f1ap_pdu_contents_ue.h" +#include "srsran/f1ap/common/f1ap_message.h" +#include "srsran/f1ap/du/f1ap_du_factory.h" + +using namespace srsran; +using namespace srs_du; + +namespace { + +class f1ap_test_mode_adapter final : public f1ap_du, public f1c_connection_client +{ +public: + f1ap_test_mode_adapter(const du_test_config::test_ue_config& test_ue_cfg_, + f1c_connection_client& f1c_client_handler_, + task_executor& ctrl_exec_) : + test_ue_cfg(test_ue_cfg_), f1c_client(f1c_client_handler_), ctrl_exec(ctrl_exec_) + { + } + + void connect(std::unique_ptr adapted_) { adapted = std::move(adapted_); } + + // F1AP interface. + void handle_message(const f1ap_message& msg) override { adapted->handle_message(msg); } + void handle_rrc_delivery_report(const f1ap_rrc_delivery_report_msg& report) override + { + adapted->handle_rrc_delivery_report(report); + } + SRSRAN_NODISCARD bool connect_to_cu_cp() override { return adapted->connect_to_cu_cp(); } + async_task handle_f1_setup_request(const f1_setup_request_message& request) override + { + return adapted->handle_f1_setup_request(request); + } + async_task handle_f1_removal_request() override { return adapted->handle_f1_removal_request(); } + f1ap_ue_creation_response handle_ue_creation_request(const f1ap_ue_creation_request& msg) override + { + return adapted->handle_ue_creation_request(msg); + } + f1ap_ue_configuration_response handle_ue_configuration_request(const f1ap_ue_configuration_request& msg) override + { + return adapted->handle_ue_configuration_request(msg); + } + void handle_ue_deletion_request(du_ue_index_t ue_index) override + { + return adapted->handle_ue_deletion_request(ue_index); + } + void handle_ue_context_release_request(const f1ap_ue_context_release_request& request) override + { + return adapted->handle_ue_context_release_request(request); + } + async_task + handle_ue_context_modification_required(const f1ap_ue_context_modification_required& msg) override + { + return adapted->handle_ue_context_modification_required(msg); + } + void handle_ue_inactivity_notification(const f1ap_ue_inactivity_notification_message& msg) override + { + return adapted->handle_ue_inactivity_notification(msg); + } + void handle_notify(const f1ap_notify_message& msg) override { return adapted->handle_notify(msg); } + gnb_cu_ue_f1ap_id_t get_gnb_cu_ue_f1ap_id(const du_ue_index_t& ue_index) override + { + return adapted->get_gnb_cu_ue_f1ap_id(ue_index); + } + gnb_cu_ue_f1ap_id_t get_gnb_cu_ue_f1ap_id(const gnb_du_ue_f1ap_id_t& gnb_du_ue_f1ap_id) override + { + return adapted->get_gnb_cu_ue_f1ap_id(gnb_du_ue_f1ap_id); + } + gnb_du_ue_f1ap_id_t get_gnb_du_ue_f1ap_id(const du_ue_index_t& ue_index) override + { + return adapted->get_gnb_du_ue_f1ap_id(ue_index); + } + gnb_du_ue_f1ap_id_t get_gnb_du_ue_f1ap_id(const gnb_cu_ue_f1ap_id_t& gnb_cu_ue_f1ap_id) override + { + return adapted->get_gnb_du_ue_f1ap_id(gnb_cu_ue_f1ap_id); + } + du_ue_index_t get_ue_index(const gnb_du_ue_f1ap_id_t& gnb_du_ue_f1ap_id) override + { + return adapted->get_ue_index(gnb_du_ue_f1ap_id); + } + du_ue_index_t get_ue_index(const gnb_cu_ue_f1ap_id_t& gnb_cu_ue_f1ap_id) override + { + return adapted->get_ue_index(gnb_cu_ue_f1ap_id); + } + + // F1-C client interface. + std::unique_ptr + handle_du_connection_request(std::unique_ptr du_rx_pdu_notifier) override; + +private: + class f1ap_to_gw_pdu_interceptor; + class gw_to_f1ap_pdu_interceptor; + + bool is_test_mode_ue(rnti_t rnti) const + { + if (rnti >= test_ue_cfg.rnti and rnti < to_rnti((unsigned)test_ue_cfg.rnti + test_ue_cfg.nof_ues)) { + return true; + } + return false; + } + + void handle_cu_cp_message(const f1ap_message& msg) + { + using namespace asn1::f1ap; + + if (msg.pdu.type().value == f1ap_pdu_c::types_opts::init_msg) { + if (msg.pdu.init_msg().value.type().value == f1ap_elem_procs_o::init_msg_c::types_opts::dl_rrc_msg_transfer) { + auto& ie = msg.pdu.init_msg().value.dl_rrc_msg_transfer(); + gnb_du_ue_f1ap_id_t du_ue_id = int_to_gnb_du_ue_f1ap_id(ie->gnb_du_ue_f1ap_id); + auto it = du_ue_to_rnti.find(du_ue_id); + if (it != du_ue_to_rnti.end()) { + // It is a test mode UE. + if (ie->srb_id == 0) { + // An RRC Setup was sent to the DU. We should generate and send the RRC Setup Complete back to the CU-CP + // to complete the RRC Setup procedure. + f1ap_message pdu_resp; + pdu_resp.pdu.set_init_msg().load_info_obj(ASN1_F1AP_ID_UL_RRC_MSG_TRANSFER); + auto& resp_ie = pdu_resp.pdu.init_msg().value.ul_rrc_msg_transfer(); + resp_ie->srb_id = 1; + resp_ie->gnb_du_ue_f1ap_id = ie->gnb_du_ue_f1ap_id; + resp_ie->gnb_cu_ue_f1ap_id = ie->gnb_cu_ue_f1ap_id; + resp_ie->rrc_container.from_string("0000100005df80105e400340403c443c3fc000040c951da60b80b8380000000000"); + logger.info("Test Mode: Injected F1AP UL RRC Message (containing rrcSetupComplete)"); + tx_notifier->on_new_message(pdu_resp); + } + } + } + } + } + + const du_test_config::test_ue_config& test_ue_cfg; + f1c_connection_client& f1c_client; + task_executor& ctrl_exec; + srslog::basic_logger& logger = srslog::fetch_basic_logger("DU-F1"); + + std::unordered_map du_ue_to_rnti; + + std::unique_ptr adapted; + + std::unique_ptr tx_notifier; +}; + +/// Class that intercepts F1AP PDUs sent by the F1AP-DU to the F1-C GW. +class f1ap_test_mode_adapter::f1ap_to_gw_pdu_interceptor : public f1ap_message_notifier +{ +public: + f1ap_to_gw_pdu_interceptor(f1ap_test_mode_adapter& parent_) : parent(parent_), adapted_notif(*parent.tx_notifier) {} + ~f1ap_to_gw_pdu_interceptor() override + { + // Called by F1AP-DU thread when the DU wants to release the connection. + parent.tx_notifier.reset(); + } + + void on_new_message(const f1ap_message& msg) override + { + using namespace asn1::f1ap; + + if (msg.pdu.type().value == f1ap_pdu_c::types_opts::init_msg) { + switch (msg.pdu.init_msg().value.type().value) { + case f1ap_elem_procs_o::init_msg_c::types_opts::init_ul_rrc_msg_transfer: { + auto& ie = msg.pdu.init_msg().value.init_ul_rrc_msg_transfer(); + // In case of test mode, save gNB-DU-UE-F1AP-ID of test mode UE. + rnti_t rnti = to_rnti(ie->c_rnti); + if (parent.is_test_mode_ue(rnti)) { + gnb_du_ue_f1ap_id_t du_ue_id = int_to_gnb_du_ue_f1ap_id(ie->gnb_du_ue_f1ap_id); + parent.du_ue_to_rnti.insert(std::make_pair(du_ue_id, rnti)); + } + } break; + default: + break; + } + } + + // Forward message to adapted notifier. + adapted_notif.on_new_message(msg); + } + +private: + f1ap_test_mode_adapter& parent; + f1ap_message_notifier& adapted_notif; +}; + +class f1ap_test_mode_adapter::gw_to_f1ap_pdu_interceptor : public f1ap_message_notifier +{ +public: + gw_to_f1ap_pdu_interceptor(f1ap_test_mode_adapter& parent_, std::unique_ptr adapted_notif_) : + parent(parent_), adapted_notif(std::move(adapted_notif_)) + { + } + + void on_new_message(const f1ap_message& msg) override + { + // Forward message to F1AP-DU. + adapted_notif->on_new_message(msg); + + // Handle intercepted message from F1AP-CU. + if (not parent.ctrl_exec.defer([this, msg]() { parent.handle_cu_cp_message(msg); })) { + parent.logger.error("Failed to handle DL F1AP PDU"); + } + } + +private: + f1ap_test_mode_adapter& parent; + std::unique_ptr adapted_notif; +}; + +// F1-C client interface. +std::unique_ptr +f1ap_test_mode_adapter::handle_du_connection_request(std::unique_ptr du_rx_pdu_notifier) +{ + tx_notifier = f1c_client.handle_du_connection_request( + std::make_unique(*this, std::move(du_rx_pdu_notifier))); + return std::make_unique(*this); +} + +} // namespace + +std::unique_ptr srsran::srs_du::create_du_high_f1ap(f1c_connection_client& f1c_client_handler, + f1ap_du_configurator& du_mng, + task_executor& ctrl_exec, + du_high_ue_executor_mapper& ue_exec_mapper, + f1ap_du_paging_notifier& paging_notifier, + const du_test_config& test_cfg) +{ + if (not test_cfg.test_ue.has_value()) { + return create_f1ap(f1c_client_handler, du_mng, ctrl_exec, ue_exec_mapper, paging_notifier); + } + + // Create a F1AP test mode adapter that wraps the real F1AP and intercepts messages to the F1-C client. + auto f1ap_testmode = std::make_unique(*test_cfg.test_ue, f1c_client_handler, ctrl_exec); + f1ap_testmode->connect(create_f1ap(*f1ap_testmode, du_mng, ctrl_exec, ue_exec_mapper, paging_notifier)); + return f1ap_testmode; +} diff --git a/lib/du_high/adapters/f1ap_test_mode_adapter.h b/lib/du_high/adapters/f1ap_test_mode_adapter.h new file mode 100644 index 0000000000..4fa303b005 --- /dev/null +++ b/lib/du_high/adapters/f1ap_test_mode_adapter.h @@ -0,0 +1,43 @@ +/* + * + * 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/du/du_test_config.h" +#include "srsran/du_high/du_high_executor_mapper.h" +#include "srsran/f1ap/du/f1ap_du.h" +#include "srsran/f1ap/gateways/f1c_connection_client.h" +#include + +namespace srsran { +namespace srs_du { + +/// Creates an F1AP handler for the DU-high. +std::unique_ptr create_du_high_f1ap(f1c_connection_client& f1c_client_handler, + f1ap_du_configurator& du_mng, + task_executor& ctrl_exec, + du_high_ue_executor_mapper& ue_exec_mapper, + f1ap_du_paging_notifier& paging_notifier, + const du_test_config& test_cfg); + +} // namespace srs_du +} // namespace srsran \ No newline at end of file diff --git a/lib/du_high/adapters/mac_test_mode_adapter.cpp b/lib/du_high/adapters/mac_test_mode_adapter.cpp index 1675c0e238..513685e662 100644 --- a/lib/du_high/adapters/mac_test_mode_adapter.cpp +++ b/lib/du_high/adapters/mac_test_mode_adapter.cpp @@ -55,6 +55,8 @@ static expected create_test_pdu_with_bsr(slot_point sl_r sl_rx, to_du_cell_index(0), mac_rx_pdu_list{mac_rx_pdu{test_rnti, 0, harq_id, std::move(buf.value())}}}; } +namespace { + /// \brief Adapter for the MAC SDU TX builder that auto fills the DL buffer state update. class test_ue_mac_sdu_tx_builder_adapter : public mac_sdu_tx_builder { @@ -76,9 +78,11 @@ class test_ue_mac_sdu_tx_builder_adapter : public mac_sdu_tx_builder unsigned on_buffer_state_update() override { return TEST_UE_DL_BUFFER_STATE_UPDATE_SIZE; } private: - byte_buffer tx_sdu = {}; + byte_buffer tx_sdu; }; +} // namespace + mac_test_mode_cell_adapter::mac_test_mode_cell_adapter(const srs_du::du_test_config::test_ue_config& test_ue_cfg_, const mac_cell_creation_request& cell_cfg, mac_cell_control_information_handler& adapted_, @@ -280,8 +284,8 @@ static bool pucch_info_and_uci_ind_match(const pucch_info& pucch, const mac_uci_ return false; } if (pucch.format == pucch_format::FORMAT_1 and - variant_holds_alternative(uci_ind.pdu)) { - const auto& f1_ind = variant_get(uci_ind.pdu); + std::holds_alternative(uci_ind.pdu)) { + const auto& f1_ind = std::get(uci_ind.pdu); if (f1_ind.sr_info.has_value() != (pucch.format_1.sr_bits != sr_nof_bits::no_sr)) { return false; } @@ -291,8 +295,8 @@ static bool pucch_info_and_uci_ind_match(const pucch_info& pucch, const mac_uci_ return true; } if (pucch.format == pucch_format::FORMAT_2 and - variant_holds_alternative(uci_ind.pdu)) { - const auto& f2_ind = variant_get(uci_ind.pdu); + std::holds_alternative(uci_ind.pdu)) { + const auto& f2_ind = std::get(uci_ind.pdu); if (f2_ind.sr_info.has_value() != (pucch.format_2.sr_bits != sr_nof_bits::no_sr)) { return false; } @@ -318,29 +322,31 @@ void mac_test_mode_cell_adapter::forward_uci_ind_to_mac(const mac_uci_indication // Update buffer states. for (const mac_uci_pdu& pdu : uci_msg.ucis) { - if (ue_info_mgr.is_test_ue(pdu.rnti) and variant_holds_alternative(pdu.pdu)) { - const auto& f1_ind = variant_get(pdu.pdu); - - if (f1_ind.harq_info.has_value()) { - // In case of PUCCH F1 with HARQ-ACK bits, we assume that the Msg4 has been received. At this point, we - // update the test UE with positive DL buffer states and BSR. - if (not ue_info_mgr.is_msg4_rxed(pdu.rnti)) { - if (test_ue_cfg.pdsch_active) { - // Update DL buffer state automatically. - dl_bs_notifier(pdu.rnti); - } + if (ue_info_mgr.is_test_ue(pdu.rnti) and std::holds_alternative(pdu.pdu)) { + const auto& f1_ind = std::get(pdu.pdu); - if (test_ue_cfg.pusch_active) { - auto rx_pdu = create_test_pdu_with_bsr(uci_msg.sl_rx, pdu.rnti, to_harq_id(0)); - if (rx_pdu.is_error()) { - logger.warning("Unable to create test PDU with BSR"); - continue; - } - // In case of PUSCH test mode is enabled, push a BSR to trigger the first PUSCH. - pdu_handler.handle_rx_data_indication(std::move(rx_pdu.value())); + if (not f1_ind.harq_info.has_value()) { + continue; + } + + // In case of PUCCH F1 with HARQ-ACK bits, we assume that the Msg4 has been received. At this point, we + // update the test UE with positive DL buffer states and BSR. + if (not ue_info_mgr.is_msg4_rxed(pdu.rnti)) { + if (test_ue_cfg.pdsch_active) { + // Update DL buffer state automatically. + dl_bs_notifier(pdu.rnti); + } + + if (test_ue_cfg.pusch_active) { + auto rx_pdu = create_test_pdu_with_bsr(uci_msg.sl_rx, pdu.rnti, to_harq_id(0)); + if (rx_pdu.is_error()) { + logger.warning("Unable to create test PDU with BSR"); + continue; } - ue_info_mgr.msg4_rxed(pdu.rnti, true); + // In case of PUSCH test mode is enabled, push a BSR to trigger the first PUSCH. + pdu_handler.handle_rx_data_indication(std::move(rx_pdu.value())); } + ue_info_mgr.msg4_rxed(pdu.rnti, true); } } } @@ -392,10 +398,10 @@ void mac_test_mode_cell_adapter::handle_uci(const mac_uci_indication_message& ms mac_uci_pdu& test_uci = msg_copy.ucis.back(); bool entry_found = false; - if (variant_holds_alternative(test_uci.pdu)) { + if (std::holds_alternative(test_uci.pdu)) { for (const ul_sched_info& pusch : entry.puschs) { if (pusch.pusch_cfg.rnti == pdu.rnti and pusch.uci.has_value()) { - fill_uci_pdu(variant_get(test_uci.pdu), pusch); + fill_uci_pdu(std::get(test_uci.pdu), pusch); entry_found = true; } } @@ -405,9 +411,9 @@ void mac_test_mode_cell_adapter::handle_uci(const mac_uci_indication_message& ms if (pucch_info_and_uci_ind_match(pucch, test_uci)) { // Intercept the UCI indication and force HARQ-ACK=ACK and UCI. if (pucch.format == pucch_format::FORMAT_1) { - fill_uci_pdu(variant_get(test_uci.pdu), pucch); + fill_uci_pdu(std::get(test_uci.pdu), pucch); } else { - fill_uci_pdu(variant_get(test_uci.pdu), pucch); + fill_uci_pdu(std::get(test_uci.pdu), pucch); } entry_found = true; } @@ -594,7 +600,7 @@ void mac_test_mode_adapter::handle_dl_buffer_state_update(const mac_dl_buffer_st } std::vector -mac_test_mode_adapter::adapt_bearers(const std::vector& orig_bearers) +mac_test_mode_adapter::adapt_bearers(const std::vector& orig_bearers) const { static test_ue_mac_sdu_tx_builder_adapter dl_adapter; diff --git a/lib/du_high/adapters/mac_test_mode_adapter.h b/lib/du_high/adapters/mac_test_mode_adapter.h index 2a92c304b2..7df7586ceb 100644 --- a/lib/du_high/adapters/mac_test_mode_adapter.h +++ b/lib/du_high/adapters/mac_test_mode_adapter.h @@ -244,7 +244,8 @@ class mac_test_mode_adapter final : public mac_interface, bool handle_ul_ccch_msg(du_ue_index_t ue_index, byte_buffer pdu) override; void handle_ue_config_applied(du_ue_index_t ue_idx) override; - std::vector adapt_bearers(const std::vector& orig_bearers); + std::vector + adapt_bearers(const std::vector& orig_bearers) const; srs_du::du_test_config::test_ue_config test_ue; std::unique_ptr mac_adapted; diff --git a/lib/du_high/du_high_impl.cpp b/lib/du_high/du_high_impl.cpp index d9d8e0c001..420ecd3c83 100644 --- a/lib/du_high/du_high_impl.cpp +++ b/lib/du_high/du_high_impl.cpp @@ -24,12 +24,12 @@ #include "adapters/adapters.h" #include "adapters/du_high_adapter_factories.h" #include "adapters/f1ap_adapters.h" +#include "adapters/f1ap_test_mode_adapter.h" #include "du_high_executor_strategies.h" #include "srsran/du_manager/du_manager_factory.h" #include "srsran/e2/e2.h" #include "srsran/e2/e2_factory.h" #include "srsran/f1ap/du/f1ap_du_factory.h" -#include "srsran/mac/mac_factory.h" #include "srsran/support/executors/task_redispatcher.h" #include "srsran/support/timers.h" @@ -128,13 +128,19 @@ du_high_impl::du_high_impl(const du_high_configuration& config_) : cfg.sched_cfg, cfg.sched_ue_metrics_notifier ? *cfg.sched_ue_metrics_notifier : *metrics_notifier}, cfg.test_cfg); - f1ap = create_f1ap(*cfg.f1c_client, - adapters->f1_to_du_notifier, - cfg.exec_mapper->du_control_executor(), - cfg.exec_mapper->ue_mapper(), - adapters->f1ap_paging_notifier); + f1ap = create_du_high_f1ap(*cfg.f1c_client, + adapters->f1_to_du_notifier, + cfg.exec_mapper->du_control_executor(), + cfg.exec_mapper->ue_mapper(), + adapters->f1ap_paging_notifier, + cfg.test_cfg); + + expected f1u_bind_string = config_.f1u_gw->get_du_bind_address(cfg.gnb_du_id); + assert(f1u_bind_string.has_value()); + transport_layer_address f1u_bind_addr = transport_layer_address::create_from_string(f1u_bind_string.value()); + du_manager = create_du_manager(du_manager_params{ - {cfg.gnb_du_name, cfg.gnb_du_id, 1, cfg.du_bind_addr, cfg.cells, cfg.srbs, cfg.qos}, + {cfg.gnb_du_name, cfg.gnb_du_id, 1, f1u_bind_addr, cfg.cells, cfg.srbs, cfg.qos}, {timers, cfg.exec_mapper->du_control_executor(), cfg.exec_mapper->ue_mapper(), cfg.exec_mapper->cell_mapper()}, {*f1ap, *f1ap}, {*config_.f1u_gw}, diff --git a/lib/du_low/CMakeLists.txt b/lib/du_low/CMakeLists.txt index 5109798ac7..162c301a67 100644 --- a/lib/du_low/CMakeLists.txt +++ b/lib/du_low/CMakeLists.txt @@ -24,4 +24,5 @@ set(SOURCES du_low_wrapper_factory.cpp du_low_wrapper_impl.cpp) -add_library(srsran_du_low STATIC ${SOURCES}) +add_library(srsran_du_low_wrapper STATIC ${SOURCES}) +target_link_libraries(srsran_du_low_wrapper srsran_phy_fapi_adaptor srsran_upper_phy srsran_channel_precoder) diff --git a/lib/du_low/du_low_factory.cpp b/lib/du_low/du_low_factory.cpp index a25265bf58..d883314601 100644 --- a/lib/du_low/du_low_factory.cpp +++ b/lib/du_low/du_low_factory.cpp @@ -22,6 +22,8 @@ #include "srsran/du_low/du_low_factory.h" #include "du_low_impl.h" +#include "srsran/du_low/du_low_config.h" +#include "srsran/support/error_handling.h" using namespace srsran; diff --git a/lib/du_low/du_low_impl.cpp b/lib/du_low/du_low_impl.cpp index d60573a5f1..fcf2432812 100644 --- a/lib/du_low/du_low_impl.cpp +++ b/lib/du_low/du_low_impl.cpp @@ -27,6 +27,8 @@ using namespace srsran; du_low_impl::du_low_impl(std::vector> upper_) : upper(std::move(upper_)) { srsran_assert(!upper.empty(), "Invalid upper PHY"); + for (auto& up : upper) + upper_ptrs.push_back(up.get()); } upper_phy& du_low_impl::get_upper_phy(unsigned cell_id) @@ -42,3 +44,7 @@ void du_low_impl::stop() cell->stop(); } } +span du_low_impl::get_all_upper_phys() +{ + return upper_ptrs; +} diff --git a/lib/du_low/du_low_impl.h b/lib/du_low/du_low_impl.h index cd7d70e434..fcc2366033 100644 --- a/lib/du_low/du_low_impl.h +++ b/lib/du_low/du_low_impl.h @@ -23,8 +23,6 @@ #pragma once #include "srsran/du_low/du_low.h" -#include "srsran/du_low/du_low_config.h" -#include "srsran/fapi_adaptor/phy/phy_fapi_adaptor.h" #include "srsran/phy/upper/upper_phy.h" namespace srsran { @@ -41,8 +39,12 @@ class du_low_impl final : public du_low // See interface for documentation. void stop() override; + // See interface for documentation. + span get_all_upper_phys() override; + private: std::vector> upper; + std::vector upper_ptrs; }; } // namespace srsran diff --git a/lib/du_manager/converters/asn1_csi_meas_config_helpers.cpp b/lib/du_manager/converters/asn1_csi_meas_config_helpers.cpp index 8d35919468..c8aa214079 100644 --- a/lib/du_manager/converters/asn1_csi_meas_config_helpers.cpp +++ b/lib/du_manager/converters/asn1_csi_meas_config_helpers.cpp @@ -398,20 +398,19 @@ static asn1::rrc_nr::csi_res_cfg_s make_asn1_csi_resource_config(const csi_resou { csi_res_cfg_s out{}; out.csi_res_cfg_id = cfg.res_cfg_id; - if (variant_holds_alternative(cfg.csi_rs_res_set_list)) { - auto& res_set = out.csi_rs_res_set_list.set_nzp_csi_rs_ssb(); - const auto& res_set_val = variant_get(cfg.csi_rs_res_set_list); - for (const auto& res_set_id : res_set_val.nzp_csi_rs_res_set_list) { + if (const auto* nzp_csi_res = std::get_if(&cfg.csi_rs_res_set_list)) { + auto& res_set = out.csi_rs_res_set_list.set_nzp_csi_rs_ssb(); + for (const auto& res_set_id : nzp_csi_res->nzp_csi_rs_res_set_list) { res_set.nzp_csi_rs_res_set_list.push_back(res_set_id); } - res_set.csi_ssb_res_set_list_present = not res_set_val.csi_ssb_res_set_list.empty(); - for (const auto& res_set_id : res_set_val.csi_ssb_res_set_list) { + res_set.csi_ssb_res_set_list_present = not nzp_csi_res->csi_ssb_res_set_list.empty(); + for (const auto& res_set_id : nzp_csi_res->csi_ssb_res_set_list) { res_set.csi_ssb_res_set_list[0] = res_set_id; } - } else if (variant_holds_alternative(cfg.csi_rs_res_set_list)) { - auto& res_set = out.csi_rs_res_set_list.set_csi_im_res_set_list(); - const auto& res_set_val = variant_get(cfg.csi_rs_res_set_list); - for (const auto& res_set_id : res_set_val) { + } else if (const auto* csi_im_res = + std::get_if(&cfg.csi_rs_res_set_list)) { + auto& res_set = out.csi_rs_res_set_list.set_csi_im_res_set_list(); + for (const auto& res_set_id : *csi_im_res) { res_set.push_back(res_set_id); } } @@ -497,241 +496,232 @@ static void make_asn1_csi_report_periodicity_and_offset(csi_report_periodicity_a static void make_asn1_codebook_config(codebook_cfg_s& out, const codebook_config& cfg) { - if (variant_holds_alternative(cfg.codebook_type)) { - const auto& tp1_cfg_val = variant_get(cfg.codebook_type); - auto& out_tp1_cfg = out.codebook_type.set_type1(); + if (const auto* tp1_cfg_val = std::get_if(&cfg.codebook_type)) { + auto& out_tp1_cfg = out.codebook_type.set_type1(); // Single Panel. - if (variant_holds_alternative(tp1_cfg_val.sub_type)) { - const auto& sp_cfg_val = variant_get(tp1_cfg_val.sub_type); - auto& out_sp_cfg = out_tp1_cfg.sub_type.set_type_i_single_panel(); + if (const auto* sp_cfg_val = std::get_if(&tp1_cfg_val->sub_type)) { + auto& out_sp_cfg = out_tp1_cfg.sub_type.set_type_i_single_panel(); // Two antennas. - if (variant_holds_alternative< - codebook_config::type1::single_panel::two_antenna_ports_two_tx_codebook_subset_restriction>( - sp_cfg_val.nof_antenna_ports)) { - const auto& ant_restriction = - variant_get( - sp_cfg_val.nof_antenna_ports); + if (const auto* ant_restriction1 = + std::get_if( + &sp_cfg_val->nof_antenna_ports)) { auto& out_ant_restriction = out_sp_cfg.nr_of_ant_ports.set_two(); - out_ant_restriction.two_tx_codebook_subset_restrict.from_number(ant_restriction.to_uint64()); + out_ant_restriction.two_tx_codebook_subset_restrict.from_number(ant_restriction1->to_uint64()); } // More than two antennas. - else if (variant_holds_alternative( - sp_cfg_val.nof_antenna_ports)) { - const auto& ant_restriction = variant_get( - sp_cfg_val.nof_antenna_ports); + else if (const auto* ant_restriction2 = + std::get_if( + &sp_cfg_val->nof_antenna_ports)) { auto& out_ant_restriction = out_sp_cfg.nr_of_ant_ports.set_more_than_two(); - switch (ant_restriction.n1_n2_restriction_type) { + switch (ant_restriction2->n1_n2_restriction_type) { case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::two_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_two_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::two_two: { auto& n1_n2 = out_ant_restriction.n1_n2.set_two_two_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::four_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_four_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::three_two: { auto& n1_n2 = out_ant_restriction.n1_n2.set_three_two_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::six_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_six_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::four_two: { auto& n1_n2 = out_ant_restriction.n1_n2.set_four_two_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::eight_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_eight_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t:: four_three: { auto& n1_n2 = out_ant_restriction.n1_n2.set_four_three_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::six_two: { auto& n1_n2 = out_ant_restriction.n1_n2.set_six_two_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t:: twelve_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_twelve_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::four_four: { auto& n1_n2 = out_ant_restriction.n1_n2.set_four_four_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t::eight_two: { auto& n1_n2 = out_ant_restriction.n1_n2.set_eight_two_type_i_single_panel_restrict(); - n1_n2.from_string(fmt::format("{:b}", ant_restriction.n1_n2_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", ant_restriction2->n1_n2_restriction_value)); break; } case codebook_config::type1::single_panel::more_than_two_antenna_ports::n1_n2_restriction_type_t:: sixteen_one: { auto& n1_n2 = out_ant_restriction.n1_n2.set_sixteen_one_type_i_single_panel_restrict(); - n1_n2.from_number(ant_restriction.n1_n2_restriction_value.to_uint64()); + n1_n2.from_number(ant_restriction2->n1_n2_restriction_value.to_uint64()); break; } default: - srsran_assertion_failure("Invalid n1-n2 type={}", ant_restriction.n1_n2_restriction_type); + srsran_assertion_failure("Invalid n1-n2 type={}", ant_restriction2->n1_n2_restriction_type); } - if (ant_restriction.typei_single_panel_codebook_subset_restriction_i2.size() > 0) { + if (!ant_restriction2->typei_single_panel_codebook_subset_restriction_i2.empty()) { out_ant_restriction.type_i_single_panel_codebook_subset_restrict_i2_present = true; out_ant_restriction.type_i_single_panel_codebook_subset_restrict_i2.from_number( - ant_restriction.typei_single_panel_codebook_subset_restriction_i2.to_uint64()); + ant_restriction2->typei_single_panel_codebook_subset_restriction_i2.to_uint64()); } } - out_sp_cfg.type_i_single_panel_ri_restrict.from_number(sp_cfg_val.typei_single_panel_ri_restriction.to_uint64()); - } else if (variant_holds_alternative(tp1_cfg_val.sub_type)) { - const auto& mp_cfg_val = variant_get(tp1_cfg_val.sub_type); - auto& out_mp_cfg = out_tp1_cfg.sub_type.set_type_i_multi_panel(); - switch (mp_cfg_val.ng_n1_n2_restriction_type) { + out_sp_cfg.type_i_single_panel_ri_restrict.from_number(sp_cfg_val->typei_single_panel_ri_restriction.to_uint64()); + } else if (const auto* mp_cfg_val = std::get_if(&tp1_cfg_val->sub_type)) { + auto& out_mp_cfg = out_tp1_cfg.sub_type.set_type_i_multi_panel(); + switch (mp_cfg_val->ng_n1_n2_restriction_type) { case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::two_two_one: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_two_two_one_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::two_four_one: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_two_four_one_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::four_two_one: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_four_two_one_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::two_two_two: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_two_two_two_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::two_eight_one: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_two_eight_one_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::four_four_one: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_four_four_one_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::two_four_two: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_two_four_two_type_i_multi_panel_restrict(); - ng_n1_n2.from_string(fmt::format("{:b}", mp_cfg_val.ng_n1_n2_restriction_value)); + ng_n1_n2.from_string(fmt::format("{:b}", mp_cfg_val->ng_n1_n2_restriction_value)); break; } case codebook_config::type1::multi_panel::ng_n1_n2_restriction_type_t::four_two_two: { auto& ng_n1_n2 = out_mp_cfg.ng_n1_n2.set_four_two_two_type_i_multi_panel_restrict(); - ng_n1_n2.from_number(mp_cfg_val.ng_n1_n2_restriction_value.to_uint64()); + ng_n1_n2.from_number(mp_cfg_val->ng_n1_n2_restriction_value.to_uint64()); break; } default: - srsran_assertion_failure("Invalid ng-n1-n2 type={}", mp_cfg_val.ng_n1_n2_restriction_type); + srsran_assertion_failure("Invalid ng-n1-n2 type={}", mp_cfg_val->ng_n1_n2_restriction_type); } - out_mp_cfg.ri_restrict.from_number(mp_cfg_val.ri_restriction.to_uint64()); + out_mp_cfg.ri_restrict.from_number(mp_cfg_val->ri_restriction.to_uint64()); } - out_tp1_cfg.codebook_mode = tp1_cfg_val.codebook_mode; - } else if (variant_holds_alternative(cfg.codebook_type)) { - const auto& tp2_cfg_val = variant_get(cfg.codebook_type); - auto& out_tp2_cfg = out.codebook_type.set_type2(); - if (variant_holds_alternative(tp2_cfg_val.sub_type)) { - const auto& typeii = variant_get(tp2_cfg_val.sub_type); - auto& out_typeii = out_tp2_cfg.sub_type.set_type_ii(); - switch (typeii.n1_n2_codebook_subset_restriction_type) { + out_tp1_cfg.codebook_mode = tp1_cfg_val->codebook_mode; + } else if (const auto* tp2_cfg_val = std::get_if(&cfg.codebook_type)) { + auto& out_tp2_cfg = out.codebook_type.set_type2(); + if (const auto* typeii = std::get_if(&tp2_cfg_val->sub_type)) { + auto& out_typeii = out_tp2_cfg.sub_type.set_type_ii(); + switch (typeii->n1_n2_codebook_subset_restriction_type) { case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::two_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_two_one(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::two_two: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_two_two(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::four_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_four_one(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::three_two: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_three_two(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::six_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_six_one(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::four_two: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_four_two(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::eight_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_eight_one(); - n1_n2.from_number(typeii.n1_n2_codebook_subset_restriction_value.to_uint64()); + n1_n2.from_number(typeii->n1_n2_codebook_subset_restriction_value.to_uint64()); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::four_three: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_four_three(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::six_two: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_six_two(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::twelve_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_twelve_one(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::four_four: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_four_four(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::eight_two: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_eight_two(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } case codebook_config::type2::typeii::n1_n2_codebook_subset_restriction_type_t::sixteen_one: { auto& n1_n2 = out_typeii.n1_n2_codebook_subset_restrict.set_sixteen_one(); - n1_n2.from_string(fmt::format("{:b}", typeii.n1_n2_codebook_subset_restriction_value)); + n1_n2.from_string(fmt::format("{:b}", typeii->n1_n2_codebook_subset_restriction_value)); break; } default: srsran_assertion_failure("Invalid n1-n2 codebook subset restriction type={}", - typeii.n1_n2_codebook_subset_restriction_type); + typeii->n1_n2_codebook_subset_restriction_type); } - out_typeii.type_ii_ri_restrict.from_number(typeii.typeii_ri_restriction.to_uint64()); - } else if (variant_holds_alternative(tp2_cfg_val.sub_type)) { - const auto& typeii_ps = variant_get(tp2_cfg_val.sub_type); - auto& out_typeii_ps = out_tp2_cfg.sub_type.set_type_ii_port_sel(); - if (typeii_ps.port_selection_sampling_size.has_value()) { + out_typeii.type_ii_ri_restrict.from_number(typeii->typeii_ri_restriction.to_uint64()); + } else if (const auto* typeii_ps = + std::get_if(&tp2_cfg_val->sub_type)) { + auto& out_typeii_ps = out_tp2_cfg.sub_type.set_type_ii_port_sel(); + if (typeii_ps->port_selection_sampling_size.has_value()) { out_typeii_ps.port_sel_sampling_size_present = true; - switch (typeii_ps.port_selection_sampling_size.value()) { + switch (typeii_ps->port_selection_sampling_size.value()) { case 1: out_typeii_ps.port_sel_sampling_size = codebook_cfg_s::codebook_type_c_::type2_s_::sub_type_c_:: type_ii_port_sel_s_::port_sel_sampling_size_opts::n1; @@ -750,15 +740,15 @@ static void make_asn1_codebook_config(codebook_cfg_s& out, const codebook_config break; default: srsran_assertion_failure("Invalid port selection sampling size={}", - typeii_ps.port_selection_sampling_size.value()); + typeii_ps->port_selection_sampling_size.value()); } } out_typeii_ps.type_ii_port_sel_ri_restrict.from_number( - typeii_ps.typeii_port_selection_ri_restriction.to_uint64()); + typeii_ps->typeii_port_selection_ri_restriction.to_uint64()); } - out_tp2_cfg.subband_amplitude = tp2_cfg_val.subband_amplitude; - switch (tp2_cfg_val.phase_alphabet_size) { + out_tp2_cfg.subband_amplitude = tp2_cfg_val->subband_amplitude; + switch (tp2_cfg_val->phase_alphabet_size) { case 4: out_tp2_cfg.phase_alphabet_size = codebook_cfg_s::codebook_type_c_::type2_s_::phase_alphabet_size_opts::n4; break; @@ -766,10 +756,10 @@ static void make_asn1_codebook_config(codebook_cfg_s& out, const codebook_config out_tp2_cfg.phase_alphabet_size = codebook_cfg_s::codebook_type_c_::type2_s_::phase_alphabet_size_opts::n8; break; default: - srsran_assertion_failure("Invalid phase alphabet size={}", tp2_cfg_val.phase_alphabet_size); + srsran_assertion_failure("Invalid phase alphabet size={}", tp2_cfg_val->phase_alphabet_size); } - switch (tp2_cfg_val.nof_beams) { + switch (tp2_cfg_val->nof_beams) { case 2: out_tp2_cfg.nof_beams = codebook_cfg_s::codebook_type_c_::type2_s_::nof_beams_opts::two; break; @@ -780,7 +770,7 @@ static void make_asn1_codebook_config(codebook_cfg_s& out, const codebook_config out_tp2_cfg.nof_beams = codebook_cfg_s::codebook_type_c_::type2_s_::nof_beams_opts::four; break; default: - srsran_assertion_failure("Invalid nof. beams={}", tp2_cfg_val.nof_beams); + srsran_assertion_failure("Invalid nof. beams={}", tp2_cfg_val->nof_beams); } } } @@ -893,32 +883,31 @@ static asn1::rrc_nr::csi_report_cfg_s make_asn1_csi_report_config(const csi_repo out.nzp_csi_rs_res_for_interference = cfg.nzp_csi_rs_res_for_interference.value(); } - if (variant_holds_alternative(cfg.report_cfg_type)) { - const auto& rep_cfg_val = - variant_get(cfg.report_cfg_type); - if (rep_cfg_val.report_type == + if (const auto* rep_cfg_val = + std::get_if(&cfg.report_cfg_type)) { + if (rep_cfg_val->report_type == csi_report_config::periodic_or_semi_persistent_report_on_pucch::report_type_t::periodic) { auto& rep_cfg = out.report_cfg_type.set_periodic(); make_asn1_csi_report_periodicity_and_offset( - rep_cfg.report_slot_cfg, rep_cfg_val.report_slot_period, rep_cfg_val.report_slot_offset); - for (const auto& pucch_csi_res : rep_cfg_val.pucch_csi_res_list) { + rep_cfg.report_slot_cfg, rep_cfg_val->report_slot_period, rep_cfg_val->report_slot_offset); + for (const auto& pucch_csi_res : rep_cfg_val->pucch_csi_res_list) { rep_cfg.pucch_csi_res_list.push_back({.ul_bw_part_id = pucch_csi_res.ul_bwp, .pucch_res = static_cast(pucch_csi_res.pucch_res_id.ue_res_id)}); } - } else if (rep_cfg_val.report_type == + } else if (rep_cfg_val->report_type == csi_report_config::periodic_or_semi_persistent_report_on_pucch::report_type_t::semi_persistent) { auto& rep_cfg = out.report_cfg_type.semi_persistent_on_pucch(); make_asn1_csi_report_periodicity_and_offset( - rep_cfg.report_slot_cfg, rep_cfg_val.report_slot_period, rep_cfg_val.report_slot_offset); - for (const auto& pucch_csi_res : rep_cfg_val.pucch_csi_res_list) { + rep_cfg.report_slot_cfg, rep_cfg_val->report_slot_period, rep_cfg_val->report_slot_offset); + for (const auto& pucch_csi_res : rep_cfg_val->pucch_csi_res_list) { rep_cfg.pucch_csi_res_list.push_back({.ul_bw_part_id = pucch_csi_res.ul_bwp, .pucch_res = static_cast(pucch_csi_res.pucch_res_id.ue_res_id)}); } } - } else if (variant_holds_alternative(cfg.report_cfg_type)) { - const auto& rep_cfg_val = variant_get(cfg.report_cfg_type); - auto& rep_cfg = out.report_cfg_type.semi_persistent_on_pusch(); - switch (rep_cfg_val.slot_cfg) { + } else if (const auto* rep_cfg_val2 = + std::get_if(&cfg.report_cfg_type)) { + auto& rep_cfg = out.report_cfg_type.semi_persistent_on_pusch(); + switch (rep_cfg_val2->slot_cfg) { case csi_report_periodicity::slots5: rep_cfg.report_slot_cfg = csi_report_cfg_s::report_cfg_type_c_::semi_persistent_on_pusch_s_::report_slot_cfg_opts::sl5; @@ -948,16 +937,15 @@ static asn1::rrc_nr::csi_report_cfg_s make_asn1_csi_report_config(const csi_repo csi_report_cfg_s::report_cfg_type_c_::semi_persistent_on_pusch_s_::report_slot_cfg_opts::sl320; break; default: - srsran_assertion_failure("Invalid CSI report periodicity={}", rep_cfg_val.slot_cfg); + srsran_assertion_failure("Invalid CSI report periodicity={}", rep_cfg_val2->slot_cfg); } - for (const auto& offset : rep_cfg_val.report_slot_offset_list) { + for (unsigned offset : rep_cfg_val2->report_slot_offset_list) { rep_cfg.report_slot_offset_list.push_back(offset); } - rep_cfg.p0alpha = rep_cfg_val.p0_alpha; - } else if (variant_holds_alternative(cfg.report_cfg_type)) { - const auto& rep_cfg_val = variant_get(cfg.report_cfg_type); - auto& rep_cfg = out.report_cfg_type.set_aperiodic(); - for (const auto& offset : rep_cfg_val.report_slot_offset_list) { + rep_cfg.p0alpha = rep_cfg_val2->p0_alpha; + } else if (const auto* rep_cfg_val3 = std::get_if(&cfg.report_cfg_type)) { + auto& rep_cfg = out.report_cfg_type.set_aperiodic(); + for (const auto& offset : rep_cfg_val3->report_slot_offset_list) { rep_cfg.report_slot_offset_list.push_back(offset); } } @@ -1195,20 +1183,17 @@ make_asn1_aperiodic_trigger_state(const csi_aperiodic_trigger_state& cfg) csi_associated_report_cfg_info_s out_report_cfg_info{}; out_report_cfg_info.report_cfg_id = report_cfg_info.report_cfg_id; - if (variant_holds_alternative(report_cfg_info.res_for_channel)) { - const auto& res_for_chnl = - variant_get(report_cfg_info.res_for_channel); + if (const auto* res_for_chnl = + std::get_if(&report_cfg_info.res_for_channel)) { auto& out_res_for_chnl = out_report_cfg_info.res_for_ch.set_nzp_csi_rs(); - out_res_for_chnl.res_set = res_for_chnl.resource_set; - for (const auto& qcl_info : res_for_chnl.qcl_info) { + out_res_for_chnl.res_set = res_for_chnl->resource_set; + for (const auto& qcl_info : res_for_chnl->qcl_info) { out_res_for_chnl.qcl_info.push_back(qcl_info); } - } else if (variant_holds_alternative( - report_cfg_info.res_for_channel)) { - const auto& res_for_chnl = - variant_get(report_cfg_info.res_for_channel); + } else if (const auto* res_for_chnl2 = std::get_if( + &report_cfg_info.res_for_channel)) { auto& out_res_for_chnl = out_report_cfg_info.res_for_ch.set_csi_ssb_res_set(); - out_res_for_chnl = res_for_chnl; + out_res_for_chnl = *res_for_chnl2; } if (report_cfg_info.nzp_csi_rs_resources_for_interference.has_value()) { @@ -1300,7 +1285,7 @@ void srsran::srs_du::calculate_csi_meas_config_diff(asn1::rrc_nr::csi_meas_cfg_s out.report_trigger_size = dest.report_trigger_size.value(); } - if ((dest.aperiodic_trigger_state_list.has_value() and not src.aperiodic_trigger_state_list.has_value()) || + if ((dest.aperiodic_trigger_state_list.has_value() and not src.aperiodic_trigger_state_list.has_value()) or (dest.aperiodic_trigger_state_list.has_value() and src.aperiodic_trigger_state_list.has_value() and dest.aperiodic_trigger_state_list != src.aperiodic_trigger_state_list)) { out.aperiodic_trigger_state_list_present = true; @@ -1314,7 +1299,7 @@ void srsran::srs_du::calculate_csi_meas_config_diff(asn1::rrc_nr::csi_meas_cfg_s } if ((dest.semi_persistent_on_pusch_trigger_state_list.has_value() and - not src.semi_persistent_on_pusch_trigger_state_list.has_value()) || + not src.semi_persistent_on_pusch_trigger_state_list.has_value()) or (dest.semi_persistent_on_pusch_trigger_state_list.has_value() and src.semi_persistent_on_pusch_trigger_state_list.has_value() and dest.semi_persistent_on_pusch_trigger_state_list != src.semi_persistent_on_pusch_trigger_state_list)) { diff --git a/lib/du_manager/converters/asn1_rrc_config_helpers.cpp b/lib/du_manager/converters/asn1_rrc_config_helpers.cpp index 3747968615..4af058e37d 100644 --- a/lib/du_manager/converters/asn1_rrc_config_helpers.cpp +++ b/lib/du_manager/converters/asn1_rrc_config_helpers.cpp @@ -135,7 +135,7 @@ asn1::rrc_nr::search_space_s srsran::srs_du::make_asn1_rrc_search_space(const se asn1::number_to_enum(ss.nrof_candidates.aggregation_level16, cfg.get_nof_candidates()[4]); ss.search_space_type_present = true; if (cfg.is_common_search_space()) { - const auto dci_fmt = variant_get(cfg.get_monitored_dci_formats()); + const auto dci_fmt = std::get(cfg.get_monitored_dci_formats()); ss.search_space_type.set_common(); ss.search_space_type.common().dci_format0_0_and_format1_0_present = dci_fmt.f0_0_and_f1_0; ss.search_space_type.common().dci_format2_0_present = dci_fmt.f2_0; @@ -143,8 +143,7 @@ asn1::rrc_nr::search_space_s srsran::srs_du::make_asn1_rrc_search_space(const se ss.search_space_type.common().dci_format2_2_present = dci_fmt.f2_2; ss.search_space_type.common().dci_format2_3_present = dci_fmt.f2_3; } else { - const auto dci_fmt = - variant_get(cfg.get_monitored_dci_formats()); + const auto dci_fmt = std::get(cfg.get_monitored_dci_formats()); ss.search_space_type.set_ue_specific(); ss.search_space_type.ue_specific().dci_formats.value = dci_fmt == srsran::search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0 @@ -154,7 +153,7 @@ asn1::rrc_nr::search_space_s srsran::srs_du::make_asn1_rrc_search_space(const se return ss; } -rlc_bearer_cfg_s make_asn1_rrc_rlc_bearer(const rlc_bearer_config& cfg) +static rlc_bearer_cfg_s make_asn1_rrc_rlc_bearer(const rlc_bearer_config& cfg) { rlc_bearer_cfg_s out; @@ -919,8 +918,8 @@ static asn1::rrc_nr::tdd_ul_dl_pattern_s make_asn1_rrc_tdd_ul_dl_pattern(subcarr // Set period in ms. const float periodicity_ms = static_cast(pattern.dl_ul_tx_period_nof_slots) / static_cast(get_nof_slots_per_subframe(ref_scs)); - auto same_period_func = [periodicity_ms](float v) { return std::abs(v - periodicity_ms) < 0.001F; }; - auto it = std::find_if(basic_periods.begin(), basic_periods.end(), same_period_func); + auto same_period_func = [periodicity_ms](float v) { return std::abs(v - periodicity_ms) < 0.001F; }; + const auto* it = std::find_if(basic_periods.begin(), basic_periods.end(), same_period_func); if (it != basic_periods.end()) { out.dl_ul_tx_periodicity.value = (asn1::rrc_nr::tdd_ul_dl_pattern_s::dl_ul_tx_periodicity_opts::options)std::distance(basic_periods.begin(), it); @@ -960,7 +959,8 @@ srsran::srs_du::make_asn1_rrc_tdd_ul_dl_cfg_common(const tdd_ul_dl_config_common return out; } -void calculate_pdcch_config_diff(asn1::rrc_nr::pdcch_cfg_s& out, const pdcch_config& src, const pdcch_config& dest) +static void +calculate_pdcch_config_diff(asn1::rrc_nr::pdcch_cfg_s& out, const pdcch_config& src, const pdcch_config& dest) { calculate_addmodremlist_diff( out.coreset_to_add_mod_list, @@ -981,7 +981,7 @@ void calculate_pdcch_config_diff(asn1::rrc_nr::pdcch_cfg_s& out, const pdcch_con // TODO: Remaining. } -void make_asn1_rrc_dmrs_dl_for_pdsch(asn1::rrc_nr::dmrs_dl_cfg_s& out, const dmrs_downlink_config& cfg) +static void make_asn1_rrc_dmrs_dl_for_pdsch(asn1::rrc_nr::dmrs_dl_cfg_s& out, const dmrs_downlink_config& cfg) { if (cfg.is_dmrs_type2) { out.dmrs_type_present = true; @@ -1019,7 +1019,7 @@ void make_asn1_rrc_dmrs_dl_for_pdsch(asn1::rrc_nr::dmrs_dl_cfg_s& out, const dmr } } -void make_asn1_rrc_qcl_info(asn1::rrc_nr::qcl_info_s& out, const qcl_info& cfg) +static void make_asn1_rrc_qcl_info(asn1::rrc_nr::qcl_info_s& out, const qcl_info& cfg) { if (cfg.cell.has_value()) { out.cell_present = true; @@ -1089,7 +1089,8 @@ asn1::rrc_nr::tci_state_s srsran::srs_du::make_asn1_rrc_tci_state(const tci_stat return tci_st; } -void calculate_pdsch_config_diff(asn1::rrc_nr::pdsch_cfg_s& out, const pdsch_config& src, const pdsch_config& dest) +static void +calculate_pdsch_config_diff(asn1::rrc_nr::pdsch_cfg_s& out, const pdsch_config& src, const pdsch_config& dest) { out.data_scrambling_id_pdsch_present = dest.data_scrambling_id_pdsch.has_value(); if (out.data_scrambling_id_pdsch_present) { @@ -1186,11 +1187,10 @@ void calculate_pdsch_config_diff(asn1::rrc_nr::pdsch_cfg_s& out, const pdsch_con } // PRB Bundling. - if (variant_holds_alternative(dest.prb_bndlg.bundling)) { + if (const auto* result = std::get_if(&dest.prb_bndlg.bundling)) { auto& st_bundling = out.prb_bundling_type.set_static_bundling(); st_bundling.bundle_size_present = true; - const auto& bdlng = variant_get(dest.prb_bndlg.bundling); - switch (bdlng.sz.value()) { + switch (result->sz.value()) { case prb_bundling::static_bundling::bundling_size::n4: st_bundling.bundle_size = pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::n4; break; @@ -1198,13 +1198,13 @@ void calculate_pdsch_config_diff(asn1::rrc_nr::pdsch_cfg_s& out, const pdsch_con st_bundling.bundle_size = pdsch_cfg_s::prb_bundling_type_c_::static_bundling_s_::bundle_size_opts::wideband; break; default: - srsran_assertion_failure("Invalid static PRB bundling size={}", bdlng.sz.value()); + srsran_assertion_failure("Invalid static PRB bundling size={}", result->sz.value()); } } else { // Dynamic bundling. auto& dy_bundling = out.prb_bundling_type.set_dyn_bundling(); dy_bundling.bundle_size_set1_present = true; - const auto& bdlng = variant_get(dest.prb_bndlg.bundling); + const auto& bdlng = std::get(dest.prb_bndlg.bundling); switch (bdlng.sz_set1.value()) { case prb_bundling::dynamic_bundling::bundling_size_set1::n4: dy_bundling.bundle_size_set1 = pdsch_cfg_s::prb_bundling_type_c_::dyn_bundling_s_::bundle_size_set1_opts::n4; @@ -1311,8 +1311,8 @@ asn1::rrc_nr::pucch_res_set_s srsran::srs_du::make_asn1_rrc_pucch_resource_set(c return pucch_res_set; } -void make_asn1_rrc_pucch_formats_common_param(asn1::rrc_nr::pucch_format_cfg_s& out, - const pucch_common_all_formats& cfg) +static void make_asn1_rrc_pucch_formats_common_param(asn1::rrc_nr::pucch_format_cfg_s& out, + const pucch_common_all_formats& cfg) { out.interslot_freq_hop_present = cfg.interslot_freq_hop; out.add_dmrs_present = cfg.additional_dmrs; @@ -1380,14 +1380,14 @@ asn1::rrc_nr::pucch_res_s srsran::srs_du::make_asn1_rrc_pucch_resource(const puc } switch (cfg.format) { case pucch_format::FORMAT_0: { - const auto& f0 = variant_get(cfg.format_params); + const auto& f0 = std::get(cfg.format_params); auto& format0 = pucch_res.format.set_format0(); format0.init_cyclic_shift = f0.initial_cyclic_shift; format0.nrof_symbols = f0.nof_symbols; format0.start_symbol_idx = f0.starting_sym_idx; } break; case pucch_format::FORMAT_1: { - const auto& f1 = variant_get(cfg.format_params); + const auto& f1 = std::get(cfg.format_params); auto& format1 = pucch_res.format.set_format1(); format1.init_cyclic_shift = f1.initial_cyclic_shift; format1.nrof_symbols = f1.nof_symbols; @@ -1395,21 +1395,21 @@ asn1::rrc_nr::pucch_res_s srsran::srs_du::make_asn1_rrc_pucch_resource(const puc format1.time_domain_occ = f1.time_domain_occ; } break; case pucch_format::FORMAT_2: { - const auto& f2 = variant_get(cfg.format_params); + const auto& f2 = std::get(cfg.format_params); auto& format2 = pucch_res.format.set_format2(); format2.start_symbol_idx = f2.starting_sym_idx; format2.nrof_symbols = f2.nof_symbols; format2.nrof_prbs = f2.nof_prbs; } break; case pucch_format::FORMAT_3: { - const auto& f3 = variant_get(cfg.format_params); + const auto& f3 = std::get(cfg.format_params); auto& format3 = pucch_res.format.set_format3(); format3.start_symbol_idx = f3.starting_sym_idx; format3.nrof_symbols = f3.nof_symbols; format3.nrof_prbs = f3.nof_prbs; } break; case pucch_format::FORMAT_4: { - const auto& f4 = variant_get(cfg.format_params); + const auto& f4 = std::get(cfg.format_params); auto& format4 = pucch_res.format.set_format4(); format4.start_symbol_idx = f4.starting_sym_idx; format4.nrof_symbols = f4.nof_symbols; @@ -1519,7 +1519,8 @@ srsran::srs_du::make_asn1_rrc_sr_resource(const scheduling_request_resource_conf return sr_res_cfg; } -void calculate_pucch_config_diff(asn1::rrc_nr::pucch_cfg_s& out, const pucch_config& src, const pucch_config& dest) +static void +calculate_pucch_config_diff(asn1::rrc_nr::pucch_cfg_s& out, const pucch_config& src, const pucch_config& dest) { // PUCCH Resource Set. calculate_addmodremlist_diff( @@ -1597,7 +1598,7 @@ void calculate_pucch_config_diff(asn1::rrc_nr::pucch_cfg_s& out, const pucch_con } } -void make_asn1_rrc_ptrs_ul_cfg(asn1::rrc_nr::ptrs_ul_cfg_s& out, const ptrs_uplink_config& cfg) +static void make_asn1_rrc_ptrs_ul_cfg(asn1::rrc_nr::ptrs_ul_cfg_s& out, const ptrs_uplink_config& cfg) { if (cfg.trans_precoder_disabled.has_value()) { out.transform_precoder_disabled_present = true; @@ -1677,9 +1678,9 @@ void make_asn1_rrc_ptrs_ul_cfg(asn1::rrc_nr::ptrs_ul_cfg_s& out, const ptrs_upli } } -void make_asn1_rrc_dmrs_ul_for_pusch(asn1::rrc_nr::dmrs_ul_cfg_s& out, - const dmrs_uplink_config& src, - const dmrs_uplink_config& dest) +static void make_asn1_rrc_dmrs_ul_for_pusch(asn1::rrc_nr::dmrs_ul_cfg_s& out, + const dmrs_uplink_config& src, + const dmrs_uplink_config& dest) { if (dest.is_dmrs_type2) { out.dmrs_type_present = true; @@ -1752,12 +1753,12 @@ srsran::srs_du::make_asn1_rrc_pusch_pathloss_ref_rs(const pusch_config::pusch_po { pusch_pathloss_ref_rs_s ploss_ref_rs; ploss_ref_rs.pusch_pathloss_ref_rs_id = cfg.id; - if (variant_holds_alternative(cfg.rs)) { + if (const auto* nzp_csi_res = std::get_if(&cfg.rs)) { auto& csi_rs_idx = ploss_ref_rs.ref_sig.set_csi_rs_idx(); - csi_rs_idx = variant_get(cfg.rs); - } else if (variant_holds_alternative(cfg.rs)) { + csi_rs_idx = *nzp_csi_res; + } else if (const auto* ssb_id = std::get_if(&cfg.rs)) { auto& ssb_idx = ploss_ref_rs.ref_sig.set_ssb_idx(); - ssb_idx = variant_get(cfg.rs); + ssb_idx = *ssb_id; } return ploss_ref_rs; } @@ -1782,7 +1783,7 @@ srsran::srs_du::make_asn1_rrc_sri_pusch_pwr_ctrl(const pusch_config::pusch_power return sri_pwr_ctl; } -void make_asn1_rrc_alpha(asn1::rrc_nr::alpha_e& out, const alpha& cfg) +static void make_asn1_rrc_alpha(asn1::rrc_nr::alpha_e& out, alpha cfg) { switch (cfg) { case alpha::alpha0: @@ -1814,9 +1815,9 @@ void make_asn1_rrc_alpha(asn1::rrc_nr::alpha_e& out, const alpha& cfg) } } -void make_asn1_rrc_pusch_pwr_ctrl(asn1::rrc_nr::pusch_pwr_ctrl_s& out, - const pusch_config::pusch_power_control& src, - const pusch_config::pusch_power_control& dest) +static void make_asn1_rrc_pusch_pwr_ctrl(asn1::rrc_nr::pusch_pwr_ctrl_s& out, + const pusch_config::pusch_power_control& src, + const pusch_config::pusch_power_control& dest) { if (dest.is_tpc_accumulation_disabled) { out.tpc_accumulation_present = true; @@ -1832,16 +1833,16 @@ void make_asn1_rrc_pusch_pwr_ctrl(asn1::rrc_nr::pusch_pwr_ctrl_s& out, out.p0_nominal_without_grant = dest.p0_nominal_without_grant.value(); } - for (unsigned idx = 0; idx < dest.p0_alphasets.size(); idx++) { + for (const auto& set : dest.p0_alphasets) { p0_pusch_alpha_set_s p0_alphaset{}; - p0_alphaset.p0_pusch_alpha_set_id = dest.p0_alphasets[idx].id; - if (dest.p0_alphasets[idx].p0.has_value()) { + p0_alphaset.p0_pusch_alpha_set_id = set.id; + if (set.p0.has_value()) { p0_alphaset.p0_present = true; - p0_alphaset.p0 = dest.p0_alphasets[idx].p0.value(); + p0_alphaset.p0 = set.p0.value(); } - if (dest.p0_alphasets[idx].p0_pusch_alpha != srsran::alpha::not_set) { + if (set.p0_pusch_alpha != srsran::alpha::not_set) { p0_alphaset.alpha_present = true; - make_asn1_rrc_alpha(p0_alphaset.alpha, dest.p0_alphasets[idx].p0_pusch_alpha); + make_asn1_rrc_alpha(p0_alphaset.alpha, set.p0_pusch_alpha); } out.p0_alpha_sets.push_back(p0_alphaset); } @@ -1867,7 +1868,7 @@ void make_asn1_rrc_pusch_pwr_ctrl(asn1::rrc_nr::pusch_pwr_ctrl_s& out, [](const pusch_config::pusch_power_control::sri_pusch_pwr_ctrl& res) { return res.id; }); } -void fill_uci_beta_offset(beta_offsets_s& beta_out, const beta_offsets& beta_in) +static void fill_uci_beta_offset(beta_offsets_s& beta_out, const beta_offsets& beta_in) { if (beta_in.beta_offset_ack_idx_1.has_value()) { beta_out.beta_offset_ack_idx1_present = true; @@ -1919,7 +1920,7 @@ void fill_uci_beta_offset(beta_offsets_s& beta_out, const beta_offsets& beta_in) } } -void fill_uci_on_pusch(asn1::rrc_nr::uci_on_pusch_s& uci_asn1, const uci_on_pusch& uci_in) +static void fill_uci_on_pusch(asn1::rrc_nr::uci_on_pusch_s& uci_asn1, const uci_on_pusch& uci_in) { switch (uci_in.scaling) { case alpha_scaling_opt::f0p5: @@ -1940,19 +1941,18 @@ void fill_uci_on_pusch(asn1::rrc_nr::uci_on_pusch_s& uci_asn1, const uci_on_pusc if (uci_in.beta_offsets_cfg.has_value()) { uci_asn1.beta_offsets_present = true; - if (variant_holds_alternative(uci_in.beta_offsets_cfg.value())) { - const auto& in_semi_static = variant_get(uci_in.beta_offsets_cfg.value()); - auto& out_semi_static = uci_asn1.beta_offsets.set_semi_static(); - - fill_uci_beta_offset(out_semi_static, in_semi_static); + if (const auto* in_semi_static = + std::get_if(&uci_in.beta_offsets_cfg.value())) { + auto& out_semi_static = uci_asn1.beta_offsets.set_semi_static(); + fill_uci_beta_offset(out_semi_static, *in_semi_static); } else { - auto& input_dynamic = variant_get(uci_in.beta_offsets_cfg.value()); - auto& out_dynamic = uci_asn1.beta_offsets.set_dyn(); + const auto& input_dynamic = std::get(uci_in.beta_offsets_cfg.value()); + auto& out_dynamic = uci_asn1.beta_offsets.set_dyn(); srsran_assert(input_dynamic.size() == out_dynamic.max_size(), "Mismatch between input and output vectors"); - for (size_t n = 0; n != input_dynamic.size(); ++n) { - fill_uci_beta_offset(out_dynamic[n], input_dynamic[n]); + for (unsigned i = 0, e = input_dynamic.size(); i != e; ++i) { + fill_uci_beta_offset(out_dynamic[i], input_dynamic[i]); } } } else { @@ -1982,7 +1982,8 @@ make_asn1_rrc_pusch_time_domain_alloc_list(const pusch_time_domain_resource_allo return out; } -void calculate_pusch_config_diff(asn1::rrc_nr::pusch_cfg_s& out, const pusch_config& src, const pusch_config& dest) +static void +calculate_pusch_config_diff(asn1::rrc_nr::pusch_cfg_s& out, const pusch_config& src, const pusch_config& dest) { if (dest.data_scrambling_id_pusch.has_value()) { out.data_scrambling_id_pusch_present = true; @@ -2133,38 +2134,37 @@ asn1::rrc_nr::srs_res_set_s srsran::srs_du::make_asn1_rrc_srs_res_set(const srs_ srs_res_set.srs_res_id_list.push_back(res_id); } - if (variant_holds_alternative(cfg.res_type)) { - auto& aper_res = srs_res_set.res_type.set_aperiodic(); - const auto& cfg_aper_res = variant_get(cfg.res_type); - aper_res.aperiodic_srs_res_trigger = cfg_aper_res.aperiodic_srs_res_trigger; - if (cfg_aper_res.csi_rs.has_value()) { + if (const auto* cfg_aper_res = std::get_if(&cfg.res_type)) { + auto& aper_res = srs_res_set.res_type.set_aperiodic(); + aper_res.aperiodic_srs_res_trigger = cfg_aper_res->aperiodic_srs_res_trigger; + if (cfg_aper_res->csi_rs.has_value()) { aper_res.csi_rs_present = true; - aper_res.csi_rs = cfg_aper_res.csi_rs.value(); + aper_res.csi_rs = cfg_aper_res->csi_rs.value(); } - if (cfg_aper_res.slot_offset.has_value()) { + if (cfg_aper_res->slot_offset.has_value()) { aper_res.slot_offset_present = true; - aper_res.slot_offset = cfg_aper_res.slot_offset.value(); + aper_res.slot_offset = cfg_aper_res->slot_offset.value(); } - if (not cfg_aper_res.aperiodic_srs_res_trigger_list.empty()) { + if (not cfg_aper_res->aperiodic_srs_res_trigger_list.empty()) { aper_res.aperiodic_srs_res_trigger_list.set_present(); auto* aper_res_trig_list = aper_res.aperiodic_srs_res_trigger_list.get(); - for (const auto& ap_res_trig : cfg_aper_res.aperiodic_srs_res_trigger_list) { + for (const auto& ap_res_trig : cfg_aper_res->aperiodic_srs_res_trigger_list) { aper_res_trig_list->push_back(ap_res_trig); } } - } else if (variant_holds_alternative(cfg.res_type)) { - auto& semi_p_res = srs_res_set.res_type.set_semi_persistent(); - const auto& cfg_semi_p_res = variant_get(cfg.res_type); - if (cfg_semi_p_res.associated_csi_rs.has_value()) { + } else if (const auto* cfg_semi_p_res = + std::get_if(&cfg.res_type)) { + auto& semi_p_res = srs_res_set.res_type.set_semi_persistent(); + if (cfg_semi_p_res->associated_csi_rs.has_value()) { semi_p_res.associated_csi_rs_present = true; - semi_p_res.associated_csi_rs = cfg_semi_p_res.associated_csi_rs.value(); + semi_p_res.associated_csi_rs = cfg_semi_p_res->associated_csi_rs.value(); } - } else if (variant_holds_alternative(cfg.res_type)) { - auto& per_res = srs_res_set.res_type.set_periodic(); - const auto& cfg_per_res = variant_get(cfg.res_type); - if (cfg_per_res.associated_csi_rs.has_value()) { + } else if (const auto* cfg_per_res = + std::get_if(&cfg.res_type)) { + auto& per_res = srs_res_set.res_type.set_periodic(); + if (cfg_per_res->associated_csi_rs.has_value()) { per_res.associated_csi_rs_present = true; - per_res.associated_csi_rs = cfg_per_res.associated_csi_rs.value(); + per_res.associated_csi_rs = cfg_per_res->associated_csi_rs.value(); } } @@ -2212,20 +2212,20 @@ asn1::rrc_nr::srs_res_set_s srsran::srs_du::make_asn1_rrc_srs_res_set(const srs_ if (cfg.pathloss_ref_rs.has_value()) { srs_res_set.pathloss_ref_rs_present = true; - if (variant_holds_alternative(cfg.pathloss_ref_rs.value())) { + if (const auto* ssb_id = std::get_if(&cfg.pathloss_ref_rs.value())) { auto& ssb_idx = srs_res_set.pathloss_ref_rs.set_ssb_idx(); - ssb_idx = variant_get(cfg.pathloss_ref_rs.value()); - } else if (variant_holds_alternative(cfg.pathloss_ref_rs.value())) { + ssb_idx = *ssb_id; + } else if (const auto* csi_rs_res = std::get_if(&cfg.pathloss_ref_rs.value())) { auto& csi_res_idx = srs_res_set.pathloss_ref_rs.set_csi_rs_idx(); - csi_res_idx = variant_get(cfg.pathloss_ref_rs.value()); + csi_res_idx = *csi_rs_res; } } return srs_res_set; } -void make_asn1_rrc_srs_config_perioidicity_and_offset(asn1::rrc_nr::srs_periodicity_and_offset_c& out, - const srs_config::srs_periodicity_and_offset& cfg) +static void make_asn1_rrc_srs_config_perioidicity_and_offset(asn1::rrc_nr::srs_periodicity_and_offset_c& out, + const srs_config::srs_periodicity_and_offset& cfg) { switch (cfg.type) { case srs_config::srs_periodicity_and_offset::type_t::sl1: @@ -2418,26 +2418,26 @@ asn1::rrc_nr::srs_res_s srsran::srs_du::make_asn1_rrc_srs_res(const srs_config:: res.spatial_relation_info.serving_cell_id_present = true; res.spatial_relation_info.serving_cell_id = cfg.spatial_relation_info.value().serv_cell_id.value(); } - if (variant_holds_alternative(cfg.spatial_relation_info.value().reference_signal)) { + if (const auto* ssb_id = std::get_if(&cfg.spatial_relation_info.value().reference_signal)) { auto& ssb_idx = res.spatial_relation_info.ref_sig.set_ssb_idx(); - ssb_idx = variant_get(cfg.spatial_relation_info.value().reference_signal); - } else if (variant_holds_alternative(cfg.spatial_relation_info.value().reference_signal)) { + ssb_idx = *ssb_id; + } else if (const auto* nzp_csi_res = + std::get_if(&cfg.spatial_relation_info.value().reference_signal)) { auto& csi_rs_idx = res.spatial_relation_info.ref_sig.set_csi_rs_idx(); - csi_rs_idx = variant_get(cfg.spatial_relation_info.value().reference_signal); - } else if (variant_holds_alternative( - cfg.spatial_relation_info.value().reference_signal)) { - auto& srs_ref_sig = res.spatial_relation_info.ref_sig.set_srs(); - const auto& cfg_srs_ref_sig = variant_get( - cfg.spatial_relation_info.value().reference_signal); - srs_ref_sig.res_id = cfg_srs_ref_sig.res_id; - srs_ref_sig.ul_bwp = cfg_srs_ref_sig.ul_bwp; + csi_rs_idx = *nzp_csi_res; + } else if (const auto* cfg_srs_ref_sig = + std::get_if( + &cfg.spatial_relation_info.value().reference_signal)) { + auto& srs_ref_sig = res.spatial_relation_info.ref_sig.set_srs(); + srs_ref_sig.res_id = cfg_srs_ref_sig->res_id; + srs_ref_sig.ul_bwp = cfg_srs_ref_sig->ul_bwp; } } return res; } -void calculate_srs_config_diff(asn1::rrc_nr::srs_cfg_s& out, const srs_config& src, const srs_config& dest) +static void calculate_srs_config_diff(asn1::rrc_nr::srs_cfg_s& out, const srs_config& src, const srs_config& dest) { calculate_addmodremlist_diff( out.srs_res_set_to_add_mod_list, @@ -2510,9 +2510,9 @@ calculate_uplink_config_diff(asn1::rrc_nr::ul_cfg_s& out, const uplink_config& s return out.init_ul_bwp_present; } -void calculate_pdsch_serving_cell_cfg_diff(asn1::rrc_nr::pdsch_serving_cell_cfg_s& out, - const pdsch_serving_cell_config& src, - const pdsch_serving_cell_config& dest) +static void calculate_pdsch_serving_cell_cfg_diff(asn1::rrc_nr::pdsch_serving_cell_cfg_s& out, + const pdsch_serving_cell_config& src, + const pdsch_serving_cell_config& dest) { if ((dest.code_block_group_tx.has_value() && not src.code_block_group_tx.has_value()) || (dest.code_block_group_tx.has_value() && src.code_block_group_tx.has_value() && @@ -2709,7 +2709,7 @@ srsran::srs_du::make_asn1_rrc_scheduling_request(const scheduling_request_to_add return req; } -void make_asn1_rrc_bsr_config(asn1::rrc_nr::bsr_cfg_s& out, const bsr_config& cfg) +static void make_asn1_rrc_bsr_config(asn1::rrc_nr::bsr_cfg_s& out, const bsr_config& cfg) { switch (cfg.retx_timer) { case retx_bsr_timer::sf10: @@ -2869,7 +2869,7 @@ asn1::rrc_nr::tag_s srsran::srs_du::make_asn1_rrc_tag_config(const tag& cfg) return tag_cfg; } -void make_asn1_rrc_phr_config(asn1::rrc_nr::phr_cfg_s& out, const phr_config& cfg) +static void make_asn1_rrc_phr_config(asn1::rrc_nr::phr_cfg_s& out, const phr_config& cfg) { switch (cfg.periodic_timer) { case phr_periodic_timer::sf10: diff --git a/lib/du_manager/converters/f1ap_configuration_helpers.cpp b/lib/du_manager/converters/f1ap_configuration_helpers.cpp index 83e5a7cd4d..4bfbb30f8e 100644 --- a/lib/du_manager/converters/f1ap_configuration_helpers.cpp +++ b/lib/du_manager/converters/f1ap_configuration_helpers.cpp @@ -48,6 +48,7 @@ byte_buffer srsran::srs_du::make_asn1_rrc_cell_mib_buffer(const du_cell_config& srsran_assertion_failure("Invalid SCS common"); mib.sub_carrier_spacing_common.value = asn1::rrc_nr::mib_s::sub_carrier_spacing_common_opts::scs15or60; } + /// As per TS 38.331, MIB, the field "ssb-SubcarrierOffset" in the MIB only encodes the 4 LSB of k_SSB. mib.ssb_subcarrier_offset = static_cast(du_cfg.ssb_cfg.k_ssb.to_uint() & 0b00001111U); mib.dmrs_type_a_position.value = du_cfg.dmrs_typeA_pos == dmrs_typeA_position::pos2 @@ -63,6 +64,7 @@ byte_buffer srsran::srs_du::make_asn1_rrc_cell_mib_buffer(const du_cell_config& asn1::bit_ref bref{buf}; asn1::SRSASN_CODE ret = mib.pack(bref); srsran_assert(ret == asn1::SRSASN_SUCCESS, "Failed to pack MIB"); + return buf; } @@ -74,6 +76,7 @@ static asn1::rrc_nr::subcarrier_spacing_e get_asn1_scs(subcarrier_spacing scs) static asn1::rrc_nr::dl_cfg_common_sib_s make_asn1_rrc_dl_cfg_common_sib(const dl_config_common& cfg) { using namespace asn1::rrc_nr; + dl_cfg_common_sib_s out; // > frequencyInfoDL FrequencyInfoDL-SIB for (const auto& dl_band : cfg.freq_info_dl.freq_band_list) { @@ -85,10 +88,13 @@ static asn1::rrc_nr::dl_cfg_common_sib_s make_asn1_rrc_dl_cfg_common_sib(const d out.freq_info_dl.offset_to_point_a = cfg.freq_info_dl.offset_to_point_a; out.freq_info_dl.scs_specific_carrier_list = srs_du::make_asn1_rrc_scs_specific_carrier_list(cfg.freq_info_dl.scs_carrier_list); + // > initialDownlinkBWP BWP-DownlinkCommon out.init_dl_bwp = srs_du::make_asn1_init_dl_bwp(cfg); + // BCCH-Config out.bcch_cfg.mod_period_coeff.value = bcch_cfg_s::mod_period_coeff_opts::n4; + // PCCH-Config switch (cfg.pcch_cfg.default_paging_cycle) { case paging_cycle::rf32: @@ -247,10 +253,12 @@ static asn1::rrc_nr::ul_cfg_common_sib_s make_asn1_rrc_ul_config_common(const ul static asn1::rrc_nr::serving_cell_cfg_common_sib_s make_asn1_rrc_cell_serving_cell_common(const du_cell_config& du_cfg) { using namespace asn1::rrc_nr; + serving_cell_cfg_common_sib_s cell; cell.dl_cfg_common = make_asn1_rrc_dl_cfg_common_sib(du_cfg.dl_cfg_common); cell.ul_cfg_common_present = true; cell.ul_cfg_common = make_asn1_rrc_ul_config_common(du_cfg.ul_cfg_common); + // SSB params. cell.ssb_positions_in_burst.in_one_group.from_number(static_cast(du_cfg.ssb_cfg.ssb_bitmap) >> static_cast(56U)); @@ -315,8 +323,8 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg sib1.conn_est_fail_ctrl.conn_est_fail_offset = 1; if (du_cfg.si_config.has_value()) { - for (auto sib : du_cfg.si_config->sibs) { - if (variant_holds_alternative(sib)) { + for (const auto& sib : du_cfg.si_config->sibs) { + if (std::holds_alternative(sib)) { sib1.si_sched_info_present = true; bool ret = asn1::number_to_enum(sib1.si_sched_info.si_win_len, du_cfg.si_config.value().si_window_len_slots); srsran_assert(ret, "Invalid SI window length"); @@ -327,7 +335,7 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg srsran_assert(ret, "Invalid SI period"); for (auto mapping_info : cfg_si.sib_mapping_info) { sib_type_info_s type_info; - const uint8_t sib_id = static_cast(mapping_info); + auto sib_id = static_cast(mapping_info); ret = asn1::number_to_enum(type_info.type, sib_id); type_info.value_tag_present = true; type_info.value_tag = 0; @@ -339,7 +347,7 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg sib1.si_sched_info.sched_info_list.push_back(asn1_si); } } - } else if (variant_holds_alternative(sib)) { + } else if (std::holds_alternative(sib)) { sib1.non_crit_ext_present = true; sib1.non_crit_ext.non_crit_ext_present = true; sib1.non_crit_ext.non_crit_ext.non_crit_ext_present = true; @@ -353,9 +361,12 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg asn1_si_r17.si_broadcast_status_r17.value = sched_info2_r17_s::si_broadcast_status_r17_opts::broadcasting; bool ret = asn1::number_to_enum(asn1_si_r17.si_periodicity_r17, cfg_si.si_period_radio_frames); srsran_assert(ret, "Invalid SI period"); + if (cfg_si.si_window_position.has_value()) { + asn1_si_r17.si_win_position_r17 = cfg_si.si_window_position.value(); + } for (auto mapping_info : cfg_si.sib_mapping_info) { sib_type_info_v1700_s type_info; - const uint8_t sib_id_r17 = static_cast(mapping_info); + auto sib_id_r17 = static_cast(mapping_info); type_info.sib_type_r17.set_type1_r17(); ret = asn1::number_to_enum(type_info.sib_type_r17.type1_r17(), sib_id_r17); if (ret) { @@ -366,6 +377,8 @@ static asn1::rrc_nr::sib1_s make_asn1_rrc_cell_sib1(const du_cell_config& du_cfg si_sched_info_r17.sched_info_list2_r17.push_back(asn1_si_r17); } } + } else { + srsran_terminate("Invalid SIB type"); } } } @@ -438,18 +451,17 @@ asn1::rrc_nr::sib19_r17_s make_asn1_rrc_cell_sib19(const sib19_info& sib19_param } if (sib19_params.ephemeris_info.has_value()) { - if (variant_holds_alternative(sib19_params.ephemeris_info.value())) { - auto& pos_vel = variant_get(sib19_params.ephemeris_info.value()); + if (const auto* pos_vel = std::get_if(&sib19_params.ephemeris_info.value())) { sib19.ntn_cfg_r17.ephemeris_info_r17_present = true; sib19.ntn_cfg_r17.ephemeris_info_r17.set_position_velocity_r17(); - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_x_r17 = pos_vel.position_x; - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_y_r17 = pos_vel.position_y; - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_z_r17 = pos_vel.position_z; - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vx_r17 = pos_vel.velocity_vx; - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vy_r17 = pos_vel.velocity_vy; - sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vz_r17 = pos_vel.velocity_vz; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_x_r17 = pos_vel->position_x; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_y_r17 = pos_vel->position_y; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_z_r17 = pos_vel->position_z; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vx_r17 = pos_vel->velocity_vx; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vy_r17 = pos_vel->velocity_vy; + sib19.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().velocity_vz_r17 = pos_vel->velocity_vz; } else { - auto& orbital_elem = variant_get(sib19_params.ephemeris_info.value()); + const auto& orbital_elem = std::get(sib19_params.ephemeris_info.value()); sib19.ntn_cfg_r17.ephemeris_info_r17_present = true; sib19.ntn_cfg_r17.ephemeris_info_r17.set_orbital_r17(); sib19.ntn_cfg_r17.ephemeris_info_r17.orbital_r17().semi_major_axis_r17 = (uint64_t)orbital_elem.semi_major_axis; @@ -509,20 +521,21 @@ static asn1::rrc_nr::sys_info_ies_s::sib_type_and_info_item_c_ make_asn1_rrc_sib switch (get_sib_info_type(sib)) { case sib_type::sib2: { - const sib2_info& cfg = variant_get(sib); - sib2_s& out_sib = ret.set_sib2(); - out_sib = make_asn1_rrc_cell_sib2(cfg); + const auto& cfg = std::get(sib); + sib2_s& out_sib = ret.set_sib2(); + out_sib = make_asn1_rrc_cell_sib2(cfg); if (cfg.nof_ssbs_to_average.has_value()) { out_sib.cell_resel_info_common.nrof_ss_blocks_to_average_present = true; out_sib.cell_resel_info_common.nrof_ss_blocks_to_average = cfg.nof_ssbs_to_average.value(); } - // TODO - } break; + break; + } case sib_type::sib19: { - const sib19_info& cfg = variant_get(sib); - sib19_r17_s& out_sib = ret.set_sib19_v1700(); - out_sib = make_asn1_rrc_cell_sib19(cfg); - } break; + const auto& cfg = std::get(sib); + sib19_r17_s& out_sib = ret.set_sib19_v1700(); + out_sib = make_asn1_rrc_cell_sib19(cfg); + break; + } default: srsran_assertion_failure("Invalid SIB type"); } diff --git a/lib/du_manager/converters/rlc_config_helpers.cpp b/lib/du_manager/converters/rlc_config_helpers.cpp index cb568064a1..2453ed1bb6 100644 --- a/lib/du_manager/converters/rlc_config_helpers.cpp +++ b/lib/du_manager/converters/rlc_config_helpers.cpp @@ -27,7 +27,7 @@ using namespace srs_du; template static void fill_rlc_entity_creation_message_common(rlc_entity_creation_message& msg, - uint32_t du_index, + gnb_du_id_t du_id, du_ue_index_t ue_index, du_cell_index_t pcell_index, Bearer& bearer, @@ -35,7 +35,7 @@ static void fill_rlc_entity_creation_message_common(rlc_entity_creation_message& rlc_tx_upper_layer_control_notifier& rlc_rlf_notifier, rlc_pcap& pcap_writer) { - msg.du_index = du_index; + msg.gnb_du_id = du_id; msg.ue_index = ue_index; msg.config = bearer.rlc_cfg; msg.rx_upper_dn = &bearer.connector.rlc_rx_sdu_notif; @@ -50,7 +50,7 @@ static void fill_rlc_entity_creation_message_common(rlc_entity_creation_message& // for SRBs rlc_entity_creation_message -srsran::srs_du::make_rlc_entity_creation_message(uint32_t du_index, +srsran::srs_du::make_rlc_entity_creation_message(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, du_cell_index_t pcell_index, du_ue_srb& bearer, @@ -60,14 +60,14 @@ srsran::srs_du::make_rlc_entity_creation_message(uint32_t { rlc_entity_creation_message msg; fill_rlc_entity_creation_message_common( - msg, du_index, ue_index, pcell_index, bearer, du_services, rlc_rlf_notifier, pcap_writer); + msg, gnb_du_id, ue_index, pcell_index, bearer, du_services, rlc_rlf_notifier, pcap_writer); msg.rb_id = bearer.srb_id; return msg; } // for DRBs rlc_entity_creation_message -srsran::srs_du::make_rlc_entity_creation_message(uint32_t du_index, +srsran::srs_du::make_rlc_entity_creation_message(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, du_cell_index_t pcell_index, du_ue_drb& bearer, @@ -78,7 +78,7 @@ srsran::srs_du::make_rlc_entity_creation_message(uint32_t { rlc_entity_creation_message msg; fill_rlc_entity_creation_message_common( - msg, du_index, ue_index, pcell_index, bearer, du_services, rlc_rlf_notifier, pcap_writer); + msg, gnb_du_id, ue_index, pcell_index, bearer, du_services, rlc_rlf_notifier, pcap_writer); msg.rb_id = bearer.drb_id; msg.rlc_metrics_notif = rlc_metrics_notifier_; return msg; diff --git a/lib/du_manager/converters/rlc_config_helpers.h b/lib/du_manager/converters/rlc_config_helpers.h index 79063d2818..ebb3dbcec7 100644 --- a/lib/du_manager/converters/rlc_config_helpers.h +++ b/lib/du_manager/converters/rlc_config_helpers.h @@ -48,7 +48,7 @@ struct rlc_bearer_config { }; /// \brief Create configuration for RLC SRB entity. -rlc_entity_creation_message make_rlc_entity_creation_message(uint32_t du_index, +rlc_entity_creation_message make_rlc_entity_creation_message(gnb_du_id_t du_id, du_ue_index_t ue_index, du_cell_index_t pcell_index, du_ue_srb& bearer, @@ -57,7 +57,7 @@ rlc_entity_creation_message make_rlc_entity_creation_message(uint32_t rlc_pcap& rlc_pcap); /// \brief Create configuration for RLC DRB entity. -rlc_entity_creation_message make_rlc_entity_creation_message(uint32_t du_index, +rlc_entity_creation_message make_rlc_entity_creation_message(gnb_du_id_t du_id, du_ue_index_t ue_index, du_cell_index_t pcell_index, du_ue_drb& bearer, diff --git a/lib/du_manager/converters/scheduler_configuration_helpers.cpp b/lib/du_manager/converters/scheduler_configuration_helpers.cpp index f7a6186891..e54aa8accf 100644 --- a/lib/du_manager/converters/scheduler_configuration_helpers.cpp +++ b/lib/du_manager/converters/scheduler_configuration_helpers.cpp @@ -68,6 +68,8 @@ srsran::srs_du::make_sched_cell_config_req(du_cell_index_t cell_index, sched_req.si_scheduling->si_messages[i].period_radio_frames = du_cfg.si_config->si_sched_info[i].si_period_radio_frames; sched_req.si_scheduling->si_messages[i].msg_len = si_payload_sizes[i + 1]; + sched_req.si_scheduling->si_messages[i].si_window_position = + du_cfg.si_config->si_sched_info[i].si_window_position; } } diff --git a/lib/du_manager/du_ue/du_ue_adapters.h b/lib/du_manager/du_ue/du_ue_adapters.h index d48528c301..aba873f2f1 100644 --- a/lib/du_manager/du_ue/du_ue_adapters.h +++ b/lib/du_manager/du_ue/du_ue_adapters.h @@ -137,47 +137,57 @@ class rlc_f1u_tx_sdu_adapter : public rlc_rx_upper_layer_data_notifier class rlc_f1c_tx_data_notifier : public rlc_tx_upper_layer_data_notifier { public: - void connect(f1c_bearer& bearer_) { bearer = &bearer_; } + void connect(f1c_bearer& bearer_) { bearer.store(&bearer_, std::memory_order_relaxed); } void disconnect(); void on_transmitted_sdu(uint32_t max_deliv_pdcp_sn) override { - srsran_assert(bearer != nullptr, "RLC to F1-C TX data notifier is disconnected"); - bearer->handle_transmit_notification(max_deliv_pdcp_sn); + f1c_bearer* b = bearer.load(std::memory_order_relaxed); + srsran_assert(b != nullptr, "RLC to F1-C TX data notifier is disconnected"); + b->handle_transmit_notification(max_deliv_pdcp_sn); } void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override { - srsran_assert(bearer != nullptr, "RLC to F1-C TX data notifier is disconnected"); - bearer->handle_delivery_notification(max_deliv_pdcp_sn); + f1c_bearer* b = bearer.load(std::memory_order_relaxed); + srsran_assert(b != nullptr, "RLC to F1-C TX data notifier is disconnected"); + b->handle_delivery_notification(max_deliv_pdcp_sn); } private: - f1c_bearer* bearer = nullptr; + /// An atomic pointer to the handler. This pointer may be changed by \c disconnect from UE thread while the cell + /// thread still uses this to notify F1 of transmitted/delivered PDCP SNs. + /// The lifetime of the F1 bearer exceeds the \c disconnect and is synchronized with the scheduler. + std::atomic bearer = nullptr; }; class rlc_f1u_tx_data_notifier : public rlc_tx_upper_layer_data_notifier { public: - void connect(f1u_tx_delivery_handler& handler_) { handler = &handler_; } + void connect(f1u_tx_delivery_handler& handler_) { handler.store(&handler_, std::memory_order_relaxed); } void disconnect(); void on_transmitted_sdu(uint32_t max_deliv_pdcp_sn) override { - srsran_assert(handler != nullptr, "RLC to F1-U TX data notifier is disconnected"); - handler->handle_transmit_notification(max_deliv_pdcp_sn); + f1u_tx_delivery_handler* h = handler.load(std::memory_order_relaxed); + srsran_assert(h != nullptr, "RLC to F1-U TX data notifier is disconnected"); + h->handle_transmit_notification(max_deliv_pdcp_sn); } void on_delivered_sdu(uint32_t max_deliv_pdcp_sn) override { - srsran_assert(handler != nullptr, "RLC to F1-U TX data notifier is disconnected"); - handler->handle_delivery_notification(max_deliv_pdcp_sn); + f1u_tx_delivery_handler* h = handler.load(std::memory_order_relaxed); + srsran_assert(h != nullptr, "RLC to F1-U TX data notifier is disconnected"); + h->handle_delivery_notification(max_deliv_pdcp_sn); } private: - f1u_tx_delivery_handler* handler = nullptr; + /// An atomic pointer to the handler. This pointer may be changed by \c disconnect from UE thread while the cell + /// thread still uses this to notify F1 of transmitted/delivered PDCP SNs. + /// The lifetime of the F1 bearer exceeds the \c disconnect and is synchronized with the scheduler. + std::atomic handler = nullptr; }; class rlc_tx_control_notifier : public rlc_tx_upper_layer_control_notifier 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 c99684f5f3..5869ed17d9 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 @@ -90,7 +90,7 @@ du_pucch_resource_manager::du_pucch_resource_manager(span // Compute fundamental CSI report period. // TODO: Handle more than one CSI report period. if (default_csi_report_cfg.has_value()) { - const auto& rep = srsran::variant_get( + const auto& rep = std::get( default_csi_report_cfg->report_cfg_type); csi_period_slots = csi_report_periodicity_to_uint(rep.report_slot_period); } @@ -154,9 +154,9 @@ du_pucch_resource_manager::find_optimal_csi_report_slot_offset( // TODO: Support more than one nzp-CSI-RS resource for measurement. const csi_res_config_id_t csi_res_cfg_id = csi_meas_cfg.csi_report_cfg_list[0].res_for_channel_meas; const csi_resource_config& csi_res_cfg = csi_meas_cfg.csi_res_cfg_list[csi_res_cfg_id]; - const auto& nzp_csi_rs_ssb = variant_get(csi_res_cfg.csi_rs_res_set_list); - const auto& csi_set = csi_meas_cfg.nzp_csi_rs_res_set_list[nzp_csi_rs_ssb.nzp_csi_rs_res_set_list[0]]; - const nzp_csi_rs_resource& csi_res = csi_meas_cfg.nzp_csi_rs_res_list[csi_set.nzp_csi_rs_res[0]]; + const auto& nzp_csi_rs_ssb = std::get(csi_res_cfg.csi_rs_res_set_list); + const auto& csi_set = csi_meas_cfg.nzp_csi_rs_res_set_list[nzp_csi_rs_ssb.nzp_csi_rs_res_set_list[0]]; + const nzp_csi_rs_resource& csi_res = csi_meas_cfg.nzp_csi_rs_res_list[csi_set.nzp_csi_rs_res[0]]; 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; @@ -247,21 +247,20 @@ bool du_pucch_resource_manager::alloc_resources(cell_group_config& cell_grp_cfg) sr_res_offset = *sr_res_offset_it; free_sr_list.erase(sr_res_offset_it); break; - } else { - cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list = {*default_csi_report_cfg}; - - auto optimal_res_it = get_csi_resource_offset( - cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg.value(), sr_res_offset_it->second, free_csi_list); - - if (optimal_res_it != free_csi_list.end()) { - // At this point the allocation has been successful. Remove SR and CSI resources assigned to this UE from the - // lists of free resources. - csi_res_offset = *optimal_res_it; - free_csi_list.erase(optimal_res_it); - sr_res_offset = *sr_res_offset_it; - free_sr_list.erase(sr_res_offset_it); - break; - } + } + cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list = {*default_csi_report_cfg}; + + auto optimal_res_it = get_csi_resource_offset( + cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg.value(), sr_res_offset_it->second, free_csi_list); + + if (optimal_res_it != free_csi_list.end()) { + // At this point the allocation has been successful. Remove SR and CSI resources assigned to this UE from the + // lists of free resources. + csi_res_offset = *optimal_res_it; + free_csi_list.erase(optimal_res_it); + sr_res_offset = *sr_res_offset_it; + free_sr_list.erase(sr_res_offset_it); + break; } } ++sr_res_offset_it; @@ -296,7 +295,7 @@ bool du_pucch_resource_manager::alloc_resources(cell_group_config& cell_grp_cfg) cell_grp_cfg.cells[0].serv_cell_cfg.ul_config->init_ul_bwp.pucch_cfg->sr_res_list.front().offset = sr_res_offset.value().second; if (cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg.has_value()) { - srsran::variant_get( + std::get( cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list[0].report_cfg_type) .report_slot_offset = csi_res_offset.value().second; } @@ -313,7 +312,7 @@ void du_pucch_resource_manager::dealloc_resources(cell_group_config& cell_grp_cf unsigned csi_offset = 0; if (cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg.has_value()) { - auto& target_csi_cfg = srsran::variant_get( + auto& target_csi_cfg = std::get( cell_grp_cfg.cells[0].serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list[0].report_cfg_type); csi_offset = target_csi_cfg.report_slot_offset; cells[cell_grp_cfg.cells[0].serv_cell_cfg.cell_index].csi_res_offset_free_list.emplace_back( 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 9593b10fd9..540e5df593 100644 --- a/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp +++ b/lib/du_manager/ran_resource_management/pucch_resource_generator.cpp @@ -26,6 +26,8 @@ using namespace srsran; using namespace srs_du; +namespace { + struct pucch_grant { pucch_format format; ofdm_symbol_range symbols; @@ -34,15 +36,17 @@ struct pucch_grant { std::optional occ_cs_idx; }; -// Returns the number of possible spreading factors which is a function of the number of symbols. +} // namespace + +/// Returns the number of possible spreading factors which is a function of the number of symbols. static unsigned format1_symb_to_spreading_factor(bounded_integer f1_symbols) { // As per Table 6.3.2.4.1-1, TS 38.211. return f1_symbols.to_uint() / 2; }; -// Given the OCC-CS index (implementation-defined), maps and returns the \c initialCyclicShift, defined as per -// PUCCH-format1, in PUCCH-Config, TS 38.331. +/// Given the OCC-CS index (implementation-defined), maps and returns the \c initialCyclicShift, defined as per +/// PUCCH-format1, in PUCCH-Config, TS 38.331. static unsigned occ_cs_index_to_cyclic_shift(unsigned occ_cs_idx, unsigned nof_css) { // NOTE: the OCC-CS index -> (CS, OCC) mapping is defined as follows. @@ -55,8 +59,8 @@ static unsigned occ_cs_index_to_cyclic_shift(unsigned occ_cs_idx, unsigned nof_c return (occ_cs_idx * cs_step) % max_nof_css; } -// Given the OCC-CS index (implementation-defined), maps and returns the \c timeDomainOCC, defined as per PUCCH-format1, -// in PUCCH-Config, TS 38.331. +/// Given the OCC-CS index (implementation-defined), maps and returns the \c timeDomainOCC, defined as per +/// PUCCH-format1, in PUCCH-Config, TS 38.331. static unsigned occ_cs_index_to_occ(unsigned occ_cs_idx, unsigned nof_css) { // NOTE: the OCC-CS index -> (CS, OCC) mapping is defined as follows. @@ -336,7 +340,7 @@ error_type srsran::srs_du::pucch_parameters_validator(unsigned // 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()); - unsigned nof_f1_rbs = + 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) { @@ -386,7 +390,7 @@ static std::vector merge_f1_f2_resource_lists(const std::vector< unsigned f1_rbs_occupancy_low_freq = 0; unsigned f1_rbs_occupancy_hi_freq = 0; for (const auto& res_f1 : pucch_f1_resource_list) { - const unsigned res_id = static_cast(resource_list.size()); + 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()}; @@ -427,7 +431,7 @@ static std::vector merge_f1_f2_resource_lists(const std::vector< } for (const auto& res_f2 : pucch_f2_resource_list) { - const unsigned res_id = static_cast(resource_list.size()); + 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}}; @@ -513,8 +517,8 @@ static unsigned cell_res_list_validator(const std::vector& auto count_resources = [&res_list](pucch_format format) { unsigned cnt = 0; - for (auto it = res_list.begin(); it != res_list.end(); ++it) { - if (it->format == format) { + for (const auto& it : res_list) { + if (it.format == format) { ++cnt; } } @@ -659,7 +663,7 @@ bool srsran::srs_du::ue_pucch_config_builder(serving_cell_config& .second_hop_prb = csi_cell_res.second_hop_prb, .format = csi_cell_res.format, .format_params = csi_cell_res.format_params}); - srsran::variant_get( + std::get( serv_cell_cfg.csi_meas_cfg->csi_report_cfg_list[0].report_cfg_type) .pucch_csi_res_list.front() .pucch_res_id = {csi_cell_res.res_id.cell_res_id, ue_pucch_res_id}; diff --git a/lib/e1ap/CMakeLists.txt b/lib/e1ap/CMakeLists.txt index 7cca9c1dea..ca95f6c696 100644 --- a/lib/e1ap/CMakeLists.txt +++ b/lib/e1ap/CMakeLists.txt @@ -21,3 +21,4 @@ add_subdirectory(common) add_subdirectory(cu_cp) add_subdirectory(cu_up) +add_subdirectory(gateways) \ No newline at end of file diff --git a/lib/e1ap/common/e1ap_asn1_converters.h b/lib/e1ap/common/e1ap_asn1_converters.h index 42fa084e0d..96aa64e061 100644 --- a/lib/e1ap/common/e1ap_asn1_converters.h +++ b/lib/e1ap/common/e1ap_asn1_converters.h @@ -22,20 +22,23 @@ #pragma once -#include "srsran/adt/optional.h" #include "srsran/asn1/asn1_utils.h" #include "srsran/asn1/e1ap/e1ap_ies.h" -#include "srsran/cu_cp/cu_cp_types.h" -#include "srsran/e1ap/cu_cp/e1ap_cu_cp.h" -#include "srsran/e1ap/cu_cp/e1ap_cu_cp_bearer_context_update.h" +#include "srsran/e1ap/common/e1ap_types.h" +#include "srsran/pdcp/pdcp_config.h" #include "srsran/ran/bcd_helpers.h" #include "srsran/ran/cause/e1ap_cause.h" +#include "srsran/ran/cu_types.h" +#include "srsran/ran/nr_cgi.h" #include "srsran/ran/qos_prio_level.h" +#include "srsran/ran/s_nssai.h" +#include "srsran/sdap/sdap_config.h" +#include "srsran/security/security.h" #include "srsran/support/error_handling.h" #include -#include namespace srsran { + /// \brief Converts type \c ciphering_algorithm to an E1AP ASN.1 type. /// \param[in] ciph_algo Cyphering Algorithm object. /// \return The E1AP ASN.1 object where the result of the conversion is stored. @@ -58,7 +61,7 @@ ciphering_algorithm_to_e1ap_asn1(const srsran::security::ciphering_algorithm& ci asn1_ciph_algo = asn1::e1ap::ciphering_algorithm_opts::c_neg128_nea3; break; default: - srsran_assert(false, "Invalid ciphering algorithm ({})", ciph_algo); + report_fatal_error("Invalid ciphering algorithm ({})", ciph_algo); break; } @@ -71,7 +74,7 @@ ciphering_algorithm_to_e1ap_asn1(const srsran::security::ciphering_algorithm& ci inline srsran::security::ciphering_algorithm e1ap_asn1_to_ciphering_algorithm(const asn1::e1ap::ciphering_algorithm_e& asn1_ciph_algo) { - srsran::security::ciphering_algorithm ciph_algo = {}; + srsran::security::ciphering_algorithm ciph_algo; switch (asn1_ciph_algo) { case asn1::e1ap::ciphering_algorithm_opts::nea0: @@ -87,7 +90,7 @@ e1ap_asn1_to_ciphering_algorithm(const asn1::e1ap::ciphering_algorithm_e& asn1_c ciph_algo = srsran::security::ciphering_algorithm::nea3; break; default: - srsran_assert(false, "Invalid ciphering algorithm ({})", asn1_ciph_algo); + report_fatal_error("Invalid ciphering algorithm ({})", asn1_ciph_algo); } return ciph_algo; @@ -115,7 +118,7 @@ integrity_algorithm_to_e1ap_asn1(const srsran::security::integrity_algorithm& in asn1_int_algo = asn1::e1ap::integrity_protection_algorithm_opts::i_neg128_nia3; break; default: - srsran_assert(false, "Invalid integrity protection algorithm ({})", int_algo); + report_fatal_error("Invalid integrity protection algorithm ({})", int_algo); } return asn1_int_algo; @@ -127,7 +130,7 @@ integrity_algorithm_to_e1ap_asn1(const srsran::security::integrity_algorithm& in inline srsran::security::integrity_algorithm e1ap_asn1_to_integrity_algorithm(const asn1::e1ap::integrity_protection_algorithm_e& asn1_int_algo) { - srsran::security::integrity_algorithm int_algo = {}; + srsran::security::integrity_algorithm int_algo; switch (asn1_int_algo) { case asn1::e1ap::integrity_protection_algorithm_opts::nia0: @@ -143,7 +146,7 @@ e1ap_asn1_to_integrity_algorithm(const asn1::e1ap::integrity_protection_algorith int_algo = srsran::security::integrity_algorithm::nia3; break; default: - srsran_assert(false, "Invalid integrity protection algorithm ({})", asn1_int_algo); + report_fatal_error("Invalid integrity protection algorithm ({})", asn1_int_algo); } return int_algo; @@ -186,10 +189,7 @@ inline asn1::e1ap::nr_cgi_s nr_cgi_to_e1ap_asn1(const nr_cell_global_id_t& nr_cg { asn1::e1ap::nr_cgi_s asn1_nr_cgi; - // nr cell id asn1_nr_cgi.nr_cell_id.from_number(nr_cgi.nci); - - // plmn id asn1_nr_cgi.plmn_id.from_string(nr_cgi.plmn_hex); return asn1_nr_cgi; @@ -224,7 +224,7 @@ inline asn1::e1ap::sdap_hdr_dl_opts::options sdap_hdr_dl_cfg_to_e1ap_asn1(sdap_h /// \brief Converts type \c sdap_config to an E1AP ASN.1 type. /// \param sdap_cfg sdap config object. /// \return The E1AP ASN.1 object where the result of the conversion is stored. -inline asn1::e1ap::sdap_cfg_s sdap_config_to_e1ap_asn1(sdap_config_t sdap_cfg) +inline asn1::e1ap::sdap_cfg_s sdap_config_to_e1ap_asn1(const sdap_config_t& sdap_cfg) { asn1::e1ap::sdap_cfg_s asn1_sdap_cfg; @@ -285,12 +285,7 @@ inline sdap_config_t e1ap_asn1_to_sdap_config(asn1::e1ap::sdap_cfg_s asn1_sdap_c { sdap_config_t sdap_cfg; - if (asn1_sdap_cfg.default_drb == asn1::e1ap::default_drb_opts::options::true_value) { - sdap_cfg.default_drb = true; - } else { - sdap_cfg.default_drb = false; - } - + sdap_cfg.default_drb = asn1_sdap_cfg.default_drb == asn1::e1ap::default_drb_opts::options::true_value; sdap_cfg.sdap_hdr_dl = e1ap_asn1_to_sdap_hdr_dl_cfg(asn1_sdap_cfg.sdap_hdr_dl); sdap_cfg.sdap_hdr_ul = e1ap_asn1_to_sdap_hdr_ul_cfg(asn1_sdap_cfg.sdap_hdr_ul); @@ -312,7 +307,7 @@ inline asn1::e1ap::rlc_mode_e rlc_mode_to_asn1(srsran::pdcp_rlc_mode rlc_mod) asn1_rlc_mode = asn1::e1ap::rlc_mode_opts::rlc_am; break; default: - srsran_assert(false, "Invalid RLC mode ({})", rlc_mod); + report_fatal_error("Invalid RLC mode ({})", rlc_mod); } return asn1_rlc_mode; @@ -323,7 +318,7 @@ inline asn1::e1ap::rlc_mode_e rlc_mode_to_asn1(srsran::pdcp_rlc_mode rlc_mod) /// \return The rlc_mode object where the result of the conversion is stored. inline srsran::pdcp_rlc_mode asn1_to_rlc_mode(asn1::e1ap::rlc_mode_e asn1_rlc_mod) { - srsran::pdcp_rlc_mode rlc_mode = {}; + srsran::pdcp_rlc_mode rlc_mode; switch (asn1_rlc_mod) { case asn1::e1ap::rlc_mode_opts::rlc_um_bidirectional: @@ -335,7 +330,7 @@ inline srsran::pdcp_rlc_mode asn1_to_rlc_mode(asn1::e1ap::rlc_mode_e asn1_rlc_mo rlc_mode = srsran::pdcp_rlc_mode::am; break; default: - srsran_assert(false, "Invalid RLC mode: {}", asn1_rlc_mod.to_string()); + report_fatal_error("Invalid RLC mode: {}", asn1_rlc_mod.to_string()); break; } @@ -355,7 +350,7 @@ inline asn1::e1ap::pdcp_sn_size_e pdcp_sn_size_to_asn1(pdcp_sn_size sn_size) asn1_sn_size = asn1::e1ap::pdcp_sn_size_e::s_neg18; break; default: - srsran_assert(false, "Unsupported PDCP SN size. PDCP SN size={}", sn_size); + report_fatal_error("Unsupported PDCP SN size. PDCP SN size={}", sn_size); } return asn1_sn_size; @@ -364,7 +359,7 @@ inline asn1::e1ap::pdcp_sn_size_e pdcp_sn_size_to_asn1(pdcp_sn_size sn_size) /// \brief Convert E1AP ASN.1 to PDCP SN size. inline pdcp_sn_size asn1_to_pdcp_sn_size(asn1::e1ap::pdcp_sn_size_e asn1_sn_size) { - pdcp_sn_size sn_size = {}; + pdcp_sn_size sn_size; switch (asn1_sn_size) { case asn1::e1ap::pdcp_sn_size_e::s_neg12: @@ -374,7 +369,7 @@ inline pdcp_sn_size asn1_to_pdcp_sn_size(asn1::e1ap::pdcp_sn_size_e asn1_sn_size sn_size = pdcp_sn_size::size18bits; break; default: - srsran_assert(false, "Unsupported PDCP SN size. PDCP SN size={}", asn1_sn_size); + report_fatal_error("Unsupported PDCP SN size. PDCP SN size={}", asn1_sn_size); } return sn_size; @@ -383,7 +378,7 @@ inline pdcp_sn_size asn1_to_pdcp_sn_size(asn1::e1ap::pdcp_sn_size_e asn1_sn_size /// \brief Converts E1AP ASN.1 discard timer type to \c pdcp_discard_timer type. inline pdcp_discard_timer asn1_to_pdcp_discard_timer(asn1::e1ap::discard_timer_e asn1_discard_timer) { - pdcp_discard_timer discard_timer = {}; + pdcp_discard_timer discard_timer; switch (asn1_discard_timer) { case asn1::e1ap::discard_timer_opts::ms10: @@ -435,7 +430,7 @@ inline pdcp_discard_timer asn1_to_pdcp_discard_timer(asn1::e1ap::discard_timer_e discard_timer = pdcp_discard_timer::infinity; break; default: - srsran_assert(false, "Unsupported PDCP discard timer. PDCP discard timer={}", asn1_discard_timer); + report_fatal_error("Unsupported PDCP discard timer. PDCP discard timer={}", asn1_discard_timer); } return discard_timer; } @@ -495,7 +490,7 @@ inline asn1::e1ap::discard_timer_e pdcp_discard_timer_to_asn1(pdcp_discard_timer asn1_discard_timer = asn1::e1ap::discard_timer_opts::infinity; break; default: - srsran_assert(false, "Unsupported PDCP discard timer. PDCP discard timer={}", discard_timer); + report_fatal_error("Unsupported PDCP discard timer. PDCP discard timer={}", discard_timer); } return asn1_discard_timer; @@ -504,7 +499,7 @@ inline asn1::e1ap::discard_timer_e pdcp_discard_timer_to_asn1(pdcp_discard_timer /// \brief Converts E1AP ASN.1 t-Reordering type to \c pdcp_t_reordering type. inline pdcp_t_reordering asn1_to_pdcp_t_reordering(asn1::e1ap::t_reordering_e asn1_t_reordering) { - pdcp_t_reordering t_reordering = {}; + pdcp_t_reordering t_reordering; // t-Reordering switch (asn1_t_reordering) { @@ -617,7 +612,7 @@ inline pdcp_t_reordering asn1_to_pdcp_t_reordering(asn1::e1ap::t_reordering_e as t_reordering = pdcp_t_reordering::ms3000; break; default: - srsran_assert(false, "Unsupported PDCP t-reordering timer. PDCP t-reordering timer={}", asn1_t_reordering); + report_fatal_error("Unsupported PDCP t-reordering timer. PDCP t-reordering timer={}", asn1_t_reordering); } return t_reordering; @@ -740,7 +735,7 @@ inline asn1::e1ap::t_reordering_e pdcp_t_reordering_to_asn1(pdcp_t_reordering t_ asn1_t_reordering = asn1::e1ap::t_reordering_e::nulltype; break; default: - srsran_assert(false, "Unsupported PDCP t-reordering timer. PDCP t-reordering timer={}", t_reordering); + report_fatal_error("Unsupported PDCP t-reordering timer. PDCP t-reordering timer={}", t_reordering); } return asn1_t_reordering; @@ -753,13 +748,13 @@ inline asn1::e1ap::pdcp_cfg_s pdcp_config_to_e1ap_asn1(e1ap_pdcp_config pdcp_cfg { asn1::e1ap::pdcp_cfg_s asn1_pdcp_cfg; - // pdcp sn size dl + // pdcp sn size dl. asn1_pdcp_cfg.pdcp_sn_size_dl = pdcp_sn_size_to_asn1(pdcp_cfg.pdcp_sn_size_dl); - // pdcp sn size ul + // pdcp sn size ul. asn1_pdcp_cfg.pdcp_sn_size_ul = pdcp_sn_size_to_asn1(pdcp_cfg.pdcp_sn_size_ul); - // rlc mode + // rlc mode. asn1_pdcp_cfg.rlc_mode = rlc_mode_to_asn1(pdcp_cfg.rlc_mod); // rohc params @@ -796,25 +791,25 @@ inline asn1::e1ap::pdcp_cfg_s pdcp_config_to_e1ap_asn1(e1ap_pdcp_config pdcp_cfg } } - // t reordering timer + // t reordering timer. if (pdcp_cfg.t_reordering_timer.has_value()) { asn1_pdcp_cfg.t_reordering_timer_present = true; asn1_pdcp_cfg.t_reordering_timer.t_reordering = pdcp_t_reordering_to_asn1(pdcp_cfg.t_reordering_timer.value()); } - // discard timer + // discard timer. if (pdcp_cfg.discard_timer.has_value()) { asn1_pdcp_cfg.discard_timer_present = true; asn1_pdcp_cfg.discard_timer = pdcp_discard_timer_to_asn1(pdcp_cfg.discard_timer.value()); } - // ul data split thres + // ul data split thres. if (pdcp_cfg.ul_data_split_thres.has_value()) { asn1_pdcp_cfg.ul_data_split_thres_present = true; asn1::number_to_enum(asn1_pdcp_cfg.ul_data_split_thres, pdcp_cfg.ul_data_split_thres.value()); } - // pdcp dupl + // pdcp dupl. if (pdcp_cfg.pdcp_dupl.has_value()) { asn1_pdcp_cfg.pdcp_dupl_present = true; if (pdcp_cfg.pdcp_dupl.value()) { @@ -824,7 +819,7 @@ inline asn1::e1ap::pdcp_cfg_s pdcp_config_to_e1ap_asn1(e1ap_pdcp_config pdcp_cfg } } - // pdcp reest + // pdcp reest. if (pdcp_cfg.pdcp_reest.has_value()) { asn1_pdcp_cfg.pdcp_reest_present = true; if (pdcp_cfg.pdcp_reest.value()) { @@ -834,7 +829,7 @@ inline asn1::e1ap::pdcp_cfg_s pdcp_config_to_e1ap_asn1(e1ap_pdcp_config pdcp_cfg } } - // pdcp data recovery + // pdcp data recovery. if (pdcp_cfg.pdcp_data_recovery.has_value()) { asn1_pdcp_cfg.pdcp_data_recovery_present = true; if (pdcp_cfg.pdcp_data_recovery.value()) { @@ -844,13 +839,13 @@ inline asn1::e1ap::pdcp_cfg_s pdcp_config_to_e1ap_asn1(e1ap_pdcp_config pdcp_cfg } } - // dupl activation + // dupl activation. if (pdcp_cfg.dupl_activation.has_value()) { asn1_pdcp_cfg.dupl_activation_present = true; asn1::string_to_enum(asn1_pdcp_cfg.dupl_activation, pdcp_cfg.dupl_activation.value()); } - // out of orfder delivery + // out of orfder delivery. if (pdcp_cfg.out_of_order_delivery.has_value()) { asn1_pdcp_cfg.out_of_order_delivery_present = true; if (pdcp_cfg.out_of_order_delivery.value()) { @@ -870,13 +865,13 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc { e1ap_pdcp_config pdcp_cfg; - // pdcp sn size dl + // pdcp sn size dl. pdcp_cfg.pdcp_sn_size_dl = asn1_to_pdcp_sn_size(asn1_pdcp_cfg.pdcp_sn_size_dl); - // pdcp sn size ul + // pdcp sn size ul. pdcp_cfg.pdcp_sn_size_ul = asn1_to_pdcp_sn_size(asn1_pdcp_cfg.pdcp_sn_size_ul); - // rlc mode + // rlc mode. pdcp_cfg.rlc_mod = asn1_to_rlc_mode(asn1_pdcp_cfg.rlc_mode); // rohc params @@ -899,8 +894,8 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc rohc_params.rohc = rohc; pdcp_cfg.rohc_params = rohc_params; } else if (asn1_pdcp_cfg.rohc_params.type().value == asn1::e1ap::rohc_params_c::types_opts::ul_only_rohc) { - e1ap_rohc_params rohc_params = {}; - e1ap_rohc ul_only_rohc; + e1ap_rohc_params rohc_params = {}; + e1ap_rohc ul_only_rohc = {}; ul_only_rohc.max_cid = asn1_pdcp_cfg.rohc_params.ul_only_rohc().max_c_id; ul_only_rohc.rohc_profiles = asn1_pdcp_cfg.rohc_params.ul_only_rohc().rohc_profiles; @@ -919,22 +914,22 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc } } - // t reordering timer + // t reordering timer. if (asn1_pdcp_cfg.t_reordering_timer_present) { pdcp_cfg.t_reordering_timer = asn1_to_pdcp_t_reordering(asn1_pdcp_cfg.t_reordering_timer.t_reordering); } - // discard timer + // discard timer. if (asn1_pdcp_cfg.discard_timer_present) { pdcp_cfg.discard_timer = asn1_to_pdcp_discard_timer(asn1_pdcp_cfg.discard_timer); } - // ul data split thres + // ul data split thres. if (asn1_pdcp_cfg.ul_data_split_thres_present) { pdcp_cfg.ul_data_split_thres = asn1_pdcp_cfg.ul_data_split_thres.to_number(); } - // pdcp dupl + // pdcp dupl. if (asn1_pdcp_cfg.pdcp_dupl_present) { if (asn1_pdcp_cfg.pdcp_dupl == asn1::e1ap::pdcp_dupl_opts::options::true_value) { pdcp_cfg.pdcp_dupl = true; @@ -943,7 +938,7 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc } } - // pdcp reest + // pdcp reest. if (asn1_pdcp_cfg.pdcp_reest_present) { if (asn1_pdcp_cfg.pdcp_reest == asn1::e1ap::pdcp_reest_opts::options::true_value) { pdcp_cfg.pdcp_reest = true; @@ -952,7 +947,7 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc } } - // pdcp data recovery + // pdcp data recovery. if (asn1_pdcp_cfg.pdcp_data_recovery_present) { if (asn1_pdcp_cfg.pdcp_data_recovery == asn1::e1ap::pdcp_data_recovery_opts::options::true_value) { pdcp_cfg.pdcp_data_recovery = true; @@ -961,12 +956,12 @@ inline e1ap_pdcp_config e1ap_asn1_to_pdcp_config(asn1::e1ap::pdcp_cfg_s asn1_pdc } } - // dupl activation + // dupl activation. if (asn1_pdcp_cfg.dupl_activation_present) { pdcp_cfg.dupl_activation = asn1_pdcp_cfg.dupl_activation.to_string(); } - // out of orfder delivery + // out of orfder delivery. if (asn1_pdcp_cfg.out_of_order_delivery_present) { if (asn1_pdcp_cfg.out_of_order_delivery == asn1::e1ap::out_of_order_delivery_opts::options::true_value) { pdcp_cfg.out_of_order_delivery = true; @@ -1012,21 +1007,24 @@ inline asn1::e1ap::cause_c cause_to_asn1(e1ap_cause_t cause) { asn1::e1ap::cause_c asn1_cause; - if (variant_holds_alternative(cause)) { - asn1_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_transport() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_protocol() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_misc() = static_cast(variant_get(cause)); - } else { - report_fatal_error("Cannot convert cause to E1AP type: {}", cause); + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_radio_network() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_transport() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_protocol() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_misc() = static_cast(*result); + return asn1_cause; } + report_fatal_error("Cannot convert cause to E1AP type: {}", cause); return asn1_cause; } @@ -1058,12 +1056,13 @@ inline nr_cell_global_id_t e1ap_asn1_to_cgi(const asn1::e1ap::nr_cgi_s& asn1_cgi uint32_t encoded_plmn = asn1_cgi.plmn_id.to_number(); ngap_plmn_to_mccmnc(encoded_plmn, &cgi.mcc, &cgi.mnc); - std::string mcc_string, mnc_string; + std::string mcc_string; + std::string mnc_string; mcc_to_string(cgi.mcc, &mcc_string); mnc_to_string(cgi.mnc, &mnc_string); cgi.plmn = mcc_string + mnc_string; - // Set PLMN hex string + // Set PLMN hex string. cgi.plmn_hex = asn1_cgi.plmn_id.to_string(); cgi.nci = asn1_cgi.nr_cell_id.to_number(); @@ -1117,12 +1116,12 @@ inline void e1ap_asn1_to_flow_map_info(slotted_id_vector& for (const auto& drb_setup_item_ng_ran : drb_setup_list_ng_ran) { template_asn1_item asn1_drb_setup_item; - // Add DRB ID + // Add DRB ID. asn1_drb_setup_item.drb_id = drb_id_to_uint(drb_setup_item_ng_ran.drb_id); - // Add UL UP Transport Params + // Add UL UP Transport Params. for (const auto& ul_up_transport_param : drb_setup_item_ng_ran.ul_up_transport_params) { asn1::e1ap::up_params_item_s asn1_up_transport_param; up_transport_layer_info_to_asn1(asn1_up_transport_param.up_tnl_info, ul_up_transport_param.up_tnl_info); asn1_drb_setup_item.ul_up_transport_params.push_back(asn1_up_transport_param); } - // Add Flow setup List + // Add Flow setup List. for (const auto& qos_flow_item : drb_setup_item_ng_ran.flow_setup_list) { asn1::e1ap::qos_flow_item_s asn1_flow_item; asn1_flow_item.qos_flow_id = qos_flow_id_to_uint(qos_flow_item.qos_flow_id); asn1_drb_setup_item.flow_setup_list.push_back(asn1_flow_item); } - // Add Flow Failed List + // Add Flow Failed List. for (const auto& flow_failed_item : drb_setup_item_ng_ran.flow_failed_list) { asn1::e1ap::qos_flow_failed_item_s asn1_flow_failed_item; asn1_flow_failed_item.qos_flow_id = qos_flow_id_to_uint(flow_failed_item.qos_flow_id); asn1_drb_setup_item.flow_failed_list.push_back(asn1_flow_failed_item); } - // Add DRB Data Forwarding Info Response + // Add DRB Data Forwarding Info Response. if (drb_setup_item_ng_ran.drb_data_forwarding_info_resp.has_value()) { asn1_drb_setup_item.drb_data_forwarding_info_resp_present = true; if (drb_setup_item_ng_ran.drb_data_forwarding_info_resp.value().ul_data_forwarding.has_value()) { diff --git a/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.cpp b/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.cpp index 49de97fb6c..6194c6d064 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.cpp +++ b/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.cpp @@ -41,9 +41,9 @@ class e1ap_rx_pdu_adapter final : public e1ap_message_notifier } // namespace -e1ap_cu_up_connection_handler::e1ap_cu_up_connection_handler(e1ap_connection_client& e1ap_client_handler_, - e1ap_message_handler& e1ap_pdu_handler_) : - e1ap_client_handler(e1ap_client_handler_), +e1ap_cu_up_connection_handler::e1ap_cu_up_connection_handler(e1_connection_client& e1_client_handler_, + e1ap_message_handler& e1ap_pdu_handler_) : + e1_client_handler(e1_client_handler_), e1ap_pdu_handler(e1ap_pdu_handler_), logger(srslog::fetch_basic_logger("CU-UP-E1")) { @@ -52,7 +52,7 @@ e1ap_cu_up_connection_handler::e1ap_cu_up_connection_handler(e1ap_connection_cli SRSRAN_NODISCARD e1ap_message_notifier* e1ap_cu_up_connection_handler::connect_to_cu_cp() { e1ap_notifier = - e1ap_client_handler.handle_cu_up_connection_request(std::make_unique(e1ap_pdu_handler)); + e1_client_handler.handle_cu_up_connection_request(std::make_unique(e1ap_pdu_handler)); return e1ap_notifier.get(); } diff --git a/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.h b/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.h index 5c793868b0..23e87a368b 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.h +++ b/lib/e1ap/cu_up/e1ap_cu_up_connection_handler.h @@ -22,8 +22,8 @@ #pragma once -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" #include "srsran/e1ap/cu_up/e1ap_cu_up.h" +#include "srsran/e1ap/gateways/e1_connection_client.h" namespace srsran { namespace srs_cu_up { @@ -31,15 +31,15 @@ namespace srs_cu_up { class e1ap_cu_up_connection_handler { public: - e1ap_cu_up_connection_handler(e1ap_connection_client& e1ap_client_handler_, e1ap_message_handler& e1ap_pdu_handler_); + e1ap_cu_up_connection_handler(e1_connection_client& e1ap_client_handler_, e1ap_message_handler& e1ap_pdu_handler_); SRSRAN_NODISCARD e1ap_message_notifier* connect_to_cu_cp(); SRSRAN_NODISCARD bool is_connected() const { return e1ap_notifier != nullptr; } private: - e1ap_connection_client& e1ap_client_handler; - e1ap_message_handler& e1ap_pdu_handler; - srslog::basic_logger& logger; + e1_connection_client& e1_client_handler; + e1ap_message_handler& e1ap_pdu_handler; + srslog::basic_logger& logger; std::unique_ptr e1ap_notifier; }; diff --git a/lib/e1ap/cu_up/e1ap_cu_up_factory.cpp b/lib/e1ap/cu_up/e1ap_cu_up_factory.cpp index a9e48712a5..2ddaf00079 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_factory.cpp +++ b/lib/e1ap/cu_up/e1ap_cu_up_factory.cpp @@ -28,11 +28,11 @@ using namespace srsran; using namespace srs_cu_up; -std::unique_ptr srsran::srs_cu_up::create_e1ap(e1ap_connection_client& e1ap_client_handler_, - e1ap_cu_up_notifier& cu_up_notifier_, - timer_manager& timers_, - task_executor& cu_up_exec_) +std::unique_ptr srsran::srs_cu_up::create_e1ap(e1_connection_client& e1_client_handler_, + e1ap_cu_up_notifier& cu_up_notifier_, + timer_manager& timers_, + task_executor& cu_up_exec_) { - auto e1ap_cu_up = std::make_unique(e1ap_client_handler_, cu_up_notifier_, timers_, cu_up_exec_); + auto e1ap_cu_up = std::make_unique(e1_client_handler_, cu_up_notifier_, timers_, cu_up_exec_); return e1ap_cu_up; } diff --git a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp index 91585a4960..f8fe5b589e 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp +++ b/lib/e1ap/cu_up/e1ap_cu_up_impl.cpp @@ -26,7 +26,6 @@ #include "e1ap_cu_up_asn1_helpers.h" #include "procedures/e1ap_cu_up_setup_procedure.h" #include "srsran/e1ap/common/e1ap_message.h" -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" #include "srsran/ran/bcd_helpers.h" #include "srsran/support/timers.h" #include @@ -51,15 +50,15 @@ class e1ap_rx_pdu_adapter final : public e1ap_message_notifier } // namespace -e1ap_cu_up_impl::e1ap_cu_up_impl(e1ap_connection_client& e1ap_client_handler_, - e1ap_cu_up_notifier& cu_up_notifier_, - timer_manager& timers_, - task_executor& cu_up_exec_) : +e1ap_cu_up_impl::e1ap_cu_up_impl(e1_connection_client& e1_client_handler_, + e1ap_cu_up_notifier& cu_up_notifier_, + timer_manager& timers_, + task_executor& cu_up_exec_) : logger(srslog::fetch_basic_logger("CU-UP-E1")), cu_up_notifier(cu_up_notifier_), timers(timers_), cu_up_exec(cu_up_exec_), - connection_handler(e1ap_client_handler_, *this), + connection_handler(e1_client_handler_, *this), ue_ctxt_list(logger), ev_mng(std::make_unique(timer_factory{timers, cu_up_exec})) { diff --git a/lib/e1ap/cu_up/e1ap_cu_up_impl.h b/lib/e1ap/cu_up/e1ap_cu_up_impl.h index f48b82aa99..db2db5f388 100644 --- a/lib/e1ap/cu_up/e1ap_cu_up_impl.h +++ b/lib/e1ap/cu_up/e1ap_cu_up_impl.h @@ -34,16 +34,16 @@ namespace srsran { namespace srs_cu_up { -class e1ap_connection_client; +class e1_connection_client; class e1ap_event_manager; class e1ap_cu_up_impl final : public e1ap_interface { public: - e1ap_cu_up_impl(e1ap_connection_client& e1ap_client_handler_, - e1ap_cu_up_notifier& cu_up_notifier_, - timer_manager& timers_, - task_executor& cu_up_exec_); + e1ap_cu_up_impl(e1_connection_client& e1_client_handler_, + e1ap_cu_up_notifier& cu_up_notifier_, + timer_manager& timers_, + task_executor& cu_up_exec_); ~e1ap_cu_up_impl() override; // e1ap connection manager functions diff --git a/lib/e1ap/gateways/CMakeLists.txt b/lib/e1ap/gateways/CMakeLists.txt new file mode 100644 index 0000000000..48aaf95ab2 --- /dev/null +++ b/lib/e1ap/gateways/CMakeLists.txt @@ -0,0 +1,22 @@ +# +# Copyright 2021-2024 Software Radio Systems Limited +# +# This file is part of srsRAN +# +# srsRAN is free software: you can redistribute it and/or modify +# it under the terms of the GNU Affero General Public License as +# published by the Free Software Foundation, either version 3 of +# the License, or (at your option) any later version. +# +# srsRAN is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU Affero General Public License for more details. +# +# A copy of the GNU Affero General Public License can be found in +# the LICENSE file in the top-level directory of this distribution +# and at http://www.gnu.org/licenses/. +# + +add_library(srsran_e1_gateway e1_local_connector_factory.cpp) +target_link_libraries(srsran_e1_gateway srsran_support srsran_e1ap_common e1ap_asn1) diff --git a/lib/e1ap/gateways/e1_local_connector_factory.cpp b/lib/e1ap/gateways/e1_local_connector_factory.cpp new file mode 100644 index 0000000000..9591ac4e60 --- /dev/null +++ b/lib/e1ap/gateways/e1_local_connector_factory.cpp @@ -0,0 +1,107 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/e1ap/gateways/e1_local_connector_factory.h" +#include "srsran/cu_cp/cu_cp_e1_handler.h" +#include "srsran/e1ap/common/e1ap_message.h" +#include "srsran/pcap/dlt_pcap.h" + +using namespace srsran; + +namespace { + +/// Decorator for e1ap_message_notifier that writes the forwarded PDU to a pcap file. +class e1ap_pdu_pcap_notifier final : public e1ap_message_notifier +{ +public: + e1ap_pdu_pcap_notifier(std::unique_ptr decorated_, + dlt_pcap& pcap_writer_, + srslog::basic_logger& logger_) : + logger(logger_), pcap_writer(pcap_writer_), decorated(std::move(decorated_)) + { + srsran_sanity_check(pcap_writer.is_write_enabled(), "Pcap writing must be enabled."); + } + + void on_new_message(const e1ap_message& msg) override + { + byte_buffer buf; + asn1::bit_ref bref(buf); + if (msg.pdu.pack(bref) != asn1::SRSASN_SUCCESS) { + logger.error("Failed to pack PDU"); + } else { + pcap_writer.push_pdu(std::move(buf)); + } + + // Forward message to decorated class. + decorated->on_new_message(msg); + } + +private: + srslog::basic_logger& logger; + dlt_pcap& pcap_writer; + std::unique_ptr decorated; +}; + +/// Implementation of a CU-UP and CU-CP E1 gateway for the case that the CU-UP and CU-CP are co-located. +class e1_local_connector_impl final : public e1_local_connector +{ +public: + e1_local_connector_impl(const e1_local_connector_config& cfg) : pcap_writer(cfg.pcap) {} + + void attach_cu_cp(srs_cu_cp::cu_cp_e1_handler& cu_cp_e1_mng_) override { cu_cp_e1_mng = &cu_cp_e1_mng_; } + + std::optional get_listen_port() const override { return std::nullopt; } + + std::unique_ptr + handle_cu_up_connection_request(std::unique_ptr cu_up_notifier) override + { + report_fatal_error_if_not(cu_cp_e1_mng != nullptr, "CU-CP has not been attached to E1 gateway."); + + // Decorate DU RX notifier with pcap writing. + if (pcap_writer.is_write_enabled()) { + cu_up_notifier = std::make_unique( + std::move(cu_up_notifier), pcap_writer, srslog::fetch_basic_logger("CU-UP-E1")); + } + + // Create direct connection between CU-CP and CU-UP notifier. + auto cu_notifier = cu_cp_e1_mng->handle_new_cu_up_connection(std::move(cu_up_notifier)); + + // Decorate CU-CP RX notifier with pcap writing. + if (pcap_writer.is_write_enabled()) { + cu_notifier = std::make_unique( + std::move(cu_notifier), pcap_writer, srslog::fetch_basic_logger("CU-CP-E1")); + } + + return cu_notifier; + } + +private: + dlt_pcap& pcap_writer; + srs_cu_cp::cu_cp_e1_handler* cu_cp_e1_mng = nullptr; +}; + +} // namespace + +std::unique_ptr srsran::create_e1_local_connector(const e1_local_connector_config& cfg) +{ + return std::make_unique(cfg); +} diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_asn1_packer.cpp b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_asn1_packer.cpp index cac9e891f1..2faa478259 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_asn1_packer.cpp +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_asn1_packer.cpp @@ -40,7 +40,7 @@ e2sm_kpm_asn1_packer::handle_packed_e2sm_action_definition(const srsran::byte_bu e2sm_action_definition action_def; asn1::cbit_ref bref(action_definition); action_def.service_model = e2sm_service_model_t::KPM; - if (variant_get(action_def.action_definition).unpack(bref) != asn1::SRSASN_SUCCESS) { + if (std::get(action_def.action_definition).unpack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to unpack E2SM KPM Action Definition\n"); action_def.service_model = e2sm_service_model_t::UNKNOWN_SM; } diff --git a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_impl.cpp b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_impl.cpp index f670b0faa4..aaa3ffdae5 100644 --- a/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_impl.cpp +++ b/lib/e2/e2sm/e2sm_kpm/e2sm_kpm_impl.cpp @@ -48,7 +48,7 @@ bool e2sm_kpm_impl::action_supported(const asn1::e2ap::ric_action_to_be_setup_it } logger.info("Admitting action {} (type {})", ric_action.ric_action_id, ric_action.ric_action_type); e2sm_kpm_action_definition_s& e2sm_kpm_action_def = - variant_get(action_def.action_definition); + std::get(action_def.action_definition); switch (e2sm_kpm_action_def.ric_style_type) { case 1: @@ -237,7 +237,7 @@ e2sm_kpm_impl::get_e2sm_report_service(const srsran::byte_buffer& action_definit return nullptr; } e2sm_kpm_action_definition_s& e2sm_kpm_action_def = - variant_get(action_def.action_definition); + std::get(action_def.action_definition); uint32_t ric_style_type = e2sm_kpm_action_def.ric_style_type; switch (ric_style_type) { case 1: diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp index 10ec96f763..f2e1bf3178 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_asn1_packer.cpp @@ -47,7 +47,7 @@ e2sm_rc_asn1_packer::handle_packed_e2sm_action_definition(const srsran::byte_buf e2sm_action_definition action_def; action_def.service_model = e2sm_service_model_t::RC; asn1::cbit_ref bref(action_definition); - if (variant_get(action_def.action_definition).unpack(bref) != + if (std::get(action_def.action_definition).unpack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to unpack E2SM RC Action Definition\n"); } @@ -68,12 +68,12 @@ e2sm_rc_asn1_packer::handle_packed_ric_control_request(const asn1::e2ap::ric_ctr asn1::cbit_ref bref_hdr(req->ric_ctrl_hdr); asn1::cbit_ref bref_msg(req->ric_ctrl_msg); - if (variant_get(ric_control_request.request_ctrl_hdr).unpack(bref_hdr) != + if (std::get(ric_control_request.request_ctrl_hdr).unpack(bref_hdr) != asn1::SRSASN_SUCCESS) { printf("Failed to unpack E2SM RC Control Request Header\n"); } - if (variant_get(ric_control_request.request_ctrl_msg).unpack(bref_msg) != + if (std::get(ric_control_request.request_ctrl_msg).unpack(bref_msg) != asn1::SRSASN_SUCCESS) { printf("Failed to unpack E2SM RC Control Request Message\n"); } @@ -96,7 +96,7 @@ e2_ric_control_response e2sm_rc_asn1_packer::pack_ric_control_response(const e2s e2_control_response.ack->ric_ctrl_outcome_present = true; srsran::byte_buffer buf; asn1::bit_ref bref(buf); - if (variant_get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { + if (std::get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to pack E2SM RC RIC Control Outcome (Ack)\n"); } if (!e2_control_response.ack->ric_ctrl_outcome.resize(buf.length())) { @@ -110,7 +110,7 @@ e2_ric_control_response e2sm_rc_asn1_packer::pack_ric_control_response(const e2s e2_control_response.failure->ric_ctrl_outcome_present = true; srsran::byte_buffer buf; asn1::bit_ref bref(buf); - if (variant_get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { + if (std::get(e2sm_response.ric_ctrl_outcome).pack(bref) != asn1::SRSASN_SUCCESS) { printf("Failed to pack E2SM RC RIC Control Outcome (Failure)\n"); } if (!e2_control_response.failure->ric_ctrl_outcome.resize(buf.length())) { diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp index 00bbc62e76..b33959893e 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_action_du_executor.cpp @@ -190,7 +190,7 @@ void e2sm_rc_control_action_2_6_du_executor::parse_action_ran_parameter_value(co bool e2sm_rc_control_action_2_6_du_executor::ric_control_action_supported(const e2sm_ric_control_request& req) { const e2sm_rc_ctrl_msg_format1_s& ctrl_msg = - variant_get(req.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format1(); + std::get(req.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format1(); for (auto& ran_p : ctrl_msg.ran_p_list) { if (action_params.find(ran_p.ran_param_id) == action_params.end()) { @@ -222,9 +222,9 @@ e2sm_rc_control_action_2_6_du_executor::convert_to_du_config_request(const e2sm_ { du_mac_sched_control_config ctrl_config = {}; const e2sm_rc_ctrl_hdr_format1_s& ctrl_hdr = - variant_get(e2sm_req_.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format1(); + std::get(e2sm_req_.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format1(); const e2sm_rc_ctrl_msg_format1_s& ctrl_msg = - variant_get(e2sm_req_.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format1(); + std::get(e2sm_req_.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format1(); switch (ctrl_hdr.ue_id.type()) { case ue_id_c::types_opts::gnb_ue_id: ctrl_config.ue_id = ctrl_hdr.ue_id.gnb_ue_id().amf_ue_ngap_id; @@ -258,7 +258,7 @@ e2sm_ric_control_response e2sm_rc_control_action_2_6_du_executor::convert_to_e2s // Always fill outcome here, it will be decided later whether it should be included in the e2 response. e2sm_response.ric_ctrl_outcome_present = true; - e2sm_rc_ctrl_outcome_format1_s& ctrl_outcome = variant_get(e2sm_response.ric_ctrl_outcome) + e2sm_rc_ctrl_outcome_format1_s& ctrl_outcome = std::get(e2sm_response.ric_ctrl_outcome) .ric_ctrl_outcome_formats.set_ctrl_outcome_format1(); // TODO: fill outcome properly diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_service_impl.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_service_impl.cpp index 84b9eccd64..21debc60ed 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_service_impl.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_control_service_impl.cpp @@ -90,8 +90,8 @@ e2sm_rc_control_service::e2sm_rc_control_service(uint32_t style_id_) : e2sm_rc_c bool e2sm_rc_control_service::control_request_supported(const e2sm_ric_control_request& req) { - const e2sm_rc_ctrl_hdr_s& ctrl_hdr = variant_get(req.request_ctrl_hdr); - const e2sm_rc_ctrl_msg_s& ctrl_msg = variant_get(req.request_ctrl_msg); + const e2sm_rc_ctrl_hdr_s& ctrl_hdr = std::get(req.request_ctrl_hdr); + const e2sm_rc_ctrl_msg_s& ctrl_msg = std::get(req.request_ctrl_msg); // All styles 1 - 10 use hdr and msg format 1 if (ctrl_hdr.ric_ctrl_hdr_formats.type().value != @@ -137,7 +137,7 @@ async_task e2sm_rc_control_service::execute_control_request(const e2sm_ric_control_request& req) { const e2sm_rc_ctrl_hdr_format1_s& ctrl_hdr = - variant_get(req.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format1(); + std::get(req.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format1(); if (config_req_executors.find(ctrl_hdr.ric_ctrl_action_id) == config_req_executors.end()) { return launch_async([](coro_context>& ctx) { @@ -165,10 +165,10 @@ e2sm_ric_control_request e2sm_rc_control_service_style_255::create_req_f1_from_r req_f1.ric_ctrl_ack_request = false; e2sm_rc_ctrl_hdr_format1_s& hdr_f1 = - variant_get(req_f1.request_ctrl_hdr).ric_ctrl_hdr_formats.set_ctrl_hdr_format1(); + std::get(req_f1.request_ctrl_hdr).ric_ctrl_hdr_formats.set_ctrl_hdr_format1(); e2sm_rc_ctrl_msg_format1_s& msg_f1 = - variant_get(req_f1.request_ctrl_msg).ric_ctrl_msg_formats.set_ctrl_msg_format1(); + std::get(req_f1.request_ctrl_msg).ric_ctrl_msg_formats.set_ctrl_msg_format1(); hdr_f1.ric_style_type = style.indicated_ctrl_style_type; hdr_f1.ric_ctrl_action_id = action.ric_ctrl_action_id; @@ -195,8 +195,8 @@ e2sm_ric_control_request e2sm_rc_control_service_style_255::create_req_f1_from_r bool e2sm_rc_control_service_style_255::control_request_supported(const e2sm_ric_control_request& req) { - const e2sm_rc_ctrl_hdr_s& ctrl_hdr = variant_get(req.request_ctrl_hdr); - const e2sm_rc_ctrl_msg_s& ctrl_msg = variant_get(req.request_ctrl_msg); + const e2sm_rc_ctrl_hdr_s& ctrl_hdr = std::get(req.request_ctrl_hdr); + const e2sm_rc_ctrl_msg_s& ctrl_msg = std::get(req.request_ctrl_msg); if (ctrl_hdr.ric_ctrl_hdr_formats.type().value != e2sm_rc_ctrl_hdr_s::ric_ctrl_hdr_formats_c_::types_opts::ctrl_hdr_format2) { @@ -249,9 +249,9 @@ e2sm_rc_control_service_style_255::execute_control_request(const e2sm_ric_contro aggregated_response.success = false; const e2sm_rc_ctrl_hdr_format2_s& ctrl_hdr_f2 = - variant_get(req.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format2(); + std::get(req.request_ctrl_hdr).ric_ctrl_hdr_formats.ctrl_hdr_format2(); const e2sm_rc_ctrl_msg_format2_s& ctrl_msg_f2 = - variant_get(req.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format2(); + std::get(req.request_ctrl_msg).ric_ctrl_msg_formats.ctrl_msg_format2(); for (auto& style : ctrl_msg_f2.ric_ctrl_style_list) { for (auto& action : style.ric_ctrl_action_list) { diff --git a/lib/e2/e2sm/e2sm_rc/e2sm_rc_impl.cpp b/lib/e2/e2sm/e2sm_rc/e2sm_rc_impl.cpp index 03a00f2be0..2bbb2a589d 100644 --- a/lib/e2/e2sm/e2sm_rc/e2sm_rc_impl.cpp +++ b/lib/e2/e2sm/e2sm_rc/e2sm_rc_impl.cpp @@ -61,7 +61,7 @@ bool e2sm_rc_impl::add_e2sm_control_service(std::unique_ptr(request.request_ctrl_hdr); + const e2sm_rc_ctrl_hdr_s& ctrl_hdr = std::get(request.request_ctrl_hdr); int64_t ric_style_type = 0; if (ctrl_hdr.ric_ctrl_hdr_formats.type().value == diff --git a/lib/f1ap/cu_cp/f1ap_asn1_converters.h b/lib/f1ap/cu_cp/f1ap_asn1_converters.h index 54919fa908..35727e6511 100644 --- a/lib/f1ap/cu_cp/f1ap_asn1_converters.h +++ b/lib/f1ap/cu_cp/f1ap_asn1_converters.h @@ -24,7 +24,6 @@ #include "../common/asn1_helpers.h" #include "srsran/adt/optional.h" -#include "srsran/adt/variant.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" @@ -32,6 +31,7 @@ #include "srsran/ran/cause/f1ap_cause.h" #include "srsran/ran/nr_cgi.h" #include +#include #include namespace srsran { @@ -71,21 +71,24 @@ inline asn1::f1ap::cause_c cause_to_asn1(f1ap_cause_t cause) { asn1::f1ap::cause_c asn1_cause; - if (variant_holds_alternative(cause)) { - asn1_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_transport() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_protocol() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_misc() = static_cast(variant_get(cause)); - } else { - report_fatal_error("Cannot convert cause to F1AP type: {}", cause); + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_radio_network() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_transport() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_protocol() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_misc() = static_cast(*result); + return asn1_cause; } + report_fatal_error("Cannot convert cause to F1AP type: {}", cause); return asn1_cause; } diff --git a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp index d7c2006815..c0679c0810 100644 --- a/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp +++ b/lib/f1ap/cu_cp/procedures/f1_setup_procedure.cpp @@ -64,7 +64,7 @@ du_setup_request srsran::srs_cu_cp::create_du_setup_request(const asn1::f1ap::f1 // GNB DU served cells list if (asn1_request->gnb_du_served_cells_list_present) { for (const auto& asn1_served_cell_item : asn1_request->gnb_du_served_cells_list) { - auto& asn1_served_cell = asn1_served_cell_item.value().gnb_du_served_cells_item(); + const auto& asn1_served_cell = asn1_served_cell_item.value().gnb_du_served_cells_item(); cu_cp_du_served_cells_item served_cell; @@ -144,7 +144,7 @@ static f1ap_message create_f1_setup_response(const asn1::f1ap::f1_setup_request_ resp->gnb_cu_rrc_version.latest_rrc_version.from_number(cu_response.gnb_cu_rrc_version); // activate all DU cells - if (cu_response.cells_to_be_activ_list.size() > 0) { + if (not cu_response.cells_to_be_activ_list.empty()) { resp->cells_to_be_activ_list_present = true; for (const auto& du_cell : cu_response.cells_to_be_activ_list) { asn1::protocol_ie_single_container_s resp_cell; @@ -202,12 +202,12 @@ void srsran::srs_cu_cp::handle_f1_setup_procedure(const asn1::f1ap::f1_setup_req f1ap_message f1ap_msg; if (not request_outcome.is_accepted()) { // Failed to setup DU case. - auto& fail_resp = variant_get(request_outcome.result); + auto& fail_resp = std::get(request_outcome.result); 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. - f1ap_msg = create_f1_setup_response(request, variant_get(request_outcome.result)); + f1ap_msg = create_f1_setup_response(request, std::get(request_outcome.result)); } // Send F1AP PDU to F1-C. diff --git a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp index ccaf5ed381..eeee8c3056 100644 --- a/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp +++ b/lib/f1ap/du/procedures/f1ap_du_setup_procedure.cpp @@ -82,7 +82,7 @@ void f1ap_du_setup_procedure::operator()(coro_contexttransaction_id = transaction.id(); // DU-global parameters. - setup_req->gnb_du_id = request.gnb_du_id; + setup_req->gnb_du_id = static_cast(request.gnb_du_id); setup_req->gnb_du_name_present = not request.gnb_du_name.empty(); if (setup_req->gnb_du_name_present) { setup_req->gnb_du_name.from_string(request.gnb_du_name); @@ -220,7 +220,7 @@ f1_setup_response_message f1ap_du_setup_procedure::create_f1_setup_result() res.success = true; // Update F1 DU Context (taking values from request). - du_ctxt.du_id = int_to_gnb_du_id(request.gnb_du_id); + du_ctxt.du_id = request.gnb_du_id; du_ctxt.gnb_du_name = request.gnb_du_name; du_ctxt.served_cells.resize(request.served_cells.size()); for (unsigned i = 0; i != du_ctxt.served_cells.size(); ++i) { diff --git a/lib/f1u/CMakeLists.txt b/lib/f1u/CMakeLists.txt index e107b2ad8d..c94f8d9b6a 100644 --- a/lib/f1u/CMakeLists.txt +++ b/lib/f1u/CMakeLists.txt @@ -20,5 +20,14 @@ add_library(srsran_f1u_cu_up cu_up/f1u_bearer_impl.cpp cu_up/f1u_bearer_factory.cpp) add_library(srsran_f1u_du du/f1u_bearer_impl.cpp du/f1u_bearer_factory.cpp) + +# Local connector add_library(srsgnb_app_f1u_connector local_connector/f1u_local_connector.cpp) target_link_libraries(srsgnb_app_f1u_connector srsran_f1u_cu_up) + +#Split connector +add_library(srsgnb_app_f1u_cu_up_split_connector cu_up/split_connector/f1u_split_connector.cpp) +target_link_libraries(srsgnb_app_f1u_cu_up_split_connector srsran_f1u_cu_up) + +add_library(srsgnb_app_f1u_du_split_connector du/split_connector/f1u_split_connector.cpp) +target_link_libraries(srsgnb_app_f1u_du_split_connector srsran_f1u_du) diff --git a/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp new file mode 100644 index 0000000000..ec26efd185 --- /dev/null +++ b/lib/f1u/cu_up/split_connector/f1u_split_connector.cpp @@ -0,0 +1,237 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/f1u/cu_up/split_connector/f1u_split_connector.h" +#include "srsran/gtpu/gtpu_tunnel_nru_factory.h" +#include "srsran/ran/lcid.h" + +using namespace srsran; +using namespace srs_cu_up; + +class srs_cu_up::gtpu_tx_udp_gw_adapter : public gtpu_tunnel_common_tx_upper_layer_notifier +{ +public: + /// \brief Interface for the GTP-U to pass PDUs to the IO gateway + /// \param sdu PDU to be transmitted. + void on_new_pdu(byte_buffer buf, const sockaddr_storage& addr) override + { + if (handler != nullptr) { + handler->handle_pdu(std::move(buf), addr); + } + } + + void connect(srs_cu_up::ngu_tnl_pdu_session& handler_) { handler = &handler_; } + + void disconnect() { handler = nullptr; } + + srs_cu_up::ngu_tnl_pdu_session* handler = nullptr; +}; + +class srs_cu_up::gtpu_rx_f1u_adapter : public srsran::gtpu_tunnel_nru_rx_lower_layer_notifier +{ +public: + /// \brief Interface for the GTP-U to pass a SDU (i.e. NR-U DL message) into the lower layer. + /// \param dl_message NR-U DL message with optional T-PDU. + void on_new_sdu(nru_dl_message dl_message) override {} + + /// \brief Interface for the GTP-U to pass a SDU (i.e. NR-U UL message) into the lower layer. + /// \param ul_message NR-U UL message with optional T-PDU. + void on_new_sdu(nru_ul_message ul_message) override + { + if (handler != nullptr) { + handler->on_new_pdu(std::move(ul_message)); + } + } + + void connect(f1u_cu_up_gateway_bearer_rx_notifier& handler_) { handler = &handler_; } + + void disconnect() { handler = nullptr; } + + f1u_cu_up_gateway_bearer_rx_notifier* handler = nullptr; +}; + +/// Adapter between Network Gateway (Data) and GTP-U demux +class srs_cu_up::network_gateway_data_gtpu_demux_adapter : public srsran::network_gateway_data_notifier_with_src_addr +{ +public: + network_gateway_data_gtpu_demux_adapter() = default; + ~network_gateway_data_gtpu_demux_adapter() override = default; + + void connect_gtpu_demux(gtpu_demux_rx_upper_layer_interface& gtpu_demux_) { gtpu_demux = >pu_demux_; } + + void on_new_pdu(byte_buffer pdu, const sockaddr_storage& src_addr) override + { + srsran_assert(gtpu_demux != nullptr, "GTP-U handler must not be nullptr"); + gtpu_demux->handle_pdu(std::move(pdu), src_addr); + } + +private: + gtpu_demux_rx_upper_layer_interface* gtpu_demux = nullptr; +}; + +f1u_split_gateway_cu_bearer::f1u_split_gateway_cu_bearer(uint32_t ue_index_, + drb_id_t drb_id, + const up_transport_layer_info& ul_tnl_info_, + f1u_cu_up_gateway_bearer_rx_notifier& cu_rx_, + ngu_tnl_pdu_session& udp_session, + task_executor& ul_exec_, + srs_cu_up::f1u_bearer_disconnector& disconnector_) : + ul_exec(ul_exec_), + ue_index(ue_index_), + logger("CU-F1-U", {ue_index_, drb_id, ul_tnl_info_}), + disconnector(disconnector_), + ul_tnl_info(ul_tnl_info_), + cu_rx(cu_rx_) +{ + gtpu_to_network_adapter = std::make_unique(); + gtpu_to_f1u_adapter = std::make_unique(); + gtpu_to_network_adapter->connect(udp_session); + gtpu_to_f1u_adapter->connect(cu_rx); +} + +f1u_split_gateway_cu_bearer::~f1u_split_gateway_cu_bearer() +{ + stop(); +} + +void f1u_split_gateway_cu_bearer::stop() +{ + if (not stopped) { + disconnector.disconnect_cu_bearer(ul_tnl_info); + } + stopped = true; +} + +f1u_split_connector::f1u_split_connector(ngu_gateway* udp_gw_, + gtpu_demux* demux_, + dlt_pcap& gtpu_pcap_, + uint16_t peer_port_) : + logger_cu(srslog::fetch_basic_logger("CU-F1-U")), + peer_port(peer_port_), + udp_gw(udp_gw_), + demux(demux_), + gtpu_pcap(gtpu_pcap_) +{ + gw_data_gtpu_demux_adapter = std::make_unique(); + udp_session = udp_gw->create(*gw_data_gtpu_demux_adapter); + gw_data_gtpu_demux_adapter->connect_gtpu_demux(*demux); +} + +f1u_split_connector::~f1u_split_connector() = default; + +std::unique_ptr +f1u_split_connector::create_cu_bearer(uint32_t ue_index, + drb_id_t drb_id, + const srs_cu_up::f1u_config& config, + const up_transport_layer_info& ul_up_tnl_info, + f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, + task_executor& ul_exec) +{ + logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); + auto cu_bearer = std::make_unique( + ue_index, drb_id, ul_up_tnl_info, rx_notifier, *udp_session, ul_exec, *this); + std::unique_lock lock(map_mutex); + srsran_assert(cu_map.find(ul_up_tnl_info) == cu_map.end(), + "Cannot create CU gateway local bearer with already existing UL GTP Tunnel={}", + ul_up_tnl_info); + cu_map.insert({ul_up_tnl_info, cu_bearer.get()}); + + // create GTP-U tunnel rx + gtpu_tunnel_nru_rx_creation_message msg{}; + msg.ue_index = int_to_ue_index(ue_index); + msg.rx_cfg.local_teid = ul_up_tnl_info.gtp_teid; + msg.rx_lower = cu_bearer->gtpu_to_f1u_adapter.get(); + + std::unique_ptr tunnel_rx = srsran::create_gtpu_tunnel_nru_rx(msg); + + // attach it to the F1-U bearer + cu_bearer->attach_tunnel_rx(std::move(tunnel_rx)); + + // attach tunnel rx to DEMUX + if (!demux->add_tunnel(ul_up_tnl_info.gtp_teid, cu_bearer->ul_exec, cu_bearer->get_tunnel_rx_interface())) { + logger_cu.error("Could not attach UL-TEID to demux RX. TEID {} already exists", ul_up_tnl_info.gtp_teid); + // continue here; but the new tunnel won't be able to rx any data because the TEID was already registered at demux + } + + return cu_bearer; +} + +void f1u_split_connector::attach_dl_teid(const up_transport_layer_info& ul_up_tnl_info, + const up_transport_layer_info& dl_up_tnl_info) +{ + // Get CU bearer + f1u_split_gateway_cu_bearer* cu_bearer = nullptr; + { + std::unique_lock lock(map_mutex); + if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + logger_cu.warning("Could not find UL GTP Tunnel at CU-CP to connect. UL GTP Tunnel={}, DL GTP Tunnel={}", + ul_up_tnl_info, + dl_up_tnl_info); + return; + } + cu_bearer = cu_map.at(ul_up_tnl_info); + } + + // create GTP-U tunnel tx + gtpu_tunnel_nru_tx_creation_message msg{}; + msg.ue_index = int_to_ue_index(cu_bearer->ue_index); + msg.tx_cfg.peer_teid = dl_up_tnl_info.gtp_teid; + msg.tx_cfg.peer_addr = dl_up_tnl_info.tp_address.to_string(); + msg.tx_cfg.peer_port = peer_port; + msg.gtpu_pcap = >pu_pcap; + msg.tx_upper = cu_bearer->gtpu_to_network_adapter.get(); + + std::unique_ptr tunnel_tx = srsran::create_gtpu_tunnel_nru_tx(msg); + + // attach it to the F1-U bearer + cu_bearer->attach_tunnel_tx(std::move(tunnel_tx)); + + logger_cu.info( + "Connected CU bearer to DU bearer. UL GTP Tunnel={}, DL GTP Tunnel={}", ul_up_tnl_info, dl_up_tnl_info); +} + +void f1u_split_connector::disconnect_cu_bearer(const up_transport_layer_info& ul_up_tnl_info) +{ + f1u_split_gateway_cu_bearer* cu_bearer = nullptr; + { + std::unique_lock lock(map_mutex); + if (cu_map.find(ul_up_tnl_info) == cu_map.end()) { + logger_cu.warning("Could not disconnect CU F1-U bearer with unknown UL GTP Tunnel={}", ul_up_tnl_info); + return; + } + cu_bearer = cu_map.at(ul_up_tnl_info); + } + + // disconnect adapters + cu_bearer->gtpu_to_network_adapter->disconnect(); + cu_bearer->gtpu_to_f1u_adapter->disconnect(); + + // Remove UL from GTP-U demux + demux->remove_tunnel(ul_up_tnl_info.gtp_teid); + + // Remove DL path + { + std::unique_lock lock(map_mutex); + cu_map.erase(ul_up_tnl_info); + } + logger_cu.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", ul_up_tnl_info); +} diff --git a/lib/f1u/du/split_connector/f1u_split_connector.cpp b/lib/f1u/du/split_connector/f1u_split_connector.cpp new file mode 100644 index 0000000000..a458434838 --- /dev/null +++ b/lib/f1u/du/split_connector/f1u_split_connector.cpp @@ -0,0 +1,96 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "srsran/f1u/du/split_connector/f1u_split_connector.h" +#include "srsran/gtpu/gtpu_tunnel_nru_factory.h" +#include "srsran/ran/lcid.h" + +using namespace srsran; +using namespace srs_du; + +std::unique_ptr +f1u_split_connector::create_du_bearer(uint32_t ue_index, + drb_id_t drb_id, + srs_du::f1u_config config, + const up_transport_layer_info& dl_up_tnl_info, + const up_transport_layer_info& ul_up_tnl_info, + srs_du::f1u_du_gateway_bearer_rx_notifier& du_rx, + timer_factory timers, + task_executor& ue_executor) +{ + logger_du.info( + "Creating DU gateway local bearer with UL GTP Tunnel={} DL GTP Tunnel={}", ul_up_tnl_info, dl_up_tnl_info); + std::unique_ptr du_bearer = std::make_unique( + ue_index, drb_id, dl_up_tnl_info, du_rx, ul_up_tnl_info, *this, gtpu_pcap, peer_port); + { + std::unique_lock lock(map_mutex); + srsran_assert(du_map.find(ul_up_tnl_info) == du_map.end(), + "Cannot create DU gateway local bearer with already existing UL GTP Tunnel={}", + ul_up_tnl_info); + + du_bearer->gtpu_to_network_adapter.connect(*udp_session); + du_map.insert({dl_up_tnl_info, du_bearer.get()}); + } + + // attach RX to DEMUX + if (!demux->add_tunnel(dl_up_tnl_info.gtp_teid, ue_executor, du_bearer->get_tunnel_rx_interface())) { + logger_du.error("Could not attach UL-TEID to demux RX. TEID {} already exists", ul_up_tnl_info.gtp_teid); + return nullptr; + } + + return du_bearer; +} + +void f1u_split_connector::remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) +{ + f1u_split_gateway_du_bearer* du_bearer = nullptr; + { + std::unique_lock lock(map_mutex); + if (du_map.find(dl_up_tnl_info) == du_map.end()) { + logger_du.warning("Could not find UL GTP Tunnel at CU-CP to disconnect", dl_up_tnl_info); + return; + } + du_bearer = du_map.at(dl_up_tnl_info); + } + // disconnect adapters + du_bearer->gtpu_to_network_adapter.disconnect(); + du_bearer->gtpu_to_f1u_adapter.disconnect(); + + // Remove UL from GTP-U demux + demux->remove_tunnel(dl_up_tnl_info.gtp_teid); + + // Remove DL path + { + std::unique_lock lock(map_mutex); + du_map.erase(dl_up_tnl_info); + } + logger_du.debug("Removed CU F1-U bearer with UL GTP Tunnel={}.", dl_up_tnl_info); +} + +expected f1u_split_connector::get_du_bind_address(gnb_du_id_t gnb_du_id) +{ + std::string ip_address; + if (not udp_session->get_bind_address(ip_address)) { + return default_error_t{}; + } + return ip_address; +} diff --git a/lib/f1u/local_connector/f1u_local_connector.cpp b/lib/f1u/local_connector/f1u_local_connector.cpp index a02995fcbc..1915ffcb84 100644 --- a/lib/f1u/local_connector/f1u_local_connector.cpp +++ b/lib/f1u/local_connector/f1u_local_connector.cpp @@ -31,9 +31,7 @@ f1u_local_connector::create_cu_bearer(uint32_t ue_i const srs_cu_up::f1u_config& config, const up_transport_layer_info& ul_up_tnl_info, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, - task_executor& ul_exec, - timer_factory ue_dl_timer_factory, - unique_timer& ue_inactivity_timer) + task_executor& ul_exec) { logger_cu.info("Creating CU gateway local bearer with UL GTP Tunnel={}", ul_up_tnl_info); std::unique_lock lock(map_mutex); @@ -99,10 +97,8 @@ void f1u_local_connector::disconnect_cu_bearer(const up_transport_layer_info& ul } cu_tun->detach_du_notifier(cu_tun->dl_tnl_info.value()); } else { - logger_cu.warning( - "Could not find DU F1-U bearer from which to disconect CU bearer, no DL-TEID info present at CU bearer. " - "UL GTP Tunnel={}", - ul_up_tnl_info); + // The DU has already removed and disconnected its F1-U bearer before the CU-UP + logger_cu.info("No associated DU F1-U bearer when disconnecting CU F1-U bearer. UL GTP Tunnel={}", ul_up_tnl_info); } // Remove DL path diff --git a/lib/fapi_adaptor/mac/CMakeLists.txt b/lib/fapi_adaptor/mac/CMakeLists.txt index 6d0e406f8a..4a4b8d35ee 100644 --- a/lib/fapi_adaptor/mac/CMakeLists.txt +++ b/lib/fapi_adaptor/mac/CMakeLists.txt @@ -32,8 +32,5 @@ set(SOURCES add_library(srsran_fapi_to_mac_translator STATIC ${SOURCES}) target_link_libraries(srsran_fapi_to_mac_translator srsvec srsran_support) -add_library(mac_fapi_adaptor STATIC mac_fapi_adaptor_impl.cpp) -target_link_libraries(mac_fapi_adaptor srsran_mac_to_fapi_translator srsran_fapi_to_mac_translator) - -add_library(mac_fapi_adaptor_factory STATIC mac_fapi_adaptor_factory_impl.cpp) -target_link_libraries(mac_fapi_adaptor_factory mac_fapi_adaptor) +add_library(srsran_mac_fapi_adaptor STATIC mac_fapi_adaptor_impl.cpp mac_fapi_adaptor_factory_impl.cpp) +target_link_libraries(srsran_mac_fapi_adaptor srsran_mac_to_fapi_translator srsran_fapi_to_mac_translator) diff --git a/lib/fapi_adaptor/phy/CMakeLists.txt b/lib/fapi_adaptor/phy/CMakeLists.txt index e22ca636ee..b5eb61a3ae 100644 --- a/lib/fapi_adaptor/phy/CMakeLists.txt +++ b/lib/fapi_adaptor/phy/CMakeLists.txt @@ -20,8 +20,8 @@ add_subdirectory(messages) -add_library(fapi_to_phy_translator STATIC fapi_to_phy_translator.cpp) -target_link_libraries(fapi_to_phy_translator +add_library(srsran_fapi_to_phy_translator STATIC fapi_to_phy_translator.cpp) +target_link_libraries(srsran_fapi_to_phy_translator srsran_fapi_phy_message_adaptors srsran_fapi_precoding_matrix_tools srsran_instrumentation) @@ -31,11 +31,8 @@ set(SOURCES phy_to_fapi_results_event_translator.cpp phy_to_fapi_time_event_translator.cpp) -add_library(phy_to_fapi_translator STATIC ${SOURCES}) -target_link_libraries(phy_to_fapi_translator srslog) +add_library(srsran_phy_to_fapi_translator STATIC ${SOURCES}) +target_link_libraries(srsran_phy_to_fapi_translator srslog) -add_library(phy_fapi_adaptor STATIC phy_fapi_adaptor_impl.cpp) -target_link_libraries(phy_fapi_adaptor fapi_to_phy_translator phy_to_fapi_translator) - -add_library(phy_fapi_adaptor_factory STATIC phy_fapi_adaptor_factory_impl.cpp) -target_link_libraries(phy_fapi_adaptor_factory phy_fapi_adaptor) +add_library(srsran_phy_fapi_adaptor STATIC phy_fapi_adaptor_impl.cpp phy_fapi_adaptor_factory_impl.cpp) +target_link_libraries(srsran_phy_fapi_adaptor srsran_fapi_to_phy_translator srsran_phy_to_fapi_translator) diff --git a/lib/fapi_adaptor/precoding_matrix_mapper.cpp b/lib/fapi_adaptor/precoding_matrix_mapper.cpp index a81c65df19..141b9f1629 100644 --- a/lib/fapi_adaptor/precoding_matrix_mapper.cpp +++ b/lib/fapi_adaptor/precoding_matrix_mapper.cpp @@ -73,9 +73,9 @@ static unsigned get_pdsch_precoding_matrix_index(unsigned offset, } if (nof_ports == 2U) { - srsran_assert(variant_holds_alternative(precoding_info.type), + srsran_assert(std::holds_alternative(precoding_info.type), "Expected PMI information"); - unsigned pmi = variant_get(precoding_info.type).pmi; + unsigned pmi = std::get(precoding_info.type).pmi; logger.debug("Two ports PDSCH precoding matrix, pmi={}, nof_layers={}", pmi, nof_layers); @@ -83,10 +83,9 @@ static unsigned get_pdsch_precoding_matrix_index(unsigned offset, } if (nof_ports == 4U) { - srsran_assert(variant_holds_alternative(precoding_info.type), + srsran_assert(std::holds_alternative(precoding_info.type), "Invalid PMI information"); - const csi_report_pmi::typeI_single_panel_4ports_mode1& report = - variant_get(precoding_info.type); + const auto& report = std::get(precoding_info.type); logger.debug("Four ports PDSCH precoding matrix, i11={}, i13={}, i2={}, nof_layers={}", report.i_1_1, diff --git a/lib/gtpu/gtpu_pdu.cpp b/lib/gtpu/gtpu_pdu.cpp index e6a50af553..5df727af9d 100644 --- a/lib/gtpu/gtpu_pdu.cpp +++ b/lib/gtpu/gtpu_pdu.cpp @@ -178,11 +178,11 @@ bool gtpu_read_ie_gtpu_peer_address(gtpu_ie_gtpu_peer_address& ie, bit_decoder& switch (length) { case 4: ie.gtpu_peer_address = gtpu_ie_gtpu_peer_address::ipv4_addr_t{}; - read_ok &= dec.unpack_bytes(variant_get(ie.gtpu_peer_address)); + read_ok &= dec.unpack_bytes(std::get(ie.gtpu_peer_address)); break; case 16: ie.gtpu_peer_address = gtpu_ie_gtpu_peer_address::ipv6_addr_t{}; - read_ok &= dec.unpack_bytes(variant_get(ie.gtpu_peer_address)); + read_ok &= dec.unpack_bytes(std::get(ie.gtpu_peer_address)); break; default: logger.warning("Failed to read IE GTP-U peer address: Invalid length={}.", length); diff --git a/lib/gtpu/gtpu_pdu.h b/lib/gtpu/gtpu_pdu.h index 432beab481..c408493a0b 100644 --- a/lib/gtpu/gtpu_pdu.h +++ b/lib/gtpu/gtpu_pdu.h @@ -193,7 +193,7 @@ struct gtpu_ie_gtpu_peer_address { using ipv6_addr_t = std::array; /// IPv4 or IPv6 Address - variant gtpu_peer_address; + std::variant gtpu_peer_address; }; /// GTP-U information element for "Private Extension". See TS 29.281 Sec. 8.6 @@ -393,12 +393,12 @@ struct formatter { auto format(const srsran::gtpu_ie_gtpu_peer_address& ie, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(ie.gtpu_peer_address)) { - auto& addr = srsran::variant_get(ie.gtpu_peer_address); + if (std::holds_alternative(ie.gtpu_peer_address)) { + auto& addr = std::get(ie.gtpu_peer_address); return format_to(ctx.out(), "peer_addr={}.{}.{}.{}", addr[0], addr[1], addr[2], addr[3]); } - if (srsran::variant_holds_alternative(ie.gtpu_peer_address)) { - auto& addr = srsran::variant_get(ie.gtpu_peer_address); + if (std::holds_alternative(ie.gtpu_peer_address)) { + auto& addr = std::get(ie.gtpu_peer_address); return format_to(ctx.out(), "peer_addr={:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}:{:x}{:x}", addr[0], diff --git a/lib/gtpu/gtpu_teid_pool_impl.h b/lib/gtpu/gtpu_teid_pool_impl.h index ce15aba58e..4297b0e156 100644 --- a/lib/gtpu/gtpu_teid_pool_impl.h +++ b/lib/gtpu/gtpu_teid_pool_impl.h @@ -33,7 +33,7 @@ namespace srsran { class gtpu_teid_pool_impl final : public gtpu_teid_pool { public: - explicit gtpu_teid_pool_impl(uint32_t max_teids_) : max_teids(max_teids_), teid_pool(max_teids_) {} + explicit gtpu_teid_pool_impl(uint32_t max_nof_teids_) : max_nof_teids(max_nof_teids_), teid_pool(max_nof_teids_) {} SRSRAN_NODISCARD expected request_teid() override { @@ -45,13 +45,13 @@ class gtpu_teid_pool_impl final : public gtpu_teid_pool } // Find a free teid - bool found = false; - uint16_t tmp = next_teid; - for (uint16_t n = 0; n < max_teids; n++) { - tmp = (next_teid + n) % max_teids; - if (not teid_pool[tmp]) { - teid_pool[tmp] = true; - found = true; + bool found = false; + uint16_t tmp_idx = next_teid_idx; + for (uint16_t n = 0; n < max_nof_teids; n++) { + tmp_idx = (next_teid_idx + n) % max_nof_teids; + if (not teid_pool[tmp_idx]) { + teid_pool[tmp_idx] = true; + found = true; break; } } @@ -59,31 +59,32 @@ class gtpu_teid_pool_impl final : public gtpu_teid_pool if (not found) { return teid; } - next_teid = (tmp + 1) % max_teids; - teid = gtpu_teid_t{tmp}; + next_teid_idx = (tmp_idx + 1) % max_nof_teids; + teid = gtpu_teid_t{tmp_idx + GTPU_TEID_MIN.value()}; nof_teids++; return teid; } SRSRAN_NODISCARD bool release_teid(gtpu_teid_t teid) override { - if (not teid_pool[teid.value()]) { + uint32_t teid_idx = teid.value() - GTPU_TEID_MIN.value(); + if (not teid_pool[teid_idx]) { // trying to free non-allocated TEID return false; } - teid_pool[teid.value()] = false; + teid_pool[teid_idx] = false; nof_teids--; return true; } - bool full() const override { return nof_teids >= max_teids; } + bool full() const override { return nof_teids >= max_nof_teids; } - uint32_t get_max_teids() override { return max_teids; } + uint32_t get_max_nof_teids() override { return max_nof_teids; } private: - uint32_t next_teid = 0; - uint32_t nof_teids = 0; - const uint32_t max_teids; + uint32_t next_teid_idx = 0; + uint32_t nof_teids = 0; + const uint32_t max_nof_teids; std::vector teid_pool; }; diff --git a/lib/gtpu/gtpu_tunnel_nru_factory.cpp b/lib/gtpu/gtpu_tunnel_nru_factory.cpp index fab9a1f013..da81284744 100644 --- a/lib/gtpu/gtpu_tunnel_nru_factory.cpp +++ b/lib/gtpu/gtpu_tunnel_nru_factory.cpp @@ -31,3 +31,15 @@ std::unique_ptr srsran::create_gtpu_tunnel_nru(gtpu_tunnel_nru_ { return std::make_unique(msg.ue_index, msg.cfg, *msg.gtpu_pcap, *msg.rx_lower, *msg.tx_upper); } + +std::unique_ptr +srsran::create_gtpu_tunnel_nru_rx(gtpu_tunnel_nru_rx_creation_message& msg) +{ + return std::make_unique(msg.ue_index, msg.rx_cfg, *msg.rx_lower); +} + +std::unique_ptr +srsran::create_gtpu_tunnel_nru_tx(gtpu_tunnel_nru_tx_creation_message& msg) +{ + return std::make_unique(msg.ue_index, msg.tx_cfg, *msg.gtpu_pcap, *msg.tx_upper); +} diff --git a/lib/gtpu/gtpu_tunnel_nru_rx_impl.h b/lib/gtpu/gtpu_tunnel_nru_rx_impl.h index 21e2313793..8e859e1179 100644 --- a/lib/gtpu/gtpu_tunnel_nru_rx_impl.h +++ b/lib/gtpu/gtpu_tunnel_nru_rx_impl.h @@ -49,9 +49,9 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx // domain-specific PDU handler void handle_pdu(gtpu_dissected_pdu&& pdu, const sockaddr_storage& src_addr) final { - gtpu_teid_t teid = pdu.hdr.teid; - variant nru_msg; - bool have_nr_ran_container = false; + gtpu_teid_t teid = pdu.hdr.teid; + std::variant nru_msg; + bool have_nr_ran_container = false; for (auto ext_hdr : pdu.hdr.ext_list) { switch (ext_hdr.extension_header_type) { case gtpu_extension_header_type::nr_ran_container: @@ -60,12 +60,12 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx switch (pdu_type) { case nru_pdu_type::dl_user_data: nru_msg = {nru_dl_user_data{}}; - have_nr_ran_container = packer.unpack(variant_get(nru_msg), ext_hdr.container); + have_nr_ran_container = packer.unpack(std::get(nru_msg), ext_hdr.container); break; case nru_pdu_type::dl_data_delivery_status: nru_msg = {nru_dl_data_delivery_status{}}; have_nr_ran_container = - packer.unpack(variant_get(nru_msg), ext_hdr.container); + packer.unpack(std::get(nru_msg), ext_hdr.container); break; default: logger.log_warning("Unsupported PDU type in NR RAN container. pdu_type={}", pdu_type); @@ -84,20 +84,18 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx } } if (!have_nr_ran_container) { - logger.log_warning("Incomplete PDU at F1-U interface: missing or invalid NR RAN container. pdu_len={} teid={}", - pdu.buf.length(), - teid); // As per TS 29.281 Sec. 5.2.2.6 the (...) NR RAN Container (...) may be transmitted in a G-PDU over the // X2-U, Xn-U and F1-U user plane interfaces (...). - return; + logger.log_info("T-PDU without NR RAN container. Assuming UL. pdu_len={} teid={}", pdu.buf.length(), teid); + nru_msg = {nru_dl_data_delivery_status{}}; // set to UL } logger.log_debug(pdu.buf.begin(), pdu.buf.end(), "RX PDU. pdu_len={}", pdu.buf.length()); - if (variant_holds_alternative(nru_msg)) { + if (std::holds_alternative(nru_msg)) { nru_dl_message dl_message = {}; dl_message.t_pdu = gtpu_extract_msg(std::move(pdu)); // header is invalidated after extraction; - dl_message.dl_user_data = std::move(variant_get(nru_msg)); + dl_message.dl_user_data = std::move(std::get(nru_msg)); logger.log_info( dl_message.t_pdu.begin(), dl_message.t_pdu.end(), "RX DL user data. t_pdu_len={}", dl_message.t_pdu.length()); @@ -105,7 +103,7 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx return; } - if (variant_holds_alternative(nru_msg)) { + if (std::holds_alternative(nru_msg)) { nru_ul_message ul_message = {}; expected buf = byte_buffer_chain::create(gtpu_extract_msg(std::move(pdu))); // header is invalidated after extraction; @@ -116,7 +114,7 @@ class gtpu_tunnel_nru_rx_impl : public gtpu_tunnel_base_rx if (!buf.value().empty()) { ul_message.t_pdu = std::move(buf.value()); } - ul_message.data_delivery_status = std::move(variant_get(nru_msg)); + ul_message.data_delivery_status = std::move(std::get(nru_msg)); if (ul_message.t_pdu.has_value()) { logger.log_info(ul_message.t_pdu.value().begin(), diff --git a/lib/gtpu/gtpu_tunnel_nru_tx_impl.h b/lib/gtpu/gtpu_tunnel_nru_tx_impl.h index 138d3f2fd8..bc0848cc4d 100644 --- a/lib/gtpu/gtpu_tunnel_nru_tx_impl.h +++ b/lib/gtpu/gtpu_tunnel_nru_tx_impl.h @@ -98,26 +98,26 @@ class gtpu_tunnel_nru_tx_impl : public gtpu_tunnel_base_tx, public gtpu_tunnel_n hdr.message_type = GTPU_MSG_DATA_PDU; hdr.length = 0; // this will be computed automatically hdr.teid = cfg.peer_teid; - hdr.next_ext_hdr_type = gtpu_extension_header_type::nr_ran_container; - - if (!ul_message.data_delivery_status.has_value()) { - logger.log_error("Dropped SDU, missing data_delivery_status. teid={}", hdr.teid); - return; - } + hdr.next_ext_hdr_type = gtpu_extension_header_type::no_more_extension_headers; byte_buffer ext_buf; - if (!packer.pack(ext_buf, ul_message.data_delivery_status.value())) { - logger.log_error("Dropped SDU, error writing NR RAN container to GTP-U extension header. teid={} ext_len={}", - hdr.teid, - ext_buf.length()); - return; - } + if (ul_message.data_delivery_status.has_value()) { + logger.log_debug("Adding data_delivery_status to PDU. teid={}", hdr.teid); + hdr.next_ext_hdr_type = gtpu_extension_header_type::nr_ran_container; + + if (!packer.pack(ext_buf, ul_message.data_delivery_status.value())) { + logger.log_error("Dropped SDU, error writing NR RAN container to GTP-U extension header. teid={} ext_len={}", + hdr.teid, + ext_buf.length()); + return; + } - gtpu_extension_header ext; - ext.extension_header_type = gtpu_extension_header_type::nr_ran_container; - ext.container = ext_buf; + gtpu_extension_header ext; + ext.extension_header_type = gtpu_extension_header_type::nr_ran_container; + ext.container = ext_buf; - hdr.ext_list.push_back(ext); + hdr.ext_list.push_back(ext); + } byte_buffer buf; if (ul_message.t_pdu.has_value()) { diff --git a/lib/gtpu/ngu_gateway.cpp b/lib/gtpu/ngu_gateway.cpp index 068bd06b76..747b3362a8 100644 --- a/lib/gtpu/ngu_gateway.cpp +++ b/lib/gtpu/ngu_gateway.cpp @@ -80,6 +80,8 @@ class udp_ngu_tnl_session final : public ngu_tnl_pdu_session data_notifier.on_new_pdu(std::move(pdu), src_addr); } + bool get_bind_address(std::string& ip_address) override { return udp_gw->get_bind_address(ip_address); } + std::optional get_bind_port() override { return udp_gw->get_bind_port(); } private: @@ -137,6 +139,8 @@ class no_core_ngu_tnl_pdu_session final : public ngu_tnl_pdu_session } std::optional get_bind_port() override { return std::nullopt; } + + bool get_bind_address(std::string& ip_address) override { return false; } }; /// Implementation of the NG-U gateway for the case a local UPF stub is used. diff --git a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp index 4d9373ed5a..29bc78e5c7 100644 --- a/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp +++ b/lib/mac/mac_dl/dl_sch_pdu_assembler.cpp @@ -29,11 +29,11 @@ using namespace srsran; -// Note: Do not attempt to build an SDU if there is not enough space for the MAC subheader, min payload size and -// potential RLC header. +/// Note: Do not attempt to build an SDU if there is not enough space for the MAC subheader, min payload size and +/// potential RLC header. static const unsigned RLC_HEADER_SIZE_ESTIM = 2; -// Minimum size required to fit a MAC subheader and SDU. +/// Minimum size required to fit a MAC subheader and SDU. static size_t min_mac_subhdr_and_sdu_space_required(lcid_t lcid) { return MIN_MAC_SDU_SUBHEADER_SIZE + 1 + (lcid != LCID_SRB0 ? RLC_HEADER_SIZE_ESTIM : 0); @@ -442,10 +442,10 @@ void dl_sch_pdu_assembler::assemble_ce(dl_sch_pdu& ue_pdu, pdu_logger.add_conres_id(conres); } break; case lcid_dl_sch_t::TA_CMD: { - srsran_assert(variant_holds_alternative(subpdu.ce_payload) == true, + srsran_assert(std::holds_alternative(subpdu.ce_payload) == true, "Invalid MAC CE payload for lcid={}", subpdu.lcid.value()); - const auto ce_payload = variant_get(subpdu.ce_payload); + const auto ce_payload = std::get(subpdu.ce_payload); ue_pdu.add_tag_cmd(ce_payload); pdu_logger.add_ta_cmd(ce_payload); } break; diff --git a/lib/mac/mac_sched/uci_cell_decoder.cpp b/lib/mac/mac_sched/uci_cell_decoder.cpp index 828c81321b..7ea358ef8b 100644 --- a/lib/mac/mac_sched/uci_cell_decoder.cpp +++ b/lib/mac/mac_sched/uci_cell_decoder.cpp @@ -94,18 +94,16 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms continue; } - if (variant_holds_alternative(mac_uci.pdu)) { - const auto& pucch = variant_get(mac_uci.pdu); - + if (const auto* pucch_f0f1 = std::get_if(&mac_uci.pdu)) { uci_indication::uci_pdu::uci_pucch_f0_or_f1_pdu pdu{}; - pdu.ul_sinr_dB = pucch.ul_sinr_dB; - pdu.time_advance_offset = pucch.time_advance_offset; + pdu.ul_sinr_dB = pucch_f0f1->ul_sinr_dB; + pdu.time_advance_offset = pucch_f0f1->time_advance_offset; pdu.sr_detected = false; - if (pucch.sr_info.has_value()) { - pdu.sr_detected = pucch.sr_info.value().detected; + if (pucch_f0f1->sr_info.has_value()) { + pdu.sr_detected = pucch_f0f1->sr_info.value().detected; } - if (pucch.harq_info.has_value()) { + if (pucch_f0f1->harq_info.has_value()) { // NOTES: // - We report to the scheduler only the UCI HARQ-ACKs that contain either an ACK or NACK; we ignore the // UCIs with DTX. In that case, the scheduler will not receive the notification and the HARQ will eventually @@ -122,7 +120,7 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms // expected 2-bit reporting. To prevent this, we assume that PUCCH Format 0 or 1 UCI is valid if none of the 1 // or 2 bits report is DTX (not detected). - const auto& harq_pdus = pucch.harq_info.value().harqs; + const auto& harq_pdus = pucch_f0f1->harq_info.value().harqs; pdu.harqs.resize(harq_pdus.size()); for (unsigned i = 0, e = pdu.harqs.size(); i != e; ++i) { switch (harq_pdus[i]) { @@ -141,28 +139,27 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms } } uci_pdu.pdu.emplace(pdu); - } else if (variant_holds_alternative(mac_uci.pdu)) { - const auto& pusch = variant_get(mac_uci.pdu); - auto& pdu = uci_pdu.pdu.emplace(); - if (pusch.harq_info.has_value()) { + } else if (const auto* pusch = std::get_if(&mac_uci.pdu)) { + auto& pdu = uci_pdu.pdu.emplace(); + if (pusch->harq_info.has_value()) { pdu.harqs = - convert_mac_harq_bits_to_sched_harq_values(pusch.harq_info.value().is_valid, pusch.harq_info->payload); + convert_mac_harq_bits_to_sched_harq_values(pusch->harq_info.value().is_valid, pusch->harq_info->payload); // Report ACK for RLF detection purposes. - for (unsigned i = 0, e = pdu.harqs.size(); i != e; ++i) { - rlf_handler.handle_ack(uci_pdu.ue_index, cell_index, pdu.harqs[i] == mac_harq_ack_report_status::ack); + for (auto harq : pdu.harqs) { + rlf_handler.handle_ack(uci_pdu.ue_index, cell_index, harq == mac_harq_ack_report_status::ack); } } - if (pusch.csi_part1_info.has_value()) { - if (pusch.csi_part1_info->is_valid) { + if (pusch->csi_part1_info.has_value()) { + if (pusch->csi_part1_info->is_valid) { // Decode CSI bits given the CSI report config previously stored in the grid. const auto& slot_ucis = expected_uci_report_grid[to_grid_index(msg.sl_rx)]; // Search for CSI report config with matching RNTI. for (const auto& expected_slot_uci : slot_ucis) { if (expected_slot_uci.rnti == uci_pdu.crnti) { - pdu.csi = decode_csi_bits(pusch, expected_slot_uci.csi_rep_cfg); + pdu.csi = decode_csi_bits(*pusch, expected_slot_uci.csi_rep_cfg); break; } } @@ -176,23 +173,22 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms // NOTE: The RLF detection based on CSI is used when the UE only transmits PUCCHs; if the UE transmit PUSCHs, // the RLF detection will be based on the PUSCH CRC. However, if the PUSCH UCI has a correctly decoded CSI, we // need to reset the CSI KOs counter. - if (pusch.csi_part1_info->is_valid) { + if (pusch->csi_part1_info->is_valid) { rlf_handler.handle_csi(uci_pdu.ue_index, cell_index, true); } } - } else if (variant_holds_alternative(mac_uci.pdu)) { - const auto& pucch = variant_get(mac_uci.pdu); - auto& pdu = uci_pdu.pdu.emplace(); + } else if (const auto* pucch_f2f3f4 = std::get_if(&mac_uci.pdu)) { + auto& pdu = uci_pdu.pdu.emplace(); - pdu.ul_sinr_dB = pucch.ul_sinr_dB; - pdu.time_advance_offset = pucch.time_advance_offset; - if (pucch.sr_info.has_value()) { - pdu.sr_info = pucch.sr_info.value(); + pdu.ul_sinr_dB = pucch_f2f3f4->ul_sinr_dB; + pdu.time_advance_offset = pucch_f2f3f4->time_advance_offset; + if (pucch_f2f3f4->sr_info.has_value()) { + pdu.sr_info = pucch_f2f3f4->sr_info.value(); } - if (pucch.harq_info.has_value()) { - pdu.harqs = - convert_mac_harq_bits_to_sched_harq_values(pucch.harq_info.value().is_valid, pucch.harq_info->payload); + if (pucch_f2f3f4->harq_info.has_value()) { + pdu.harqs = convert_mac_harq_bits_to_sched_harq_values(pucch_f2f3f4->harq_info.value().is_valid, + pucch_f2f3f4->harq_info->payload); // Report ACK for RLF detection purposes. for (const mac_harq_ack_report_status& harq_st : pdu.harqs) { @@ -201,15 +197,15 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms } // Check if the UCI has been correctly decoded. - if (pucch.csi_part1_info.has_value()) { - if (pucch.csi_part1_info->is_valid) { + if (pucch_f2f3f4->csi_part1_info.has_value()) { + if (pucch_f2f3f4->csi_part1_info->is_valid) { // Decode CSI bits given the CSI report config previously stored in the grid. const auto& slot_ucis = expected_uci_report_grid[to_grid_index(msg.sl_rx)]; // Search for CSI report config with matching RNTI. for (const auto& expected_slot_uci : slot_ucis) { if (expected_slot_uci.rnti == uci_pdu.crnti) { - pdu.csi = decode_csi_bits(pucch, expected_slot_uci.csi_rep_cfg); + pdu.csi = decode_csi_bits(*pucch_f2f3f4, expected_slot_uci.csi_rep_cfg); break; } } @@ -221,7 +217,7 @@ uci_indication uci_cell_decoder::decode_uci(const mac_uci_indication_message& ms } } // We consider any status other than "crc_pass" as non-decoded CSI. - rlf_handler.handle_csi(uci_pdu.ue_index, cell_index, pucch.csi_part1_info->is_valid); + rlf_handler.handle_csi(uci_pdu.ue_index, cell_index, pucch_f2f3f4->csi_part1_info->is_valid); } } } diff --git a/lib/ngap/CMakeLists.txt b/lib/ngap/CMakeLists.txt index 59505a1400..34fd1cc1f1 100644 --- a/lib/ngap/CMakeLists.txt +++ b/lib/ngap/CMakeLists.txt @@ -25,6 +25,7 @@ set(SOURCES ngap_factory.cpp ngap_impl.cpp ngap_validators/ngap_validators.cpp + gateways/n2_connection_client_factory.cpp procedures/ng_setup_procedure.cpp procedures/ng_reset_procedure.cpp procedures/ngap_initial_context_setup_procedure.cpp diff --git a/apps/gnb/adapters/ngap_adapter.cpp b/lib/ngap/gateways/n2_connection_client_factory.cpp similarity index 93% rename from apps/gnb/adapters/ngap_adapter.cpp rename to lib/ngap/gateways/n2_connection_client_factory.cpp index d50ec164b4..4a5777c438 100644 --- a/apps/gnb/adapters/ngap_adapter.cpp +++ b/lib/ngap/gateways/n2_connection_client_factory.cpp @@ -20,11 +20,13 @@ * */ -#include "ngap_adapter.h" +#include "srsran/ngap/gateways/n2_connection_client_factory.h" +#include "../ngap_asn1_packer.h" #include "srsran/asn1/ngap/common.h" #include "srsran/asn1/ngap/ngap_pdu_contents.h" #include "srsran/gateways/sctp_network_gateway_factory.h" #include "srsran/ngap/ngap_message.h" +#include "srsran/pcap/dlt_pcap.h" using namespace srsran; using namespace srs_cu_cp; @@ -32,7 +34,7 @@ using namespace srs_cu_cp; namespace { /// Stub for the operation of the CU-CP without a core. -class ngap_gateway_local_stub final : public ngap_gateway_connector +class ngap_gateway_local_stub final : public n2_connection_client { public: ngap_gateway_local_stub(dlt_pcap& pcap_) : pcap_writer(pcap_) {} @@ -123,7 +125,7 @@ class ngap_gateway_local_stub final : public ngap_gateway_connector }; /// \brief NGAP bridge that uses the IO broker to handle the SCTP connection -class ngap_sctp_gateway_adapter : public ngap_gateway_connector, +class ngap_sctp_gateway_adapter : public n2_connection_client, public sctp_network_gateway_control_notifier, public network_gateway_data_notifier { @@ -215,14 +217,15 @@ class ngap_sctp_gateway_adapter : public ngap_gateway_connector, } // namespace -std::unique_ptr srsran::srs_cu_cp::create_ngap_gateway(const ngap_gateway_params& params) +std::unique_ptr +srsran::srs_cu_cp::create_n2_connection_client(const n2_connection_client_config& params) { - if (variant_holds_alternative(params.mode)) { + if (std::holds_alternative(params.mode)) { // Connection to local AMF stub. return std::make_unique(params.pcap); } // Connection to AMF through SCTP. - const auto& nw_mode = variant_get(params.mode); + const auto& nw_mode = std::get(params.mode); return std::make_unique(nw_mode.broker, nw_mode.sctp, params.pcap); } diff --git a/lib/ngap/ngap_asn1_converters.h b/lib/ngap/ngap_asn1_converters.h index b21c7e7d36..4489603fd1 100644 --- a/lib/ngap/ngap_asn1_converters.h +++ b/lib/ngap/ngap_asn1_converters.h @@ -23,7 +23,6 @@ #pragma once #include "ngap_asn1_utils.h" -#include "srsran/adt/variant.h" #include "srsran/asn1/ngap/ngap_ies.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/ngap/ngap_handover.h" @@ -33,6 +32,7 @@ #include "srsran/ran/lcid.h" #include "srsran/ran/up_transport_layer_info.h" #include "srsran/srslog/srslog.h" +#include namespace srsran { namespace srs_cu_cp { @@ -129,23 +129,28 @@ inline asn1::ngap::cause_c cause_to_asn1(ngap_cause_t cause) { asn1::ngap::cause_c asn1_cause; - if (variant_holds_alternative(cause)) { - asn1_cause.set_radio_network() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_transport() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_nas() = static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_protocol() = - static_cast(variant_get(cause)); - } else if (variant_holds_alternative(cause)) { - asn1_cause.set_misc() = static_cast(variant_get(cause)); - } else { - report_fatal_error("Cannot convert cause to NGAP type:{}", cause); + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_radio_network() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_transport() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_nas() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_protocol() = static_cast(*result); + return asn1_cause; + } + if (const auto* result = std::get_if(&cause)) { + asn1_cause.set_misc() = static_cast(*result); + return asn1_cause; } + report_fatal_error("Cannot convert cause to NGAP type:{}", cause); return asn1_cause; } diff --git a/lib/ngap/ngap_asn1_helpers.h b/lib/ngap/ngap_asn1_helpers.h index 96a9ca613f..d444756d8e 100644 --- a/lib/ngap/ngap_asn1_helpers.h +++ b/lib/ngap/ngap_asn1_helpers.h @@ -196,9 +196,10 @@ inline void fill_asn1_initial_ue_message(asn1::ngap::init_ue_msg_s& asn1_ms asn1_msg->ue_context_request.value = asn1::ngap::ue_context_request_opts::options::requested; if (msg.five_g_s_tmsi.has_value()) { + // TS 23.003 - 5G-S-TMSI contains AMF Set ID, AMF Pointer and 5G TMSI. asn1_msg->five_g_s_tmsi_present = true; - asn1_msg->five_g_s_tmsi.amf_set_id.from_number(context.current_guami.amf_set_id); - asn1_msg->five_g_s_tmsi.amf_pointer.from_number(context.current_guami.amf_pointer); + asn1_msg->five_g_s_tmsi.amf_set_id.from_number(msg.five_g_s_tmsi.value().amf_set_id); + asn1_msg->five_g_s_tmsi.amf_pointer.from_number(msg.five_g_s_tmsi.value().amf_pointer); asn1_msg->five_g_s_tmsi.five_g_tmsi.from_number(msg.five_g_s_tmsi.value().five_g_tmsi); } diff --git a/lib/ngap/ngap_context.h b/lib/ngap/ngap_context.h index 00d0f29fd7..0bd7cf048b 100644 --- a/lib/ngap/ngap_context.h +++ b/lib/ngap/ngap_context.h @@ -37,7 +37,6 @@ struct ngap_context_t { std::string plmn; /// Full PLMN as string (without possible filler digit) e.g. "00101" unsigned tac; std::vector served_guami_list; - guami_t current_guami; std::chrono::seconds pdu_session_setup_timeout; // timeout for PDU context setup in seconds }; diff --git a/lib/ngap/ngap_impl.cpp b/lib/ngap/ngap_impl.cpp index c0428c9fcd..dddbd75951 100644 --- a/lib/ngap/ngap_impl.cpp +++ b/lib/ngap/ngap_impl.cpp @@ -395,9 +395,6 @@ void ngap_impl::handle_initial_context_setup_request(const asn1::ngap::init_cont return; } - // Store guami - context.current_guami = init_ctxt_setup_req.guami; - // Store UE Aggregate Maximum Bitrate if (init_ctxt_setup_req.ue_aggr_max_bit_rate.has_value()) { ue_ctxt.aggregate_maximum_bit_rate_dl = init_ctxt_setup_req.ue_aggr_max_bit_rate.value().ue_aggr_max_bit_rate_dl; diff --git a/lib/ngap/procedures/ng_setup_procedure.cpp b/lib/ngap/procedures/ng_setup_procedure.cpp index 9212d6fe13..81263a337f 100644 --- a/lib/ngap/procedures/ng_setup_procedure.cpp +++ b/lib/ngap/procedures/ng_setup_procedure.cpp @@ -22,9 +22,9 @@ #include "ng_setup_procedure.h" #include "../ngap_asn1_helpers.h" -#include "srsran/adt/variant.h" #include "srsran/ngap/ngap_setup.h" #include "srsran/support/async/async_timer.h" +#include using namespace srsran; using namespace srsran::srs_cu_cp; @@ -120,7 +120,7 @@ ngap_ng_setup_result ng_setup_procedure::create_ng_setup_result() fill_ngap_ng_setup_result(res, transaction_sink.response()); - for (const auto& guami_item : variant_get(res).served_guami_list) { + for (const auto& guami_item : std::get(res).served_guami_list) { context.served_guami_list.push_back(guami_item.guami); } diff --git a/lib/ofh/compression/CMakeLists.txt b/lib/ofh/compression/CMakeLists.txt index 23fac660a7..6d87059789 100644 --- a/lib/ofh/compression/CMakeLists.txt +++ b/lib/ofh/compression/CMakeLists.txt @@ -42,10 +42,10 @@ if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") COMPILE_OPTIONS "-mavx512f;-mavx512bw;-mavx512vl;-mavx512dq;") endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "x86_64") -if (HAVE_NEON) +if (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") list(APPEND SOURCES iq_compression_bfp_neon.cpp) list(APPEND SOURCES iq_compression_none_neon.cpp) -endif (HAVE_NEON) +endif (${CMAKE_SYSTEM_PROCESSOR} MATCHES "aarch64") add_library(srsran_ofh_compression STATIC ${SOURCES}) target_link_libraries(srsran_ofh_compression srsvec) diff --git a/lib/ofh/compression/compression_factory.cpp b/lib/ofh/compression/compression_factory.cpp index 64b0abeb79..4d6cb17cc7 100644 --- a/lib/ofh/compression/compression_factory.cpp +++ b/lib/ofh/compression/compression_factory.cpp @@ -36,10 +36,10 @@ #include "iq_compression_none_avx512.h" #endif -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include "iq_compression_bfp_neon.h" #include "iq_compression_none_neon.h" -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; using namespace ofh; @@ -64,11 +64,11 @@ std::unique_ptr srsran::ofh::create_iq_compressor(compression_typ } } #endif -#ifdef HAVE_NEON +#ifdef __ARM_NEON if ((impl_type == "neon") || (impl_type == "auto")) { return std::make_unique(logger, iq_scaling); } -#endif // HAVE_NEON +#endif // __ARM_NEON return std::make_unique(logger, iq_scaling); case compression_type::BFP: #ifdef __x86_64__ @@ -86,11 +86,11 @@ std::unique_ptr srsran::ofh::create_iq_compressor(compression_typ } } #endif -#ifdef HAVE_NEON +#ifdef __ARM_NEON if ((impl_type == "neon") || (impl_type == "auto")) { return std::make_unique(logger, iq_scaling); } -#endif // HAVE_NEON +#endif // __ARM_NEON return std::make_unique(logger, iq_scaling); case compression_type::block_scaling: return std::make_unique(); @@ -125,11 +125,11 @@ srsran::ofh::create_iq_decompressor(compression_type type, srslog::basic_logger& } } #endif -#ifdef HAVE_NEON +#ifdef __ARM_NEON if ((impl_type == "neon") || (impl_type == "auto")) { return std::make_unique(logger); } -#endif // HAVE_NEON +#endif // __ARM_NEON return std::make_unique(logger); case compression_type::BFP: #ifdef __x86_64__ @@ -145,11 +145,11 @@ srsran::ofh::create_iq_decompressor(compression_type type, srslog::basic_logger& } } #endif -#ifdef HAVE_NEON +#ifdef __ARM_NEON if ((impl_type == "neon") || (impl_type == "auto")) { return std::make_unique(logger); } -#endif // HAVE_NEON +#endif // __ARM_NEON return std::make_unique(logger); case compression_type::block_scaling: return std::make_unique(); diff --git a/lib/ofh/ethernet/CMakeLists.txt b/lib/ofh/ethernet/CMakeLists.txt index c43c466835..d44e6e78d3 100644 --- a/lib/ofh/ethernet/CMakeLists.txt +++ b/lib/ofh/ethernet/CMakeLists.txt @@ -22,6 +22,7 @@ set(SOURCES ethernet_factories.cpp ethernet_transmitter_impl.cpp ethernet_receiver_impl.cpp + ethernet_rx_buffer_impl.cpp vlan_ethernet_frame_builder_impl.cpp vlan_ethernet_frame_decoder_impl.cpp) diff --git a/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp b/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp index 9368e0d6dd..d6bd0609d3 100644 --- a/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp +++ b/lib/ofh/ethernet/dpdk/dpdk_ethernet_receiver.cpp @@ -21,6 +21,7 @@ */ #include "dpdk_ethernet_receiver.h" +#include "dpdk_ethernet_rx_buffer_impl.h" #include "srsran/instrumentation/traces/ofh_traces.h" #include "srsran/ofh/ethernet/ethernet_frame_notifier.h" #include "srsran/support/executors/task_executor.h" @@ -36,7 +37,7 @@ namespace { class dummy_frame_notifier : public frame_notifier { // See interface for documentation. - void on_new_frame(span payload) override {} + void on_new_frame(ether::unique_rx_buffer buffer) override {} }; } // namespace @@ -114,25 +115,10 @@ void dpdk_receiver_impl::receive() std::this_thread::sleep_for(std::chrono::microseconds(5)); return; } - ofh_tracer << trace_event("ofh_dpdk_rx", dpdk_rx_tp); for (auto* mbuf : span<::rte_mbuf*>(mbufs.data(), num_frames)) { - std::array buffer; - - trace_point tp = ofh_tracer.now(); - trace_point dpdk_memcpy_tp = ofh_tracer.now(); ::rte_vlan_strip(mbuf); - ::rte_ether_hdr* eth = rte_pktmbuf_mtod(mbuf, ::rte_ether_hdr*); - unsigned length = mbuf->pkt_len; - - std::memcpy(buffer.data(), eth, length); - ofh_tracer << trace_event("ofh_dpdk_memcpy", dpdk_memcpy_tp); - - trace_point dpdk_free_tp = ofh_tracer.now(); - ::rte_pktmbuf_free(mbuf); - ofh_tracer << trace_event("ofh_dpdk_free_mbufs", dpdk_free_tp); - - notifier.get().on_new_frame(span(buffer.data(), length)); - ofh_tracer << trace_event("ofh_receiver", tp); + notifier.get().on_new_frame(unique_rx_buffer(dpdk_rx_buffer_impl(mbuf))); } + ofh_tracer << trace_event("ofh_dpdk_rx", dpdk_rx_tp); } diff --git a/lib/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer_impl.h b/lib/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer_impl.h new file mode 100644 index 0000000000..fdcb1b0606 --- /dev/null +++ b/lib/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer_impl.h @@ -0,0 +1,78 @@ +/* + * + * 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/ofh/ethernet/ethernet_unique_buffer.h" +#include "srsran/support/srsran_assert.h" +#include + +namespace srsran { +namespace ether { + +/// Receive buffer wrapper dedicated for DPDK Ethernet receiver. +class dpdk_rx_buffer_impl : public rx_buffer +{ +public: + explicit dpdk_rx_buffer_impl(::rte_mbuf* mbuf_) : mbuf(mbuf_) + { + srsran_assert(mbuf, "Invalid DPDK mbuf was passed to dpdk_rx_buffer_impl"); + } + + /// Copy constructor is deleted. + dpdk_rx_buffer_impl(dpdk_rx_buffer_impl& /**/) = delete; + + /// Move constructor. + dpdk_rx_buffer_impl(dpdk_rx_buffer_impl&& other) noexcept + { + mbuf = other.mbuf; + other.mbuf = nullptr; + }; + + /// Move assigment operator. + dpdk_rx_buffer_impl& operator=(dpdk_rx_buffer_impl&& other) noexcept + { + // Free stored mbuf. + ::rte_pktmbuf_free(mbuf); + + mbuf = other.mbuf; + other.mbuf = nullptr; + return *this; + } + + ~dpdk_rx_buffer_impl() { ::rte_pktmbuf_free(mbuf); } + + // See interface for documentation. + span data() const override + { + srsran_assert(mbuf != nullptr, "Invalid dpdk_rx_buffer_impl accessed"); + + ::uint8_t* eth_data = rte_pktmbuf_mtod(mbuf, uint8_t*); + return span(eth_data, mbuf->pkt_len); + } + +private: + ::rte_mbuf* mbuf; +}; + +} // namespace ether +} // namespace srsran diff --git a/lib/ofh/ethernet/ethernet_receiver_impl.cpp b/lib/ofh/ethernet/ethernet_receiver_impl.cpp index d03ce2bc83..3253bf11c1 100644 --- a/lib/ofh/ethernet/ethernet_receiver_impl.cpp +++ b/lib/ofh/ethernet/ethernet_receiver_impl.cpp @@ -21,6 +21,7 @@ */ #include "ethernet_receiver_impl.h" +#include "ethernet_rx_buffer_impl.h" #include "srsran/instrumentation/traces/ofh_traces.h" #include "srsran/ofh/ethernet/ethernet_frame_notifier.h" #include "srsran/ofh/ethernet/ethernet_properties.h" @@ -42,7 +43,7 @@ namespace { class dummy_frame_notifier : public frame_notifier { // See interface for documentation. - void on_new_frame(span payload) override {} + void on_new_frame(ether::unique_rx_buffer buffer) override {} }; } // namespace @@ -55,7 +56,7 @@ receiver_impl::receiver_impl(const std::string& interface, bool is_promiscuous_mode_enabled, task_executor& executor_, srslog::basic_logger& logger_) : - logger(logger_), executor(executor_), notifier(dummy_notifier) + logger(logger_), executor(executor_), notifier(dummy_notifier), buffer_pool(BUFFER_SIZE) { socket_fd = ::socket(AF_PACKET, SOCK_RAW, htons(ECPRI_ETH_TYPE)); if (socket_fd < 0) { @@ -163,16 +164,24 @@ void receiver_impl::receive() return; } - static constexpr unsigned BUFFER_SIZE = 9600; - trace_point tp = ofh_tracer.now(); - std::array buffer; - auto nof_bytes = ::recvfrom(socket_fd, buffer.data(), BUFFER_SIZE, 0, nullptr, nullptr); + trace_point tp = ofh_tracer.now(); + + auto exp_buffer = buffer_pool.reserve(); + + if (exp_buffer.is_error()) { + logger.warning("No buffer is available for receiving an Ethernet packet"); + return; + } + ethernet_rx_buffer_impl buffer = std::move(exp_buffer.value()); + span data_span = buffer.storage(); + auto nof_bytes = ::recvfrom(socket_fd, data_span.data(), BUFFER_SIZE, 0, nullptr, nullptr); if (nof_bytes < 0) { logger.warning("Ethernet receiver call to recvfrom failed"); return; } + buffer.resize(nof_bytes); - notifier.get().on_new_frame(span(buffer.data(), nof_bytes)); + notifier.get().on_new_frame(unique_rx_buffer(std::move(buffer))); ofh_tracer << trace_event("ofh_receiver", tp); } diff --git a/lib/ofh/ethernet/ethernet_receiver_impl.h b/lib/ofh/ethernet/ethernet_receiver_impl.h index d4de7db10e..18baccc953 100644 --- a/lib/ofh/ethernet/ethernet_receiver_impl.h +++ b/lib/ofh/ethernet/ethernet_receiver_impl.h @@ -22,6 +22,7 @@ #pragma once +#include "ethernet_rx_buffer_pool.h" #include "srsran/ofh/ethernet/ethernet_receiver.h" #include "srsran/srslog/logger.h" @@ -34,6 +35,8 @@ namespace ether { /// Implementation for the Ethernet receiver. class receiver_impl : public receiver { + static constexpr unsigned BUFFER_SIZE = 9600; + enum class receiver_status { idle, running, stop_requested, stopped }; public: @@ -64,6 +67,7 @@ class receiver_impl : public receiver std::reference_wrapper notifier; int socket_fd = -1; std::atomic rx_status{receiver_status::idle}; + ethernet_rx_buffer_pool buffer_pool; }; } // namespace ether diff --git a/lib/ofh/ethernet/ethernet_rx_buffer_impl.cpp b/lib/ofh/ethernet/ethernet_rx_buffer_impl.cpp new file mode 100644 index 0000000000..fda4b05c26 --- /dev/null +++ b/lib/ofh/ethernet/ethernet_rx_buffer_impl.cpp @@ -0,0 +1,68 @@ +/* + * + * 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 "ethernet_rx_buffer_impl.h" +#include "ethernet_rx_buffer_pool.h" + +using namespace srsran; +using namespace ether; + +ethernet_rx_buffer_impl::ethernet_rx_buffer_impl(ethernet_rx_buffer_pool& pool_, unsigned id_) : pool(pool_), id(id_) +{ + size = pool.get_data(id).size(); +} + +ethernet_rx_buffer_impl::ethernet_rx_buffer_impl(ethernet_rx_buffer_impl&& other) noexcept : pool(other.pool) +{ + id = other.id; + size = other.size; + other.id = -1; + other.size = 0; +} + +span ethernet_rx_buffer_impl::data() const +{ + srsran_assert(id >= 0, "Invalid Ethernet rx buffer accessed"); + return pool.get_data(id).first(size); +} + +span ethernet_rx_buffer_impl::storage() +{ + srsran_assert(id >= 0, "Invalid Ethernet rx buffer accessed"); + return pool.get_data(id); +} + +void ethernet_rx_buffer_impl::resize(unsigned used_size) +{ + srsran_assert(id >= 0, "Invalid Ethernet rx buffer accessed"); + srsran_assert(used_size <= pool.get_data(id).size(), + "The size of buffer can not be bigger than the allocated size of {} bytes", + pool.get_data(id).size()); + size = used_size; +} + +ethernet_rx_buffer_impl::~ethernet_rx_buffer_impl() +{ + if (id >= 0) { + pool.free(id); + } +} diff --git a/lib/ofh/ethernet/ethernet_rx_buffer_impl.h b/lib/ofh/ethernet/ethernet_rx_buffer_impl.h new file mode 100644 index 0000000000..385bdd87b5 --- /dev/null +++ b/lib/ofh/ethernet/ethernet_rx_buffer_impl.h @@ -0,0 +1,70 @@ +/* + * + * 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/ofh/ethernet/ethernet_unique_buffer.h" +#include "srsran/support/srsran_assert.h" + +namespace srsran { +namespace ether { + +class ethernet_rx_buffer_pool; + +/// Receive buffer wrapper dedicated for socket-based Ethernet receiver. +class ethernet_rx_buffer_impl : public rx_buffer +{ +public: + /// Constructor receives the reference to the pool, from which one buffer is requested. + ethernet_rx_buffer_impl(ethernet_rx_buffer_pool& pool_, unsigned id); + + /// Destructor frees this buffer in the pool. + ~ethernet_rx_buffer_impl(); + + /// Copy constructor is deleted. + ethernet_rx_buffer_impl(ethernet_rx_buffer_impl& /**/) = delete; + + /// Move assigment operator. + ethernet_rx_buffer_impl& operator=(ethernet_rx_buffer_impl&& other) = delete; + + /// Move constructor. + ethernet_rx_buffer_impl(ethernet_rx_buffer_impl&& other) noexcept; + + // See interface for documentation. + span data() const override; + + /// Returns span of bytes for writing. + span storage(); + + /// Sets the actually used size. + void resize(unsigned used_size); + +private: + ethernet_rx_buffer_pool& pool; + int id = -1; + /// Using size instead of a span here to save space and to enable using this class inside a \c unique_task + /// implementation exploiting small buffer vtable. + unsigned size = 0; +}; + +} // namespace ether +} // namespace srsran diff --git a/lib/ofh/ethernet/ethernet_rx_buffer_pool.h b/lib/ofh/ethernet/ethernet_rx_buffer_pool.h new file mode 100644 index 0000000000..40150fa75b --- /dev/null +++ b/lib/ofh/ethernet/ethernet_rx_buffer_pool.h @@ -0,0 +1,94 @@ +/* + * + * 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 "ethernet_rx_buffer_impl.h" +#include "srsran/adt/expected.h" +#include "srsran/adt/span.h" +#include "srsran/adt/spsc_queue.h" +#include "srsran/support/math_utils.h" +#include "srsran/support/srsran_assert.h" +#include "srsran/support/units.h" +#include + +namespace srsran { +namespace ether { + +/// Pool of buffers accessed by a socket-based Ethernet receiver. +class ethernet_rx_buffer_pool +{ + /// Allocate 2MB of storage, evenly divided between Ethernet receive buffers. + static inline constexpr units::bytes ETH_BUFFER_POOL_SIZE{2048000}; + + using rx_buffer_id_list = + concurrent_queue; + + unsigned number_of_buffers; + + std::vector> entries; + rx_buffer_id_list free_list; + +public: + /// Constructor allocates buffers with given size, assigns an ID to each of them and puts them into a list of free + /// buffers. + explicit ethernet_rx_buffer_pool(unsigned buffer_size) : + number_of_buffers(divide_ceil(ETH_BUFFER_POOL_SIZE.value(), buffer_size)), + entries(number_of_buffers), + free_list(number_of_buffers) + { + for (unsigned i = 0; i != number_of_buffers; ++i) { + entries[i].resize(buffer_size); + while (!free_list.try_push(i)) { + } + } + } + + /// Tries to get an ID of a free buffer from the pre-allocated storage. + expected reserve() + { + auto buffer_id = free_list.try_pop(); + if (!buffer_id.has_value()) { + return default_error_t{}; + } + + return {{*this, buffer_id.value()}}; + } + + /// Marks the buffer with given ID as free. + void free(unsigned buffer_id) + { + // Push identifier back in the pool. + while (!free_list.try_push(buffer_id)) { + } + } + + /// Returns the span of data belonging to a buffer with the given ID. + span get_data(unsigned id) + { + srsran_assert(id < entries.size(), "Buffer index ({}) is out of range ({}).", id, entries.size()); + return {entries[id].data(), entries[id].size()}; + } +}; + +} // namespace ether +} // namespace srsran diff --git a/lib/ofh/ofh_factories.cpp b/lib/ofh/ofh_factories.cpp index 6752d492de..713f0dd073 100644 --- a/lib/ofh/ofh_factories.cpp +++ b/lib/ofh/ofh_factories.cpp @@ -137,7 +137,7 @@ static std::pair, std::unique_ptr> eth_gateway, std::optional> eth_receiver, - task_executor& rx_executor, + task_executor& eth_rx_executor, srslog::basic_logger& logger) { if (eth_gateway && eth_receiver) { @@ -146,10 +146,10 @@ create_txrx(const sector_configuration& sector_cfg, } #ifdef DPDK_FOUND - auto eth_txrx = (sector_cfg.uses_dpdk) ? create_dpdk_txrx(sector_cfg, rx_executor, logger) - : create_socket_txrx(sector_cfg, rx_executor, logger); + auto eth_txrx = (sector_cfg.uses_dpdk) ? create_dpdk_txrx(sector_cfg, eth_rx_executor, logger) + : create_socket_txrx(sector_cfg, eth_rx_executor, logger); #else - auto eth_txrx = create_socket_txrx(sector_cfg, rx_executor, logger); + auto eth_txrx = create_socket_txrx(sector_cfg, eth_rx_executor, logger); #endif if (eth_gateway) { eth_txrx.first = std::move(eth_gateway.value()); @@ -174,19 +174,25 @@ std::unique_ptr srsran::ofh::create_ofh_sector(const sector_configuratio auto eth_txrx = create_txrx(sector_cfg, std::move(sector_deps.eth_gateway), std::move(sector_deps.eth_receiver), - *sector_deps.receiver_executor, + *sector_deps.txrx_executor, *sector_deps.logger); // Build the OFH receiver. auto rx_config = generate_receiver_config(sector_cfg); - auto receiver = create_receiver( - rx_config, *sector_deps.logger, std::move(eth_txrx.second), sector_deps.notifier, prach_repo, slot_repo, cp_repo); + auto receiver = create_receiver(rx_config, + *sector_deps.logger, + *sector_deps.uplink_executor, + std::move(eth_txrx.second), + sector_deps.notifier, + prach_repo, + slot_repo, + cp_repo); // Build the OFH transmitter. auto tx_config = generate_transmitter_config(sector_cfg); auto transmitter = create_transmitter(tx_config, *sector_deps.logger, - *sector_deps.transmitter_executor, + *sector_deps.txrx_executor, *sector_deps.downlink_executor, std::move(eth_txrx.first), prach_repo, diff --git a/lib/ofh/receiver/ofh_message_receiver.cpp b/lib/ofh/receiver/ofh_message_receiver.cpp index bb0346c259..71d2f028e7 100644 --- a/lib/ofh/receiver/ofh_message_receiver.cpp +++ b/lib/ofh/receiver/ofh_message_receiver.cpp @@ -27,8 +27,8 @@ using namespace srsran; using namespace ofh; -message_receiver::message_receiver(const message_receiver_config& config, - message_receiver_dependencies&& dependencies) : +message_receiver_impl::message_receiver_impl(const message_receiver_config& config, + message_receiver_dependencies&& dependencies) : logger(*dependencies.logger), nof_symbols(config.nof_symbols), scs(config.scs), @@ -51,8 +51,15 @@ message_receiver::message_receiver(const message_receiver_config& config, srsran_assert(eth_receiver, "Invalid Ethernet receiver"); } -void message_receiver::on_new_frame(span payload) +void message_receiver_impl::on_new_frame(ether::unique_rx_buffer buffer) { + process_new_frame(std::move(buffer)); +} + +void message_receiver_impl::process_new_frame(ether::unique_rx_buffer buffer) +{ + span payload = buffer.data(); + ether::vlan_frame_params eth_params; span ecpri_pdu = vlan_decoder->decode(payload, eth_params); if (ecpri_pdu.empty() || should_ethernet_frame_be_filtered(eth_params)) { @@ -66,7 +73,7 @@ void message_receiver::on_new_frame(span payload) } // Verify the sequence identifier. - const ecpri::iq_data_parameters& ecpri_iq_params = variant_get(ecpri_params.type_params); + const ecpri::iq_data_parameters& ecpri_iq_params = std::get(ecpri_params.type_params); unsigned eaxc = ecpri_iq_params.pc_id; int nof_skipped_seq_id = seq_id_checker->update_and_compare_seq_id(eaxc, (ecpri_iq_params.seq_id >> 8)); // Drop the message when it is from the past. @@ -110,7 +117,7 @@ void message_receiver::on_new_frame(span payload) ofh_tracer << trace_event("ofh_receiver_decode_data", decode_tp); } -bool message_receiver::should_ecpri_packet_be_filtered(const ecpri::packet_parameters& ecpri_params) const +bool message_receiver_impl::should_ecpri_packet_be_filtered(const ecpri::packet_parameters& ecpri_params) const { if (ecpri_params.header.msg_type != ecpri::message_type::iq_data) { logger.info("Dropped received Open Fronthaul User-Plane packet as decoded eCPRI message type is not for IQ data"); @@ -118,7 +125,7 @@ bool message_receiver::should_ecpri_packet_be_filtered(const ecpri::packet_param return true; } - const ecpri::iq_data_parameters& ecpri_iq_params = variant_get(ecpri_params.type_params); + const ecpri::iq_data_parameters& ecpri_iq_params = std::get(ecpri_params.type_params); if ((std::find(ul_eaxc.begin(), ul_eaxc.end(), ecpri_iq_params.pc_id) == ul_eaxc.end()) && (std::find(ul_prach_eaxc.begin(), ul_prach_eaxc.end(), ecpri_iq_params.pc_id) == ul_prach_eaxc.end())) { logger.info( @@ -131,7 +138,7 @@ bool message_receiver::should_ecpri_packet_be_filtered(const ecpri::packet_param return false; } -bool message_receiver::should_ethernet_frame_be_filtered(const ether::vlan_frame_params& eth_params) const +bool message_receiver_impl::should_ethernet_frame_be_filtered(const ether::vlan_frame_params& eth_params) const { if (eth_params.mac_src_address != vlan_params.mac_src_address) { logger.debug( diff --git a/lib/ofh/receiver/ofh_message_receiver.h b/lib/ofh/receiver/ofh_message_receiver.h index 20d48bc56c..4008a75176 100644 --- a/lib/ofh/receiver/ofh_message_receiver.h +++ b/lib/ofh/receiver/ofh_message_receiver.h @@ -74,22 +74,36 @@ struct message_receiver_dependencies { std::unique_ptr seq_id_checker; }; -/// Open Fronthaul message receiver. +/// Open Fronthaul message receiver interface. /// /// This class listens to incoming Ethernet frames and decodes them as Open Fronthaul messages. Once a new message is /// detected, is it handled to the corresponding data flow for further processing. class message_receiver : public ether::frame_notifier { public: - message_receiver(const message_receiver_config& config, message_receiver_dependencies&& dependencies); + /// Default destructor. + virtual ~message_receiver() = default; + + /// Returns the Ethernet receiver of this Open Fronthaul message receiver. + virtual ether::receiver& get_ethernet_receiver() = 0; +}; + +/// Open Fronthaul message receiver interface implementation. +class message_receiver_impl : public message_receiver +{ +public: + message_receiver_impl(const message_receiver_config& config, message_receiver_dependencies&& dependencies); // See interface for documentation. - void on_new_frame(span payload) override; + void on_new_frame(ether::unique_rx_buffer buffer) override; - /// Returns the Ethernet receiver of this Open Fronthaul message receiver. - ether::receiver& get_ethernet_receiver() { return *eth_receiver; } + // See interface for the documentation. + ether::receiver& get_ethernet_receiver() override { return *eth_receiver; } private: + /// Processes an Ethernet frame received from the underlying Ethernet link. + void process_new_frame(ether::unique_rx_buffer buff); + /// Returns true if the ethernet frame represented by the given eth parameters should be filtered, otherwise false. bool should_ethernet_frame_be_filtered(const ether::vlan_frame_params& eth_params) const; diff --git a/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h b/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h new file mode 100644 index 0000000000..e6e2a852b7 --- /dev/null +++ b/lib/ofh/receiver/ofh_message_receiver_task_dispatcher.h @@ -0,0 +1,57 @@ +/* + * + * 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 "ofh_message_receiver.h" +#include "srsran/support/executors/task_executor.h" + +namespace srsran { +namespace ofh { + +/// Open Fronthaul message receiver interface implementation that dispatches tasks to the actual receiver. +class ofh_message_receiver_task_dispatcher : public message_receiver +{ +public: + ofh_message_receiver_task_dispatcher(message_receiver& msg_receiver_, task_executor& executor_) : + msg_receiver(msg_receiver_), executor(executor_) + { + } + + // See interface for documentation. + void on_new_frame(ether::unique_rx_buffer buffer) override + { + if (!executor.execute([this, buff = std::move(buffer)]() mutable { msg_receiver.on_new_frame(std::move(buff)); })) { + srslog::fetch_basic_logger("OFH").warning("Failed to dispatch receiver task"); + } + } + + // See interface for documentation. + ether::receiver& get_ethernet_receiver() override { return msg_receiver.get_ethernet_receiver(); } + +private: + message_receiver& msg_receiver; + task_executor& executor; +}; + +} // namespace ofh +} // namespace srsran diff --git a/lib/ofh/receiver/ofh_receiver_factories.cpp b/lib/ofh/receiver/ofh_receiver_factories.cpp index c249808a08..91eb09a9e6 100644 --- a/lib/ofh/receiver/ofh_receiver_factories.cpp +++ b/lib/ofh/receiver/ofh_receiver_factories.cpp @@ -108,6 +108,7 @@ create_uplink_data_flow(const receiver_config& receiv static receiver_impl_dependencies resolve_receiver_dependencies(const receiver_config& receiver_cfg, srslog::basic_logger& logger, + task_executor& uplink_executor, std::unique_ptr eth_receiver, std::shared_ptr notifier, std::shared_ptr prach_context_repo, @@ -116,7 +117,8 @@ resolve_receiver_dependencies(const receiver_config& { receiver_impl_dependencies dependencies; - dependencies.logger = &logger; + dependencies.logger = &logger; + dependencies.executor = &uplink_executor; if (receiver_cfg.ignore_ecpri_payload_size_field) { dependencies.ecpri_decoder = ecpri::create_ecpri_packet_decoder_ignoring_payload_size(logger); @@ -143,6 +145,7 @@ resolve_receiver_dependencies(const receiver_config& std::unique_ptr srsran::ofh::create_receiver(const receiver_config& receiver_cfg, srslog::basic_logger& logger, + task_executor& uplink_executor, std::unique_ptr eth_rx, std::shared_ptr notifier, std::shared_ptr prach_context_repo, @@ -151,6 +154,7 @@ srsran::ofh::create_receiver(const receiver_config& r { auto rx_deps = resolve_receiver_dependencies(receiver_cfg, logger, + uplink_executor, std::move(eth_rx), std::move(notifier), std::move(prach_context_repo), diff --git a/lib/ofh/receiver/ofh_receiver_factories.h b/lib/ofh/receiver/ofh_receiver_factories.h index b12b896ea7..d46bf3ccc2 100644 --- a/lib/ofh/receiver/ofh_receiver_factories.h +++ b/lib/ofh/receiver/ofh_receiver_factories.h @@ -31,11 +31,15 @@ #include namespace srsran { + +class task_executor; + namespace ofh { /// Creates a receiver with the given configuration and dependencies. std::unique_ptr create_receiver(const receiver_config& receiver_cfg, srslog::basic_logger& logger, + task_executor& uplink_executor, std::unique_ptr eth_receiver, std::shared_ptr notifier, std::shared_ptr prach_context_repo, diff --git a/lib/ofh/receiver/ofh_receiver_impl.cpp b/lib/ofh/receiver/ofh_receiver_impl.cpp index bdb212f88e..19ca06dfb2 100644 --- a/lib/ofh/receiver/ofh_receiver_impl.cpp +++ b/lib/ofh/receiver/ofh_receiver_impl.cpp @@ -21,6 +21,7 @@ */ #include "ofh_receiver_impl.h" +#include "ofh_message_receiver_task_dispatcher.h" #include "srsran/ofh/ethernet/ethernet_properties.h" using namespace srsran; @@ -72,7 +73,8 @@ receiver_impl::receiver_impl(const receiver_config& config, receiver_impl_depend 1e6 / (get_nsymb_per_slot(config.cp) * get_nof_slots_per_subframe(config.scs)))), msg_receiver(get_message_receiver_configuration(config), get_message_receiver_dependencies(std::move(dependencies), window_checker)), - ctrl(msg_receiver) + rcv_task_dispatcher(msg_receiver, *dependencies.executor), + ctrl(rcv_task_dispatcher) { } diff --git a/lib/ofh/receiver/ofh_receiver_impl.h b/lib/ofh/receiver/ofh_receiver_impl.h index 1a24bea3a7..461b1249c9 100644 --- a/lib/ofh/receiver/ofh_receiver_impl.h +++ b/lib/ofh/receiver/ofh_receiver_impl.h @@ -26,18 +26,24 @@ #include "../support/uplink_context_repository.h" #include "../support/uplink_cplane_context_repository.h" #include "ofh_message_receiver.h" +#include "ofh_message_receiver_task_dispatcher.h" #include "ofh_receiver_controller.h" #include "ofh_rx_window_checker.h" #include "srsran/ofh/receiver/ofh_receiver.h" #include "srsran/ofh/receiver/ofh_receiver_configuration.h" namespace srsran { + +class task_executor; + namespace ofh { /// Open Fronthaul receiver implementation dependencies. struct receiver_impl_dependencies { /// Logger. srslog::basic_logger* logger = nullptr; + /// Uplink task executor. + task_executor* executor = nullptr; /// Ethernet receiver. std::unique_ptr eth_receiver; /// eCPRI packet decoder. @@ -67,9 +73,10 @@ class receiver_impl : public receiver controller& get_controller() override; private: - rx_window_checker window_checker; - message_receiver msg_receiver; - receiver_controller ctrl; + rx_window_checker window_checker; + message_receiver_impl msg_receiver; + ofh_message_receiver_task_dispatcher rcv_task_dispatcher; + receiver_controller ctrl; }; } // namespace ofh diff --git a/lib/ofh/support/prach_context_repository.h b/lib/ofh/support/prach_context_repository.h index b6ac9e2f76..1fc8d3028c 100644 --- a/lib/ofh/support/prach_context_repository.h +++ b/lib/ofh/support/prach_context_repository.h @@ -68,8 +68,7 @@ class prach_context prach_context() = default; /// Constructs an uplink PRACH context with the given PRACH buffer and PRACH buffer context. - prach_context(const prach_buffer_context& context, prach_buffer& buffer) : - context_info({context, &buffer}), nof_symbols(get_preamble_duration(context.format)) + prach_context(const prach_buffer_context& context, prach_buffer& buffer) : context_info({context, &buffer}) { srsran_assert(context.nof_fd_occasions == 1, "Only supporting one frequency domain occasion"); srsran_assert(context.nof_td_occasions == 1, "Only supporting one time domain occasion"); @@ -82,9 +81,8 @@ class prach_context freq_mapping_info = prach_frequency_mapping_get(preamble_info.scs, context.pusch_scs); - if (!nof_symbols) { - nof_symbols = 1; - } + nof_symbols = preamble_info.nof_symbols; + // Initialize statistics. for (unsigned i = 0; i != nof_symbols; ++i) { buffer_stats.emplace_back(buffer.get_max_nof_ports(), preamble_info.sequence_length); diff --git a/lib/ofh/support/uplink_context_repository.h b/lib/ofh/support/uplink_context_repository.h index 4d9dbc49bb..f05ff928fe 100644 --- a/lib/ofh/support/uplink_context_repository.h +++ b/lib/ofh/support/uplink_context_repository.h @@ -25,12 +25,12 @@ #include "srsran/adt/expected.h" #include "srsran/ofh/ofh_constants.h" #include "srsran/ofh/ofh_uplane_rx_symbol_notifier.h" -#include "srsran/ofh/timing/slot_symbol_point.h" #include "srsran/phy/support/resource_grid.h" #include "srsran/phy/support/resource_grid_context.h" #include "srsran/phy/support/resource_grid_reader.h" #include "srsran/phy/support/resource_grid_writer.h" #include "srsran/ran/cyclic_prefix.h" +#include "srsran/ran/resource_allocation/ofdm_symbol_range.h" #include "srsran/ran/resource_block.h" #include @@ -156,21 +156,29 @@ class uplink_context_repository } /// Adds the given entry to the repository at slot. - void add(const resource_grid_context& context, resource_grid& grid) + void add(const resource_grid_context& context, resource_grid& grid, const ofdm_symbol_range& symbol_range) { std::lock_guard lock(mutex); - for (unsigned symbol_id = 0, symbol_end = grid.get_reader().get_nof_symbols(); symbol_id != symbol_end; - ++symbol_id) { - if (logger) { - if (!entry(context.slot, symbol_id).empty()) { - const resource_grid_context& previous_context = entry(context.slot, symbol_id).get_grid_context(); + + // For logging purposes, check every symbol of the grid looking for existing contexts. + if (logger) { + for (unsigned symbol_id = 0, symbol_end = grid.get_reader().get_nof_symbols(); symbol_id != symbol_end; + ++symbol_id) { + if (auto& symbol_info = entry(context.slot, symbol_id); !symbol_info.empty()) { + const resource_grid_context& previous_context = symbol_info.get_grid_context(); logger->warning("Missed incoming User-Plane uplink messages for slot '{}', symbol '{}' and sector#{}", previous_context.slot, symbol_id, previous_context.sector); + + // Clear the stored context. + symbol_info = {}; } } + } + for (unsigned symbol_id = symbol_range.start(), symbol_end = symbol_range.stop(); symbol_id != symbol_end; + ++symbol_id) { entry(context.slot, symbol_id) = uplink_context(symbol_id, context, grid); } } diff --git a/lib/ofh/transmitter/ofh_uplink_request_handler_impl.cpp b/lib/ofh/transmitter/ofh_uplink_request_handler_impl.cpp index 3ab4ba17b6..5bad4465c0 100644 --- a/lib/ofh/transmitter/ofh_uplink_request_handler_impl.cpp +++ b/lib/ofh/transmitter/ofh_uplink_request_handler_impl.cpp @@ -94,7 +94,7 @@ void uplink_request_handler_impl::handle_prach_occasion(const prach_buffer_conte static_cast(SUBFRAME_DURATION_MSEC) / static_cast(get_nof_slots_per_subframe(context.pusch_scs) * nof_symbols_per_slot); - double len_msecs = (preamble_info.cp_length.to_seconds() + preamble_info.symbol_length.to_seconds()) * 1000; + double len_msecs = (preamble_info.cp_length.to_seconds() + preamble_info.symbol_length().to_seconds()) * 1000; unsigned nof_symbols = std::ceil(len_msecs / symbol_duration_msec); unsigned prach_length_slots = @@ -119,17 +119,22 @@ void uplink_request_handler_impl::handle_prach_occasion(const prach_buffer_conte } unsigned cp_length = preamble_info.cp_length.to_samples(ref_srate_Hz); - // Determine the last symbol that starts right at or before the PRACH preamble (after the cyclic prefix). + // Determine startSymbolId as the last symbol that starts right at or before the PRACH preamble (after the cyclic + // prefix). According to O-RAN.WG4.CUS.0.R003 section 4.4.3 if the SCS value provided by "frameStructure" is less than + // 15 kHz (e.g. for long preamble PRACH formats), then the symbol timing used to determine startSymbolId is based on + // the numerology of 15 kHz SCS. + subcarrier_spacing scs = is_short_preamble(context.format) ? static_cast(preamble_info.scs) + : subcarrier_spacing::kHz15; + unsigned pusch_symbol_duration = - phy_time_unit::from_units_of_kappa((144U + 2048U) >> to_numerology_value(context.pusch_scs)) - .to_samples(ref_srate_Hz); + phy_time_unit::from_units_of_kappa((144U + 2048U) >> to_numerology_value(scs)).to_samples(ref_srate_Hz); unsigned prach_start_symbol = context.start_symbol + (cp_length / pusch_symbol_duration); unsigned K = (1000 * scs_to_khz(context.pusch_scs)) / ra_scs_to_Hz(preamble_info.scs); data_flow_cplane_scheduling_prach_context cp_prach_context; cp_prach_context.slot = context.slot; - cp_prach_context.nof_repetitions = get_preamble_duration(context.format); + cp_prach_context.nof_repetitions = preamble_info.nof_symbols; cp_prach_context.start_symbol = prach_start_symbol; cp_prach_context.prach_scs = preamble_info.scs; cp_prach_context.scs = context.pusch_scs; @@ -152,9 +157,6 @@ void uplink_request_handler_impl::handle_new_uplink_slot(const resource_grid_con frame_pool->clear_uplink_slot(context.slot, logger); - // Store the context in the repository. - ul_slot_repo->add(context, grid); - data_flow_cplane_type_1_context df_context; df_context.slot = context.slot; df_context.filter_type = filter_index_type::standard_channel_filter; @@ -162,6 +164,9 @@ void uplink_request_handler_impl::handle_new_uplink_slot(const resource_grid_con df_context.symbol_range = tdd_config ? get_active_tdd_ul_symbols(tdd_config.value(), context.slot.slot_index(), cp) : ofdm_symbol_range(0, get_nsymb_per_slot(cp)); + // Store the context in the repository. + ul_slot_repo->add(context, grid, df_context.symbol_range); + for (auto eaxc : ul_eaxc) { df_context.eaxc = eaxc; data_flow->enqueue_section_type_1_message(df_context); diff --git a/lib/phy/lower/modulation/ofdm_prach_demodulator_impl.cpp b/lib/phy/lower/modulation/ofdm_prach_demodulator_impl.cpp index 821454585d..5da4517c56 100644 --- a/lib/phy/lower/modulation/ofdm_prach_demodulator_impl.cpp +++ b/lib/phy/lower/modulation/ofdm_prach_demodulator_impl.cpp @@ -97,7 +97,7 @@ void ofdm_prach_demodulator_impl::demodulate(prach_buffer& } // Calculate time-domain occasion end time. - phy_time_unit t_occasion_end = t_occasion_start + preamble_info.cp_length + preamble_info.symbol_length; + phy_time_unit t_occasion_end = t_occasion_start + preamble_info.cp_length + preamble_info.symbol_length(); // Add sixteen kappa to the cyclic prefix length if ... if (is_short_preamble(preamble_info.scs)) { @@ -114,7 +114,7 @@ void ofdm_prach_demodulator_impl::demodulate(prach_buffer& } // Calculate occasion duration. - phy_time_unit occasion_duration = preamble_info.cp_length + preamble_info.symbol_length; + phy_time_unit occasion_duration = preamble_info.cp_length + preamble_info.symbol_length(); unsigned sample_offset = t_occasion_start.to_samples(srate.to_Hz()); unsigned nof_samples = occasion_duration.to_samples(srate.to_Hz()); @@ -134,7 +134,7 @@ void ofdm_prach_demodulator_impl::demodulate(prach_buffer& unsigned prach_scs_Hz = ra_scs_to_Hz(preamble_info.scs); unsigned ofdm_symbol_len = dft.get_size(); unsigned cyclic_prefix_length = preamble_info.cp_length.to_samples(srate.to_Hz()); - unsigned nof_symbols = preamble_info.symbol_length.to_samples(ra_scs_to_Hz(preamble_info.scs)); + unsigned nof_symbols = preamble_info.nof_symbols; // Make sure the number of occasions and symbols fit in the buffer. srsran_assert(nof_symbols <= buffer.get_max_nof_symbols(), diff --git a/lib/phy/support/resource_grid_reader_impl.cpp b/lib/phy/support/resource_grid_reader_impl.cpp index b790256279..918ce142bd 100644 --- a/lib/phy/support/resource_grid_reader_impl.cpp +++ b/lib/phy/support/resource_grid_reader_impl.cpp @@ -56,45 +56,6 @@ bool resource_grid_reader_impl::is_empty() const return true; } -span resource_grid_reader_impl::get(span symbols, - unsigned port, - unsigned l, - unsigned k_init, - span mask) const -{ - unsigned mask_size = mask.size(); - - srsran_assert(k_init + mask_size <= get_nof_subc(), - "The initial subcarrier index (i.e., {}) plus the mask size (i.e., {}) exceeds the maximum number of " - "subcarriers (i.e., {})", - k_init, - mask.size(), - get_nof_subc()); - srsran_assert(l < get_nof_symbols(), - "Symbol index (i.e., {}) exceeds the maximum number of symbols (i.e., {})", - l, - get_nof_symbols()); - srsran_assert(port < get_nof_ports(), - "Port index (i.e., {}) exceeds the maximum number of ports (i.e., {})", - port, - get_nof_ports()); - - // Access the OFDM symbol from the resource grid. - span rg_symbol = data.get_view({l, port}); - - // Iterate mask. - unsigned count = 0; - for (unsigned k = 0; k != mask_size; ++k) { - if (mask[k]) { - symbols[count] = rg_symbol[k + k_init]; - count++; - } - } - - // Update symbol buffer. - return symbols.last(symbols.size() - count); -} - span resource_grid_reader_impl::get(span symbols, unsigned port, unsigned l, diff --git a/lib/phy/support/resource_grid_reader_impl.h b/lib/phy/support/resource_grid_reader_impl.h index 12b191bda5..82a174d47f 100644 --- a/lib/phy/support/resource_grid_reader_impl.h +++ b/lib/phy/support/resource_grid_reader_impl.h @@ -52,9 +52,6 @@ class resource_grid_reader_impl : public resource_grid_reader // See interface for documentation. bool is_empty() const override; - // See interface for documentation. - span get(span symbols, unsigned port, unsigned l, unsigned k_init, span mask) const override; - // See interface for documentation. span get(span symbols, unsigned port, diff --git a/lib/phy/support/resource_grid_writer_impl.cpp b/lib/phy/support/resource_grid_writer_impl.cpp index 3b14901d96..26f63dccc2 100644 --- a/lib/phy/support/resource_grid_writer_impl.cpp +++ b/lib/phy/support/resource_grid_writer_impl.cpp @@ -41,39 +41,6 @@ unsigned resource_grid_writer_impl::get_nof_symbols() const return data.get_dimension_size(resource_grid_dimensions::symbol); } -span resource_grid_writer_impl::put(unsigned port, - unsigned l, - unsigned k_init, - span mask, - span symbol_buffer) -{ - interval i_symbol_range(0, get_nof_symbols()); - interval mask_size_range(1, get_nof_subc()); - interval i_port_range(0, get_nof_ports()); - srsran_assert(i_symbol_range.contains(l), "Symbol index (i.e., {}) is out-of-range range {}.", l, i_symbol_range); - srsran_assert(mask_size_range.contains(mask.size()), - "The mask size (i.e., {}) is out-of-range range {}.", - mask.size(), - i_symbol_range); - srsran_assert( - i_port_range.contains(port), "The port identifier (i.e., {}) is out-of-range range {}.", port, i_port_range); - - // Select destination OFDM symbol from the resource grid. - span rg_symbol = data.get_view({l, port}); - - // Iterate mask using AVX2 intrinsics and preset groups of 4 subcarriers. - for (unsigned i_subc = 0, i_subc_end = mask.size(); i_subc < i_subc_end; ++i_subc) { - if (mask[i_subc]) { - rg_symbol[i_subc + k_init] = symbol_buffer.front(); - symbol_buffer = symbol_buffer.last(symbol_buffer.size() - 1); - } - } - clear_empty(port); - - // Update symbol buffer - return symbol_buffer; -} - span resource_grid_writer_impl::put(unsigned port, unsigned l, unsigned k_init, diff --git a/lib/phy/support/resource_grid_writer_impl.h b/lib/phy/support/resource_grid_writer_impl.h index c3c9e8334a..eb12da678b 100644 --- a/lib/phy/support/resource_grid_writer_impl.h +++ b/lib/phy/support/resource_grid_writer_impl.h @@ -44,10 +44,6 @@ class resource_grid_writer_impl : public resource_grid_writer // See interface for documentation. unsigned get_nof_symbols() const override; - // See interface for documentation. - span - put(unsigned port, unsigned l, unsigned k_init, span mask, span symbols) override; - // See interface for documentation. span put(unsigned port, unsigned l, diff --git a/lib/phy/upper/channel_coding/crc_calculator_lut_impl.cpp b/lib/phy/upper/channel_coding/crc_calculator_lut_impl.cpp index 2c1783c79c..1eb5311529 100644 --- a/lib/phy/upper/channel_coding/crc_calculator_lut_impl.cpp +++ b/lib/phy/upper/channel_coding/crc_calculator_lut_impl.cpp @@ -24,9 +24,9 @@ #include "srsran/srsvec/bit.h" #include "srsran/support/math_utils.h" -#if HAVE_SSE +#if __SSE4_1__ #include -#endif // HAVE_SSE +#endif // __SSE4_1__ using namespace srsran; @@ -92,15 +92,15 @@ crc_calculator_checksum_t crc_calculator_lut_impl::calculate_bit(srsran::span pter = input.subspan(8U * i, 8); -#ifdef HAVE_SSE +#ifdef __SSE4_1__ // Get 8 Bit __m64 mask = _mm_cmpgt_pi8(*(reinterpret_cast(pter.data())), _mm_set1_pi8(0)); // Get mask and reverse. uint8_t byte = reverse_byte(static_cast(_mm_movemask_pi8(mask))); -#else // HAVE_SSE +#else // __SSE4_1__ uint8_t byte = (uint8_t)(srsvec::bit_pack(pter, 8) & 0xff); -#endif // HAVE_SSE +#endif // __SSE4_1__ put_byte(byte); } diff --git a/lib/phy/upper/channel_coding/ldpc/simd_types.h b/lib/phy/upper/channel_coding/ldpc/simd_types.h index 1c2f99bff9..ab16ff2dcd 100644 --- a/lib/phy/upper/channel_coding/ldpc/simd_types.h +++ b/lib/phy/upper/channel_coding/ldpc/simd_types.h @@ -33,7 +33,7 @@ #if defined(__ARM_NEON) #include -#endif // defined(HAVE_NEON) +#endif // defined(__ARM_NEON) namespace srsran { namespace detail { @@ -54,7 +54,7 @@ using simd256_type = struct { using simd512_type = struct { // Just for compatibility with the x86 implementation, not used in ARM. }; -#endif // defined(HAVE_NEON) +#endif // defined(__ARM_NEON) struct simd128_wrapper { using simdType = simd128_type; diff --git a/lib/phy/upper/channel_coding/short/short_block_detector_impl.cpp b/lib/phy/upper/channel_coding/short/short_block_detector_impl.cpp index 4222a4f3e1..c9079538f6 100644 --- a/lib/phy/upper/channel_coding/short/short_block_detector_impl.cpp +++ b/lib/phy/upper/channel_coding/short/short_block_detector_impl.cpp @@ -76,8 +76,8 @@ static void validate_spans(span output, span output, span input) { - constexpr unsigned NOF_BITS = 3; - std::array llr_as_int; + constexpr unsigned NOF_BITS = 3; + std::array llr_as_int = {}; unsigned in_size = input.size(); if (in_size == NOF_BITS) { diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp index e09a3b3219..4a42bd4de3 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam16.cpp @@ -22,13 +22,13 @@ #include "demodulation_mapper_qam16.h" #include "srsran/phy/upper/log_likelihood_ratio.h" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ #include "avx2_helpers.h" -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include "neon_helpers.h" -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; @@ -38,7 +38,7 @@ static constexpr float RANGE_LIMIT_FLOAT = 20; // Square root of 1/10. static const float M_SQRT1_10 = 1.0F / std::sqrt(10.0F); -#ifdef HAVE_AVX2 +#ifdef __AVX2__ static void demod_QAM16_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { // Load symbols. @@ -117,9 +117,9 @@ static void demod_QAM16_avx2(log_likelihood_ratio* llr, const cf_t* symbol, cons _mm256_storeu_si256(reinterpret_cast<__m256i*>(llr), mm256::quantize_ps(l_value_0_, l_value_1_, l_value_2_, l_value_3_, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON static void demod_QAM16_neon(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { // Load symbols. @@ -191,7 +191,7 @@ static void demod_QAM16_neon(log_likelihood_ratio* llr, const cf_t* symbol, cons vst1q_s8(reinterpret_cast(llr), neon::quantize_f32(l_value_0, l_value_1, l_value_2, l_value_3, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_NEON +#endif // __ARM_NEON static log_likelihood_ratio demod_16QAM_symbol_01(float x, float noise_var) { @@ -231,7 +231,7 @@ void srsran::demodulate_soft_QAM16(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ // For AVX2, it generates 32 LLRs simultaneously. The input is read in batches of 8 symbols. for (std::size_t symbol_index_end = (symbols.size() / 8) * 8; symbol_index != symbol_index_end; symbol_index += 8) { demod_QAM16_avx2(llr_it, symbols_it, noise_it); @@ -240,9 +240,9 @@ void srsran::demodulate_soft_QAM16(span llrs, symbols_it += 8; noise_it += 8; } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON // For NEON, it generates 16 LLRs simultaneously. The input is read in batches of 4 symbols. for (std::size_t symbol_index_end = (symbols.size() / 4) * 4; symbol_index != symbol_index_end; symbol_index += 4) { demod_QAM16_neon(llr_it, symbols_it, noise_it); @@ -251,7 +251,7 @@ void srsran::demodulate_soft_QAM16(span llrs, symbols_it += 4; noise_it += 4; } -#endif // HAVE_NEON +#endif // __ARM_NEON for (std::size_t symbol_index_end = symbols.size(); symbol_index != symbol_index_end; ++symbol_index) { // Set all LLR to zero if the symbol is near zero. diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp index 3947a6640a..5fb4befe4a 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam256.cpp @@ -23,13 +23,13 @@ #include "demodulation_mapper_intervals.h" #include "srsran/phy/upper/log_likelihood_ratio.h" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ #include "avx2_helpers.h" -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include "neon_helpers.h" -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; @@ -154,7 +154,7 @@ const std::array SLOPE_67 = {4 * M_SQRT1_170, const std::array INTERCEPT_67 = {28.0F / 85, -20.0F / 85, 12.0F / 85, -4.0F / 85, -4.0F / 85, 12.0F / 85, -20.0F / 85, 28.0F / 85}; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ static void demod_QAM256_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { // Load symbols. @@ -204,9 +204,9 @@ static void demod_QAM256_avx2(log_likelihood_ratio* llr, const cf_t* symbol, con _mm256_storeu_si256(reinterpret_cast<__m256i*>(llr), mm256::quantize_ps(l_value_0_, l_value_1_, l_value_2_, l_value_3_, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON static void demod_QAM256_neon(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { // Load symbols. @@ -273,7 +273,7 @@ static void demod_QAM256_neon(log_likelihood_ratio* llr, const cf_t* symbol, con vst1q_s8(reinterpret_cast(llr + 16), neon::quantize_f32(l_value_4, l_value_5, l_value_6, l_value_7, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_NEON +#endif // __ARM_NEON static log_likelihood_ratio demod_256QAM_symbol_01(float value, float rcp_noise_var) { @@ -308,7 +308,7 @@ void srsran::demodulate_soft_QAM256(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ // For AVX2, it generates 32 LLRs simultaneously. The input is read in batches of 4 symbols. for (std::size_t symbol_index_end = (symbols.size() / 4) * 4; symbol_index != symbol_index_end; symbol_index += 4) { demod_QAM256_avx2(llr_it, symbols_it, noise_it); @@ -317,9 +317,9 @@ void srsran::demodulate_soft_QAM256(span llrs, symbols_it += 4; noise_it += 4; } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON // For NEON, it generates 32 LLRs simultaneously. The input is read in batches of 4 symbols. for (std::size_t symbol_index_end = (symbols.size() / 4) * 4; symbol_index != symbol_index_end; symbol_index += 4) { demod_QAM256_neon(llr_it, symbols_it, noise_it); @@ -328,7 +328,7 @@ void srsran::demodulate_soft_QAM256(span llrs, symbols_it += 4; noise_it += 4; } -#endif // HAVE_NEON +#endif // __ARM_NEON for (std::size_t symbol_index_end = symbols.size(); symbol_index != symbol_index_end; ++symbol_index) { // Set all LLR to zero if the symbol is near zero. diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp index 0af59f5d7e..df7cfd604b 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qam64.cpp @@ -23,13 +23,13 @@ #include "demodulation_mapper_intervals.h" #include "srsran/phy/upper/log_likelihood_ratio.h" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ #include "avx2_helpers.h" -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include "neon_helpers.h" -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; @@ -71,7 +71,7 @@ static const std::array SLOPE_45 = {4 * M_SQRT1_42, -4 * M_SQRT1_42, 4 * M_SQRT1_42, -4 * M_SQRT1_42, 0, 0, 0, 0}; static const std::array INTERCEPT_45 = {12.0F / 21, -4.0F / 21, -4.0F / 21, 12.0F / 21, 0, 0, 0, 0}; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ static void demod_QAM64_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { @@ -167,9 +167,9 @@ static void demod_QAM64_avx2(log_likelihood_ratio* llr, const cf_t* symbol, cons _mm256_storeu_si256(reinterpret_cast<__m256i*>(llr + 64), mm256::quantize_ps(l_value_8_, l_value_9_, l_value_10_, l_value_11_, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON static void demod_QAM64_neon(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { @@ -262,7 +262,7 @@ static void demod_QAM64_neon(log_likelihood_ratio* llr, const cf_t* symbol, cons vst1q_s8(reinterpret_cast(llr + 32), neon::quantize_f32(l_value_8, l_value_9, l_value_10, l_value_11, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_NEON +#endif // __ARM_NEON static log_likelihood_ratio demod_64QAM_symbol_01(float value, float rcp_noise_var) { @@ -291,7 +291,7 @@ void srsran::demodulate_soft_QAM64(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ // For AVX2, it generates 96 LLRs simultaneously. The input is read in batches of 16 symbols. for (std::size_t symbol_index_end = (symbols.size() / 16) * 16; symbol_index != symbol_index_end; symbol_index += 16) { @@ -301,9 +301,9 @@ void srsran::demodulate_soft_QAM64(span llrs, symbols_it += 16; noise_it += 16; } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON // For NEON, it generates 48 LLRs simultaneously. The input is read in batches of 8 symbols. for (std::size_t symbol_index_end = (symbols.size() / 8) * 8; symbol_index != symbol_index_end; symbol_index += 8) { demod_QAM64_neon(llr_it, symbols_it, noise_it); @@ -312,7 +312,7 @@ void srsran::demodulate_soft_QAM64(span llrs, symbols_it += 8; noise_it += 8; } -#endif // HAVE_NEON +#endif // __ARM_NEON for (std::size_t symbol_index_end = symbols.size(); symbol_index != symbol_index_end; ++symbol_index) { // Set all LLR to zero if the symbol is near zero. diff --git a/lib/phy/upper/channel_modulation/demodulation_mapper_qpsk.cpp b/lib/phy/upper/channel_modulation/demodulation_mapper_qpsk.cpp index 29457f2dc0..bb98af4d14 100644 --- a/lib/phy/upper/channel_modulation/demodulation_mapper_qpsk.cpp +++ b/lib/phy/upper/channel_modulation/demodulation_mapper_qpsk.cpp @@ -21,20 +21,20 @@ */ #include "demodulation_mapper_qpsk.h" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ #include "avx2_helpers.h" -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include "neon_helpers.h" -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; // Maximum (absolute) value considered for quantization. Larger values will be clipped. static constexpr float RANGE_LIMIT_FLOAT = 24; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ static void demod_QPSK_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { @@ -76,9 +76,9 @@ static void demod_QPSK_avx2(log_likelihood_ratio* llr, const cf_t* symbol, const mm256::quantize_ps(l_value_0, l_value_1, l_value_2, l_value_3, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON static void demod_QPSK_neon(log_likelihood_ratio* llr, const cf_t* symbol, const float* noise_var) { @@ -116,7 +116,7 @@ static void demod_QPSK_neon(log_likelihood_ratio* llr, const cf_t* symbol, const neon::quantize_f32(l_value_0, l_value_1, l_value_2, l_value_3, RANGE_LIMIT_FLOAT)); } -#endif // HAVE_NEON +#endif // __ARM_NEON static log_likelihood_ratio demod_QPSK_symbol(float x, float noise_var) { @@ -137,7 +137,7 @@ void srsran::demodulate_soft_QPSK(span llrs, log_likelihood_ratio* llr_it = llrs.begin(); std::size_t symbol_index = 0; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ // For AVX2, it generates 32 LLRs simultaneously. The input is read in batches of 16 symbols. for (std::size_t symbol_index_end = (symbols.size() / 16) * 16; symbol_index != symbol_index_end; symbol_index += 16) { @@ -147,9 +147,9 @@ void srsran::demodulate_soft_QPSK(span llrs, symbols_it += 16; noise_it += 16; } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON // For NEON, it generates 16 LLRs simultaneously. The input is read in batches of 8 symbols. for (std::size_t symbol_index_end = (symbols.size() / 8) * 8; symbol_index != symbol_index_end; symbol_index += 8) { demod_QPSK_neon(llr_it, symbols_it, noise_it); @@ -158,7 +158,7 @@ void srsran::demodulate_soft_QPSK(span llrs, symbols_it += 8; noise_it += 8; } -#endif // HAVE_NEON +#endif // __ARM_NEON // Process remainder symbols with the generic algorithm. for (std::size_t symbol_index_end = symbols.size(); symbol_index != symbol_index_end; ++symbol_index) { diff --git a/lib/phy/upper/channel_processors/CMakeLists.txt b/lib/phy/upper/channel_processors/CMakeLists.txt index de2d34179c..6055590755 100644 --- a/lib/phy/upper/channel_processors/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/CMakeLists.txt @@ -36,7 +36,7 @@ target_link_libraries(srsran_pdcch_modulator srsvec) add_library(srsran_pdcch_processor STATIC pdcch_processor_impl.cpp) target_link_libraries(srsran_pdcch_processor srsran_ran) -add_library(srsran_pdsch_encoder STATIC +add_library(srsran_pdsch_encoder STATIC pdsch_encoder_impl.cpp pdsch_encoder_hw_impl.cpp) target_link_libraries(srsran_pdsch_encoder srsran_channel_coding) @@ -60,7 +60,9 @@ target_link_libraries(srsran_prach_detector srsran_ran srsvec) add_library(srsran_pucch_processor STATIC pucch_processor_impl.cpp) -add_library(srsran_pucch_detector STATIC pucch_detector_impl.cpp) +add_library(srsran_pucch_detector STATIC + pucch_detector_format0.cpp + pucch_detector_impl.cpp) add_library(srsran_ssb_processor STATIC ssb_processor_impl.cpp) target_link_libraries(srsran_ssb_processor srsran_sequence_generators srsran_signal_processors) diff --git a/lib/phy/upper/channel_processors/channel_processor_factories.cpp b/lib/phy/upper/channel_processors/channel_processor_factories.cpp index be1408c0b0..4be3903a76 100644 --- a/lib/phy/upper/channel_processors/channel_processor_factories.cpp +++ b/lib/phy/upper/channel_processors/channel_processor_factories.cpp @@ -619,8 +619,14 @@ class pucch_detector_factory_sw : public pucch_detector_factory std::generate(alphas.begin(), alphas.end(), [n = 0U]() mutable { return (TWOPI * static_cast(n++) / static_cast(NRE)); }); - return std::make_unique( - low_papr_factory->create(1, 0, alphas), prg_factory->create(), eqzr_factory->create()); + + std::unique_ptr detector_format0 = + std::make_unique(prg_factory->create(), low_papr_factory->create(1, 0, alphas)); + + return std::make_unique(low_papr_factory->create(1, 0, alphas), + prg_factory->create(), + eqzr_factory->create(), + std::move(detector_format0)); } }; diff --git a/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp b/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp index 8db4a57467..db82005a8f 100644 --- a/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp +++ b/lib/phy/upper/channel_processors/prach_detector_generic_impl.cpp @@ -165,8 +165,7 @@ prach_detection_result prach_detector_generic_impl::detect(const prach_buffer& i max_delay_samples = (max_delay_samples * dft_size) / L_ra; // Calculate number of symbols. - unsigned nof_symbols = - static_cast(preamble_info.symbol_length.to_seconds() * ra_scs_to_Hz(preamble_info.scs)); + unsigned nof_symbols = preamble_info.nof_symbols; unsigned i_td_occasion = 0; unsigned i_fd_occasion = 0; diff --git a/lib/phy/upper/channel_processors/pucch_detector_format0.cpp b/lib/phy/upper/channel_processors/pucch_detector_format0.cpp new file mode 100644 index 0000000000..c48dc6d9a2 --- /dev/null +++ b/lib/phy/upper/channel_processors/pucch_detector_format0.cpp @@ -0,0 +1,202 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "pucch_detector_format0.h" +#include "srsran/phy/support/resource_grid_reader.h" +#include "srsran/ran/pucch/pucch_constants.h" +#include "srsran/srsvec/accumulate.h" +#include "srsran/srsvec/dot_prod.h" +#include "srsran/srsvec/prod.h" + +using namespace srsran; + +/// \brief PUCCH Format 0 dictionary entry. +/// +/// Pairs a cyclic shift with a corresponding UCI message. +struct pucch_detector_format0_entry { + /// Cyclic shift, parameter \f$m_{cs}\f$. + unsigned m_cs; + /// Message corresponding to the received cyclic shift. + pucch_uci_message message; +}; + +/// Table for positive SR only. Defined in TS38.213 Section 9.2.4. +static std::array pucch_detector_format0_noharq_sr = {{{0, {{1}, {}, {}, {}}}}}; +/// Table for one HARQ-ACK feedback bit. Defined in TS38.213 Section 9.2.4 Table 9.2.3-3. +static std::array pucch_detector_format0_oneharq_nosr = { + {{0, {{}, {0}, {}, {}}}, {6, {{}, {1}, {}, {}}}}}; +/// Table for two HARQ-ACK feedback bit. Defined in TS38.213 Section 9.2.4 Table 9.2.3-4. +static std::array pucch_detector_format0_twoharq_nosr = { + {{0, {{}, {0, 0}, {}, {}}}, {3, {{}, {0, 1}, {}, {}}}, {6, {{}, {1, 1}, {}, {}}}, {9, {{}, {1, 0}, {}, {}}}}}; +/// Table for one HARQ-ACK feedback bit and one SR bit. Defined in TS38.213 Table 9.2.3-3 for negative SR and Table +/// 9.2.5-1 for positive SR. +static std::array pucch_detector_format0_oneharq_onesr = { + {{0, {{0}, {0}, {}, {}}}, {6, {{0}, {1}, {}, {}}}, {3, {{1}, {0}, {}, {}}}, {9, {{1}, {1}, {}, {}}}}}; +/// Table for two HARQ-ACK feedback bit and one SR bit. Defined in TS38.213 Table 9.2.3-4 for negative SR and Table +/// 9.2.5-2 for positive SR. +static std::array pucch_detector_format0_twoharq_onesr = { + {{0, {{0}, {0, 0}, {}, {}}}, + {3, {{0}, {0, 1}, {}, {}}}, + {6, {{0}, {1, 1}, {}, {}}}, + {9, {{0}, {1, 0}, {}, {}}}, + {1, {{1}, {0, 0}, {}, {}}}, + {4, {{1}, {0, 1}, {}, {}}}, + {7, {{1}, {1, 1}, {}, {}}}, + {10, {{1}, {1, 0}, {}, {}}}}}; + +std::pair +pucch_detector_format0::detect(const srsran::resource_grid_reader& grid, + const pucch_detector::format0_configuration& config) +{ + // Select table. + span m_cs_table; + if (config.nof_harq_ack == 0) { + m_cs_table = span(pucch_detector_format0_noharq_sr); + } else if (config.nof_harq_ack == 1) { + if (config.sr_opportunity) { + m_cs_table = span(pucch_detector_format0_oneharq_onesr); + } else { + m_cs_table = span(pucch_detector_format0_oneharq_nosr); + } + } else if (config.nof_harq_ack == 2) { + if (config.sr_opportunity) { + m_cs_table = span(pucch_detector_format0_twoharq_onesr); + } else { + m_cs_table = span(pucch_detector_format0_twoharq_nosr); + } + } + + unsigned nof_ports = config.ports.size(); + unsigned nof_symbols = config.nof_symbols; + + // Verify parameters are correct. + srsran_assert(!m_cs_table.empty(), "Invalid payload combination."); + srsran_assert(nof_ports * nof_symbols != 0, "The number of ports or symbols is zero."); + + // Prepare the temporary RE storage. + temp_re.resize({NRE, nof_symbols, nof_ports}); + + // Extract resource elements. + for (unsigned i_symbol = 0; i_symbol != nof_symbols; ++i_symbol) { + // Calculate the initial subcarrier position. + unsigned i_subc = config.starting_prb * NRE; + if ((i_symbol != 0) && config.second_hop_prb.has_value()) { + i_subc = config.second_hop_prb.value() * NRE; + } + + // Iterate ports. + for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { + unsigned port_id = config.ports[i_port]; + grid.get(temp_re.get_view({i_symbol, i_port}), port_id, config.start_symbol_index + i_symbol, i_subc); + } + } + + // Calculate linear EPRE. + float epre = 0.0F; + for (unsigned i_symbol = 0; i_symbol != nof_symbols; ++i_symbol) { + for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { + epre += srsvec::average_power(temp_re.get_view({i_symbol, i_port})); + } + } + epre /= static_cast(nof_symbols * nof_ports); + + // Generate default message. + pucch_uci_message default_message({config.sr_opportunity ? 1U : 0U, config.nof_harq_ack, 0U, 0U}); + default_message.set_status(uci_status::invalid); + + // Correlate each of the cyclic shifts. + const pucch_uci_message* best_message = &default_message; + float best_metric = 0.0F; + float best_rsrp = 0.0F; + for (const auto& m_cs_entry : m_cs_table) { + float sum_corr = 0.0F; + float sum_noise_var = 0.0F; + + for (unsigned i_symbol = 0; i_symbol != nof_symbols; ++i_symbol) { + // Calculate group sequence. + unsigned u; + unsigned v; + helper.compute_group_sequence(pucch_group_hopping::NEITHER, config.n_id, u, v); + + // Calculate cyclic shift. + unsigned alpha = helper.get_alpha_index(config.slot, + config.cp, + config.n_id, + i_symbol + config.start_symbol_index, + config.initial_cyclic_shift, + m_cs_entry.m_cs); + span sequence = low_papr->get(u, v, alpha); + + // Process each receive port. + for (unsigned i_port = 0; i_port != nof_ports; ++i_port) { + // Select received symbols. + span rx_symbols = temp_re.get_view({i_symbol, i_port}); + + // Calculate least square estimates. + srsvec::prod_conj(rx_symbols, sequence, lse); + + // Calculate the average power of the LSE. + float avg_pwr = srsvec::average_power(lse); + + // Calculate the correlation between the received signal and the low PAPR sequence. + float corr = std::norm(srsvec::accumulate(lse) / static_cast(NRE)); + + // Estimate noise variance. Assume noise and signal are orthogonal and the EPRE is equal to the sum of the + // signal and the noise. + float noise_var = std::max(0.0F, avg_pwr - corr); + + // Accumulate correlation for each channel. + sum_corr += corr; + + // Accumulate noise variance weighted by the correlation. + sum_noise_var += noise_var * corr; + } + } + + // Calculate detection metric. + float detection_metric = 0.0F; + if (std::isnormal(sum_noise_var)) { + detection_metric = sum_corr * sum_corr / sum_noise_var; + } + + // Update the best metric and the actual message. + if (detection_metric > best_metric) { + best_metric = detection_metric; + best_rsrp = sum_corr; + best_message = &m_cs_entry.message; + } + } + + pucch_uci_message message = *best_message; + if (best_metric > detection_threshold) { + message.set_status(uci_status::valid); + } else { + message.set_status(uci_status::invalid); + } + + channel_state_information csi(channel_state_information::sinr_type::post_equalization); + csi.set_sinr_dB(channel_state_information::sinr_type::post_equalization, convert_power_to_dB(best_metric)); + csi.set_rsrp(convert_power_to_dB(best_rsrp)); + csi.set_epre(convert_power_to_dB(epre)); + + return std::make_pair(message, csi); +} \ No newline at end of file diff --git a/lib/phy/upper/channel_processors/pucch_detector_format0.h b/lib/phy/upper/channel_processors/pucch_detector_format0.h new file mode 100644 index 0000000000..5b2af59793 --- /dev/null +++ b/lib/phy/upper/channel_processors/pucch_detector_format0.h @@ -0,0 +1,68 @@ +/* + * + * 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 "../signal_processors/pucch/pucch_helper.h" +#include "srsran/phy/upper/channel_processors/pucch_detector.h" +#include "srsran/phy/upper/sequence_generators/low_papr_sequence_collection.h" +#include "srsran/ran/pucch/pucch_constants.h" + +namespace srsran { + +/// Implements a PUCCH Format 0 detector. +class pucch_detector_format0 +{ +public: + /// Creates a PUCCH Format 0 detector with its dependencies. + pucch_detector_format0(std::unique_ptr pseudo_random_, + std::unique_ptr low_papr_) : + helper(std::move(pseudo_random_)), low_papr(std::move(low_papr_)) + { + srsran_assert(low_papr, ""); + } + + /// Detects PUCCH Format0 transmission. See \ref pucch_detector for more details. + std::pair detect(const resource_grid_reader& grid, + const pucch_detector::format0_configuration& config); + +private: + /// Detection threshold. + static constexpr float detection_threshold = 4.0F; + /// Maximum number of RE used for PUCCH Format 0. Recall that PUCCH format 0 occupies a single RB. + static constexpr unsigned max_nof_re = NRE * MAX_PORTS * pucch_constants::format0_nof_symbols_range.stop(); + + /// Temporary RE storage tensor dimensions. + enum class dims : unsigned { re = 0, symbol = 1, rx_port = 2, nof_dims = 3 }; + + /// PUCCH sequence helper. + pucch_helper helper; + /// Collection of low-PAPR sequences. + std::unique_ptr low_papr; + + /// Temporary storage of the resource elements. + static_tensor(dims::nof_dims), cf_t, max_nof_re, dims> temp_re; + /// Temporary least square estimates for a single OFDM symbol. + std::array lse; +}; + +} // namespace srsran diff --git a/lib/phy/upper/channel_processors/pucch_detector_impl.cpp b/lib/phy/upper/channel_processors/pucch_detector_impl.cpp index 0abab8c9f3..27827772a2 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_impl.cpp +++ b/lib/phy/upper/channel_processors/pucch_detector_impl.cpp @@ -24,7 +24,6 @@ /// \brief PUCCH detector definition for Formats 0 and 1. #include "pucch_detector_impl.h" -#include "srsran/phy/support/resource_grid_reader.h" #include "srsran/srsvec/copy.h" #include "srsran/srsvec/mean.h" @@ -207,6 +206,13 @@ static float detect_bits(span out_bits, cf_t detected_symbol, float eq_ return (std::norm(detected_symbol) / eq_noise_var); } +std::pair +pucch_detector_impl::detect(const srsran::resource_grid_reader& grid, + const srsran::pucch_detector::format0_configuration& config) +{ + return detector_format0->detect(grid, config); +} + pucch_detector::pucch_detection_result pucch_detector_impl::detect(const resource_grid_reader& grid, const channel_estimate& estimates, const format1_configuration& config) diff --git a/lib/phy/upper/channel_processors/pucch_detector_impl.h b/lib/phy/upper/channel_processors/pucch_detector_impl.h index f0596e7b31..5f54d6f67f 100644 --- a/lib/phy/upper/channel_processors/pucch_detector_impl.h +++ b/lib/phy/upper/channel_processors/pucch_detector_impl.h @@ -25,6 +25,8 @@ #pragma once +#include "pucch_detector_format0.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/sequence_generators/low_papr_sequence_collection.h" @@ -45,25 +47,31 @@ class pucch_detector_impl : public pucch_detector static constexpr unsigned MAX_N_DATA_SYMBOLS = 7; /// \brief Constructor: provides access to a collection of low-PAPR sequences and a pseudorandom sequence generator. - /// \param[in] lpsc Pointer to a collection of low-PAPR sequences. - /// \param[in] prg Pointer to a pseudorandom sequence generator. + /// \param[in] low_papr_ Collection of low-PAPR sequences. + /// \param[in] pseudo_random_ Pseudo-random sequence generator. + /// \param[in] equalizer_ Channel equalizer. + /// \param[in] detector_format0_ PUCCH Format 0 detector. /// \remark The low-PAPR collection should correspond to the cyclic shifts \f$\{\alpha : \alpha = 2 \pi /// \frac{n}{N_{\textup{sc}}^{\textup{RB}}}, \quad n = 0, \dots, N_{\textup{sc}}^{\textup{RB}}-1\}\f$, where /// \f$N_{\textup{sc}}^{\textup{RB}} = 12\f$ is the number of subcarriers in a resource block. - pucch_detector_impl(std::unique_ptr lpsc, - std::unique_ptr prg, - std::unique_ptr eqzr) : - low_papr(std::move(lpsc)), pseudo_random(std::move(prg)), equalizer(std::move(eqzr)) + pucch_detector_impl(std::unique_ptr low_papr_, + std::unique_ptr pseudo_random_, + std::unique_ptr equalizer_, + std::unique_ptr detector_format0_) : + low_papr(std::move(low_papr_)), + pseudo_random(std::move(pseudo_random_)), + equalizer(std::move(equalizer_)), + detector_format0(std::move(detector_format0_)) { + srsran_assert(low_papr, "Invalid Low PAPR sequence generator."); + srsran_assert(pseudo_random, "Invalid pseudo-random sequence generator."); + srsran_assert(equalizer, "Invalid equalizer."); + srsran_assert(detector_format0, "PUCCH Format 0 detector."); } // See interface for documentation. - std::pair detect(const resource_grid_reader& /*grid*/, - const format0_configuration& /*config*/) override - { - srsran_assert(false, "PUCCH Format 0 not implemented yet."); - return {}; - } + std::pair detect(const resource_grid_reader& grid, + const format0_configuration& config) override; // See interface for documentation. pucch_detection_result detect(const resource_grid_reader& grid, @@ -95,6 +103,8 @@ class pucch_detector_impl : public pucch_detector std::unique_ptr pseudo_random; /// Channel equalizer. std::unique_ptr equalizer; + /// PUCCH Format 0 detector. + std::unique_ptr detector_format0; /// \brief Tensor 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), diff --git a/lib/phy/upper/channel_processors/pucch_processor_impl.cpp b/lib/phy/upper/channel_processors/pucch_processor_impl.cpp index 1490be9f2a..27791f8e93 100644 --- a/lib/phy/upper/channel_processors/pucch_processor_impl.cpp +++ b/lib/phy/upper/channel_processors/pucch_processor_impl.cpp @@ -26,6 +26,37 @@ using namespace srsran; +pucch_processor_result pucch_processor_impl::process(const resource_grid_reader& grid, + const pucch_processor::format0_configuration& config) +{ + assert_format0_config(config); + + // Calculate actual PRB. + std::optional second_hop_prb; + if (config.second_hop_prb.has_value()) { + second_hop_prb.emplace(config.second_hop_prb.value() + config.bwp_start_rb); + } + + pucch_detector::format0_configuration detector_config; + detector_config.slot = config.slot; + detector_config.starting_prb = config.starting_prb + config.bwp_start_rb; + detector_config.second_hop_prb = second_hop_prb; + detector_config.start_symbol_index = config.start_symbol_index; + detector_config.nof_symbols = config.nof_symbols; + detector_config.initial_cyclic_shift = config.initial_cyclic_shift; + detector_config.n_id = config.n_id; + detector_config.nof_harq_ack = config.nof_harq_ack; + detector_config.sr_opportunity = config.sr_opportunity; + detector_config.ports = config.ports; + std::pair detection_result = detector->detect(grid, detector_config); + + pucch_processor_result result; + result.message = detection_result.first; + result.csi = detection_result.second; + + return result; +} + pucch_processor_result pucch_processor_impl::process(const resource_grid_reader& grid, const format1_configuration& config) { @@ -229,6 +260,66 @@ void pucch_processor_impl::assert_format1_config(const pucch_processor::format1_ max_sizes.nof_rx_ports); } +void pucch_processor_impl::assert_format0_config(const pucch_processor::format0_configuration& config) +{ + // Assert BWP allocation. + srsran_assert(config.bwp_start_rb + config.bwp_size_rb <= MAX_RB, + "BWP allocation goes up to PRB {}, exceeding the configured maximum grid RB size, i.e., {}.", + config.bwp_start_rb + config.bwp_size_rb, + MAX_RB); + + // Assert that the PRB allocation is constrained to the BWP. Recall that PUCCH Format 0 occupies a single PRB. + srsran_assert(config.starting_prb < config.bwp_size_rb, + "PRB allocation within the BWP goes up to PRB {}, exceeding BWP size, i.e., {}.", + config.starting_prb + 1, + config.bwp_size_rb); + + if (config.second_hop_prb.has_value()) { + srsran_assert(config.second_hop_prb.value() < config.bwp_size_rb, + "Second hop PRB allocation within the BWP goes up to PRB {}, exceeding BWP size, i.e., {}.", + config.second_hop_prb.value() + 1, + config.bwp_size_rb); + } + + // Assert that the OFDM symbols are constrained to the slot dimensions given by the CP. + srsran_assert(config.start_symbol_index + config.nof_symbols <= get_nsymb_per_slot(config.cp), + "OFDM symbol allocation goes up to symbol {}, exceeding the number of symbols in the given slot with " + "{} CP, i.e., {}.", + config.start_symbol_index + config.nof_symbols, + config.cp.to_string(), + get_nsymb_per_slot(config.cp)); + + // Assert that the OFDM symbols are within the configured maximum slot dimensions. + srsran_assert(pucch_constants::format0_nof_symbols_range.contains(config.nof_symbols), + "Number of symbols (i.e., {}) is out of the range {}.", + config.nof_symbols, + pucch_constants::format0_nof_symbols_range); + + // Assert that initial cyclic shift is within the valid range. + srsran_assert(pucch_constants::format0_initial_cyclic_shift_range.contains(config.initial_cyclic_shift), + "The initial cyclic shift (i.e., {}) is out of the range {}.", + config.initial_cyclic_shift, + pucch_constants::format0_initial_cyclic_shift_range); + + // Assert that the sequence hopping identifier is within the valid range. + srsran_assert(pucch_constants::n_id_range.contains(config.n_id), + "The sequence hopping identifier (i.e., {}) is out of the range {}.", + config.n_id, + pucch_constants::n_id_range); + + // Assert that the number of HARQ-ACK bits is within the valid range. + srsran_assert(pucch_constants::format0_nof_harq_ack_range.contains(config.nof_harq_ack), + "The number of HARQ-ACK bits (i.e., {}) is out of the range {}.", + config.nof_harq_ack, + pucch_constants::format0_nof_harq_ack_range); + + // Assert that there is payload. + srsran_assert(config.sr_opportunity || (config.nof_harq_ack > 0), "No payload."); + + // Assert the number of receive ports. + srsran_assert(!config.ports.empty(), "The number of receive ports cannot be zero."); +} + void pucch_processor_impl::assert_format2_config(const pucch_processor::format2_configuration& config) { // Assert BWP allocation. @@ -291,6 +382,63 @@ void pucch_processor_impl::assert_format2_config(const pucch_processor::format2_ static_cast(FORMAT2_MAX_UCI_NBITS)); } +bool pucch_pdu_validator_impl::is_valid(const pucch_processor::format0_configuration& config) const +{ + // BWP PRB shall not exceed the maximum. + if (config.bwp_start_rb + config.bwp_size_rb > MAX_RB) { + return false; + } + + // PRB allocation goes beyond the BWP. Recall that PUCCH Format 0 occupies a single PRB. + if (config.starting_prb >= config.bwp_size_rb) { + return false; + } + + // Second hop PRB allocation goes beyond the BWP. + if (config.second_hop_prb.has_value()) { + if (config.second_hop_prb.value() >= config.bwp_size_rb) { + return false; + } + } + + // The number of symbols shall be in the range. + if (!pucch_constants::format0_nof_symbols_range.contains(config.nof_symbols)) { + return false; + } + + // None of the occupied symbols can exceed the configured maximum slot size, or the slot size given by the CP. + if (config.start_symbol_index + config.nof_symbols > get_nsymb_per_slot(config.cp)) { + return false; + } + + // Initial cyclic shift must be in range. + if (!pucch_constants::format0_initial_cyclic_shift_range.contains(config.initial_cyclic_shift)) { + return false; + } + + // Hopping identifier must be in range. + if (!pucch_constants::n_id_range.contains(config.n_id)) { + return false; + } + + // No payload detected. + if ((config.nof_harq_ack == 0) && !config.sr_opportunity) { + return false; + } + + // Number of HARQ-ACK exceeds maximum. + if (!pucch_constants::format0_nof_harq_ack_range.contains(config.nof_harq_ack)) { + return false; + } + + // The number of receive ports must not be empty. + if (config.ports.empty()) { + return false; + } + + return true; +} + bool pucch_pdu_validator_impl::is_valid(const pucch_processor::format1_configuration& config) const { // The BWP size exceeds the grid dimensions. diff --git a/lib/phy/upper/channel_processors/pucch_processor_impl.h b/lib/phy/upper/channel_processors/pucch_processor_impl.h index 1375b0f8c9..4d843fbcef 100644 --- a/lib/phy/upper/channel_processors/pucch_processor_impl.h +++ b/lib/phy/upper/channel_processors/pucch_processor_impl.h @@ -47,7 +47,7 @@ class pucch_pdu_validator_impl : public pucch_pdu_validator } // See interface for documentation. - bool is_valid(const pucch_processor::format0_configuration& config) const override { return true; }; + bool is_valid(const pucch_processor::format0_configuration& config) const override; bool is_valid(const pucch_processor::format1_configuration& config) const override; bool is_valid(const pucch_processor::format2_configuration& config) const override; bool is_valid(const pucch_processor::format3_configuration& config) const override { return true; } @@ -66,17 +66,7 @@ class pucch_processor_impl : public pucch_processor static constexpr unsigned FORMAT2_MAX_UCI_NBITS = 1706; // See interface for documentation. - pucch_processor_result process(const resource_grid_reader& grid, const format0_configuration& config) override - { - pucch_detector::format0_configuration detector_config; - std::pair detection_result = detector->detect(grid, detector_config); - - pucch_processor_result result; - result.message = detection_result.first; - result.csi = detection_result.second; - - return result; - } + pucch_processor_result process(const resource_grid_reader& grid, const format0_configuration& config) override; // See interface for documentation. pucch_processor_result process(const resource_grid_reader& grid, const format1_configuration& config) override; @@ -121,6 +111,8 @@ class pucch_processor_impl : public pucch_processor } private: + /// Validates PUCCH Format 0 configuration. + void assert_format0_config(const pucch_processor::format0_configuration& config); /// Validates PUCCH Format 1 configuration. void assert_format1_config(const pucch_processor::format1_configuration& config); /// Validates PUCCH Format 2 configuration. diff --git a/lib/phy/upper/channel_processors/pusch/CMakeLists.txt b/lib/phy/upper/channel_processors/pusch/CMakeLists.txt index 11403b67aa..a4716b0855 100644 --- a/lib/phy/upper/channel_processors/pusch/CMakeLists.txt +++ b/lib/phy/upper/channel_processors/pusch/CMakeLists.txt @@ -20,6 +20,7 @@ add_library(srsran_pusch_processor STATIC pusch_codeblock_decoder.cpp + pusch_decoder_empty_impl.cpp pusch_decoder_impl.cpp pusch_demodulator_impl.cpp factories.cpp diff --git a/lib/phy/upper/channel_processors/pusch/factories.cpp b/lib/phy/upper/channel_processors/pusch/factories.cpp index a2d5a33975..3a7e2423e2 100644 --- a/lib/phy/upper/channel_processors/pusch/factories.cpp +++ b/lib/phy/upper/channel_processors/pusch/factories.cpp @@ -23,6 +23,7 @@ #include "srsran/phy/upper/channel_processors/pusch/factories.h" #include "logging_pusch_processor_decorator.h" #include "pusch_codeblock_decoder.h" +#include "pusch_decoder_empty_impl.h" #include "pusch_decoder_hw_impl.h" #include "pusch_decoder_impl.h" #include "pusch_demodulator_impl.h" @@ -39,6 +40,30 @@ using namespace srsran; namespace { +/// \brief Factory for empty PUSCH decoders. +/// +/// It creates \ref pusch_decoder_empty_impl instances. +class pusch_decoder_factory_empty : public pusch_decoder_factory +{ +public: + /// Constructs a factory taking the maximum number of PRB and layers. + pusch_decoder_factory_empty(unsigned nof_prb_, unsigned nof_layers_) : nof_prb(nof_prb_), nof_layers(nof_layers_) + { + srsran_assert(nof_prb > 0, "Invalid number of PRB."); + srsran_assert(nof_layers > 0, "Invalid number of layers."); + } + + // See interface for documentation. + std::unique_ptr create() override + { + return std::make_unique(nof_prb, nof_layers); + } + +private: + unsigned nof_prb; + unsigned nof_layers; +}; + class pusch_decoder_factory_generic : public pusch_decoder_factory { public: @@ -228,47 +253,62 @@ class pusch_processor_factory_generic : public pusch_processor_factory class pusch_processor_pool_factory : public pusch_processor_factory { public: - pusch_processor_pool_factory(std::shared_ptr factory_, - unsigned max_nof_processors_, - bool blocking_) : - factory(std::move(factory_)), max_nof_processors(max_nof_processors_), blocking(blocking_) + pusch_processor_pool_factory(pusch_processor_pool_factory_config& config) : + regular_factory(config.factory), + uci_factory(config.uci_factory), + nof_regular_processors(config.nof_regular_processors), + nof_uci_processors(config.nof_uci_processors), + blocking(config.blocking) { - srsran_assert(factory, "Invalid PUSCH factory."); + srsran_assert(regular_factory, "Invalid PUSCH factory."); + srsran_assert(uci_factory, "Invalid PUSCH factory for UCI."); } std::unique_ptr create() override { - if (max_nof_processors <= 1) { - return factory->create(); + if (nof_regular_processors <= 1) { + return regular_factory->create(); } - std::vector> processors(max_nof_processors); + std::vector> processors(nof_regular_processors); for (std::unique_ptr& processor : processors) { - processor = factory->create(); + processor = regular_factory->create(); } - return std::make_unique(processors, blocking); + std::vector> uci_processors(nof_uci_processors); + for (std::unique_ptr& processor : uci_processors) { + processor = uci_factory->create(); + } + + return std::make_unique(std::move(processors), std::move(uci_processors), blocking); } std::unique_ptr create(srslog::basic_logger& logger) override { - if (max_nof_processors <= 1) { - return factory->create(logger); + if (nof_regular_processors <= 1) { + return regular_factory->create(logger); } - std::vector> processors(max_nof_processors); + std::vector> processors(nof_regular_processors); for (std::unique_ptr& processor : processors) { - processor = factory->create(logger); + processor = regular_factory->create(logger); } - return std::make_unique(processors, blocking); + std::vector> uci_processors(nof_uci_processors); + for (std::unique_ptr& processor : uci_processors) { + processor = uci_factory->create(logger); + } + + return std::make_unique(std::move(processors), std::move(uci_processors), blocking); } - std::unique_ptr create_validator() override { return factory->create_validator(); } + std::unique_ptr create_validator() override { return regular_factory->create_validator(); } private: - std::shared_ptr factory; - unsigned max_nof_processors; + std::shared_ptr regular_factory; + std::shared_ptr uci_factory; + unsigned nof_regular_processors; + unsigned nof_uci_processors; bool blocking; }; @@ -280,6 +320,11 @@ class ulsch_demultiplex_factory_sw : public ulsch_demultiplex_factory } // namespace +std::shared_ptr srsran::create_pusch_decoder_empty_factory(unsigned nof_prb, unsigned nof_layers) +{ + return std::make_shared(nof_prb, nof_layers); +} + std::shared_ptr srsran::create_pusch_decoder_factory_sw(pusch_decoder_factory_sw_configuration config) { @@ -313,11 +358,9 @@ srsran::create_pusch_processor_factory_sw(pusch_processor_factory_sw_configurati } std::shared_ptr -srsran::create_pusch_processor_pool(std::shared_ptr factory, - unsigned max_nof_processors, - bool blocking) +srsran::create_pusch_processor_pool(pusch_processor_pool_factory_config& config) { - return std::make_shared(std::move(factory), max_nof_processors, blocking); + return std::make_shared(config); } std::unique_ptr pusch_processor_factory::create(srslog::basic_logger& logger) diff --git a/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.cpp b/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.cpp new file mode 100644 index 0000000000..498c1c4960 --- /dev/null +++ b/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.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 "pusch_decoder_empty_impl.h" +#include "srsran/phy/upper/channel_coding/ldpc/ldpc.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_decoder_notifier.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_decoder_result.h" +#include "srsran/ran/pusch/pusch_constants.h" +#include "srsran/srsvec/zero.h" + +using namespace srsran; + +pusch_decoder_empty_impl::pusch_decoder_empty_impl(unsigned nof_prb, unsigned nof_layers) : + decoder_buffer(nof_prb, nof_layers) +{ +} + +pusch_decoder_buffer& pusch_decoder_empty_impl::new_data(span transport_block, + unique_rx_buffer rm_buffer_, + pusch_decoder_notifier& notifier_, + const pusch_decoder::configuration& cfg) +{ + decoder_buffer.new_data(transport_block, std::move(rm_buffer_), notifier_, cfg); + return decoder_buffer; +} + +void pusch_decoder_empty_impl::set_nof_softbits(units::bits /* nof_softbits */) +{ + // Do nothing. +} + +void pusch_decoder_empty_impl::decoder_buffer_impl::new_data(span transport_block, + srsran::unique_rx_buffer rm_buffer_, + srsran::pusch_decoder_notifier& notifier_, + const pusch_decoder::configuration& cfg) +{ + units::bytes tb_size = units::bytes(transport_block.size()); + + // Erase transport block contents from a previous transmission. + srsvec::zero(transport_block); + + // Save inputs. + rm_buffer = std::move(rm_buffer_); + srsran_assert(rm_buffer.is_valid(), "Invalid buffer."); + notifier = ¬ifier_; + is_new_data = cfg.new_data; + softbits_count = 0; + nof_codeblocks = ldpc::compute_nof_codeblocks(tb_size.to_bits(), cfg.base_graph); + + // Calculate full code block size. + unsigned lifting_size = ldpc::compute_lifting_size(tb_size.to_bits(), cfg.base_graph, nof_codeblocks); + codeblock_size = ldpc::compute_codeblock_size(cfg.base_graph, lifting_size); +} + +span pusch_decoder_empty_impl::decoder_buffer_impl::get_next_block_view(unsigned block_size) +{ + // Return always the first softbits, the decoder will ignore the soft bits. + return span(softbits_buffer).first(block_size); +} + +void pusch_decoder_empty_impl::decoder_buffer_impl::on_new_softbits(span softbits) +{ + // Accumulate the count of softbits. + softbits_count += softbits.size(); +} + +void pusch_decoder_empty_impl::decoder_buffer_impl::on_end_softbits() +{ + // Make sure the notifier is valid. + srsran_assert(notifier != nullptr, "Invalid notifier."); + + // Prepare failed transport block. + pusch_decoder_result result; + result.ldpc_decoder_stats.reset(); + result.nof_codeblocks_total = 0; + result.tb_crc_ok = false; + + // Reset rate matching data if it a new transmission. + if (is_new_data) { + for (unsigned i_cb = 0; i_cb != nof_codeblocks; ++i_cb) { + srsvec::zero(rm_buffer->get_codeblock_soft_bits(i_cb, codeblock_size.value())); + } + } + + // Release buffer. + rm_buffer.release(); + + // Notify completion. + notifier->on_sch_data(result); +} diff --git a/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.h b/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.h new file mode 100644 index 0000000000..47fafde009 --- /dev/null +++ b/lib/phy/upper/channel_processors/pusch/pusch_decoder_empty_impl.h @@ -0,0 +1,104 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +#include "srsran/phy/upper/channel_processors/pusch/pusch_decoder.h" +#include "srsran/phy/upper/channel_processors/pusch/pusch_decoder_buffer.h" +#include "srsran/phy/upper/unique_rx_buffer.h" +#include "srsran/ran/pusch/pusch_constants.h" + +namespace srsran { + +/// \brief Empty PUSCH decoder implementation. +/// +/// Implements the \ref pusch_decoder interface without any decoding capability. The decoder notifies the completion of +/// the data processing when the end of the soft bits is notified. +/// +/// It notifies a PUSCH decoder result without any statistics and unmatched CRC. +class pusch_decoder_empty_impl : public pusch_decoder +{ +public: + /// \brief Constructs an empty decoder. + /// + /// \param[in] nof_prb Resource grid maximum number of physical resource blocks. + /// \param[in] nof_layers Maximum number of layers. + pusch_decoder_empty_impl(unsigned nof_prb, unsigned nof_layers); + + // See interface for documentation. + pusch_decoder_buffer& new_data(span transport_block, + unique_rx_buffer rm_buffer, + pusch_decoder_notifier& notifier, + const configuration& cfg) override; + + // See interface for documentation. + void set_nof_softbits(units::bits nof_softbits) override; + +private: + /// Implements the decoder buffer interface. + class decoder_buffer_impl : public pusch_decoder_buffer + { + public: + /// Creates a decoder buffer. + decoder_buffer_impl(unsigned nof_prb, unsigned nof_layers) : + softbits_buffer(pusch_constants::get_max_codeword_size(nof_prb, nof_layers).value()) + { + } + + /// Configures the decoder buffer for a new transmission. + void new_data(span transport_block, + unique_rx_buffer rm_buffer, + pusch_decoder_notifier& notifier, + const configuration& cfg); + + // See interface for documentation. + span get_next_block_view(unsigned int block_size) override; + + // See interface for documentation. + void on_new_softbits(span softbits) override; + + // See interface for documentation. + void on_end_softbits() override; + + private: + /// Soft bit buffer. + std::vector softbits_buffer; + /// Current soft bits buffer. + unique_rx_buffer rm_buffer; + /// Current notifier. + pusch_decoder_notifier* notifier = nullptr; + /// Set to true if the current transmission is the first transmission and requires to reset the rate matching + /// buffer. + bool is_new_data = false; + /// Counts the number of soft bits in the buffer. + unsigned softbits_count = 0; + /// Number of codeblocks of the current transmission. + unsigned nof_codeblocks = 0; + /// Full code block size, it is used for setting the rate matching buffer to zero. + units::bits codeblock_size = units::bits(0); + }; + + /// Buffer instance. + decoder_buffer_impl decoder_buffer; +}; + +} // namespace srsran \ No newline at end of file diff --git a/lib/phy/upper/channel_processors/pusch/pusch_processor_pool.h b/lib/phy/upper/channel_processors/pusch/pusch_processor_pool.h index c2feea3d82..888c12b7f8 100644 --- a/lib/phy/upper/channel_processors/pusch/pusch_processor_pool.h +++ b/lib/phy/upper/channel_processors/pusch/pusch_processor_pool.h @@ -26,6 +26,7 @@ #include "srsran/phy/upper/channel_processors/pusch/formatters.h" #include "srsran/phy/upper/channel_processors/pusch/pusch_processor.h" #include "srsran/srslog/logger.h" +#include "srsran/support/memory_pool/concurrent_thread_local_object_pool.h" namespace srsran { @@ -109,8 +110,13 @@ class pusch_processor_pool : public pusch_processor { public: /// Creates a PUSCH processor pool from a list of processors. Ownership is transferred to the pool. - explicit pusch_processor_pool(span> processors_, bool blocking_) : - logger(srslog::fetch_basic_logger("PHY")), free_list(processors_.size()), blocking(blocking_) + pusch_processor_pool(std::vector> processors_, + std::vector> uci_processors_, + bool blocking_) : + logger(srslog::fetch_basic_logger("PHY")), + uci_processors(std::move(uci_processors_)), + free_list(processors_.size()), + blocking(blocking_) { unsigned index = 0; for (std::unique_ptr& processor : processors_) { @@ -134,6 +140,14 @@ class pusch_processor_pool : public pusch_processor // If no worker is available. if (!index.has_value()) { + // Process UCI synchronously if UCI is present. + if ((pdu.uci.nof_harq_ack > 0) || (pdu.uci.nof_csi_part1 > 0)) { + srslog::fetch_basic_logger("PHY").warning( + pdu.slot.sfn(), pdu.slot.slot_index(), "PUSCH processing queue is full. Processing UCI {:s}.", pdu); + uci_processors.get().process(data, std::move(rm_buffer), notifier, grid, pdu); + return; + } + logger.warning( pdu.slot.sfn(), pdu.slot.slot_index(), "PUSCH processing queue is full. Dropping PUSCH {:s}.", pdu); @@ -147,13 +161,6 @@ class pusch_processor_pool : public pusch_processor notifier.on_sch(discarded_results); } - // Report control-related discarded result if HARQ-ACK feedback is present. - if (pdu.uci.nof_harq_ack > 0) { - pusch_processor_result_control discarded_results; - discarded_results.harq_ack = pusch_uci_field{uci_payload_type(pdu.uci.nof_harq_ack), uci_status::unknown}; - discarded_results.csi = channel_state_information(); - notifier.on_uci(discarded_results); - } return; } @@ -162,10 +169,16 @@ class pusch_processor_pool : public pusch_processor } private: - srslog::basic_logger& logger; + /// Physical layer logger. + srslog::basic_logger& logger; + /// Actual PUSCH processor pool. std::vector processors; - detail::pusch_processor_free_list free_list; - bool blocking; + /// Synchronous PUSCH processor. It only processes UCI. + concurrent_thread_local_object_pool uci_processors; + /// List containing the indices of free PUSCH processors. + detail::pusch_processor_free_list free_list; + /// Set to true for blocking upon the the selection of a PUSCH processor. + bool blocking; }; } // namespace srsran diff --git a/lib/phy/upper/equalization/equalize_zf_1xn.h b/lib/phy/upper/equalization/equalize_zf_1xn.h index 48a5c19052..272612225b 100644 --- a/lib/phy/upper/equalization/equalize_zf_1xn.h +++ b/lib/phy/upper/equalization/equalize_zf_1xn.h @@ -53,7 +53,7 @@ void equalize_zf_1xn(span symbols_out, unsigned i_re = 0; -#if defined(__AVX2__) || defined(HAVE_NEON) +#if defined(__AVX2__) || defined(__ARM_NEON) // Views over the input data. std::array, MAX_PORTS> port_symbols; std::array, MAX_PORTS> port_ests; @@ -132,7 +132,7 @@ void equalize_zf_1xn(span symbols_out, // If abnormal calculation parameters are detected, the equalized symbols are set to zero. srsran_simd_cfi_storeu(symbols_out.data() + i_re, srsran_simd_cf_select(cf_zero, re_out, isnormal_mask)); } -#endif // __AVX2__ || HAVE_NEON +#endif // __AVX2__ || __ARM_NEON for (; i_re != nof_re; ++i_re) { float ch_mod_sq = 0.0F; diff --git a/lib/phy/upper/log_likelihood_ratio.cpp b/lib/phy/upper/log_likelihood_ratio.cpp index 90e731a887..bc2b84349f 100644 --- a/lib/phy/upper/log_likelihood_ratio.cpp +++ b/lib/phy/upper/log_likelihood_ratio.cpp @@ -28,9 +28,9 @@ #ifdef __AVX2__ #include #endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include -#endif // HAVE_NEON +#endif // __ARM_NEON using namespace srsran; @@ -172,7 +172,7 @@ static void hard_decision_simd(bit_buffer& hard_bits, const int8_t* soft_bits, u } #endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON static void hard_decision_simd(bit_buffer& hard_bits, const int8_t* soft_bits, unsigned len) { const uint8x16_t mask_msb_u8 = vdupq_n_u8(0x80); @@ -221,7 +221,7 @@ static void hard_decision_simd(bit_buffer& hard_bits, const int8_t* soft_bits, u hard_bits.insert(hard_bit, i_bit, 1); } } -#endif // HAVE_NEON +#endif // __ARM_NEON bool srsran::hard_decision(bit_buffer& hard_bits, span soft_bits) { @@ -233,7 +233,7 @@ bool srsran::hard_decision(bit_buffer& hard_bits, span(soft_bits.data()), nof_bits); @@ -245,7 +245,7 @@ bool srsran::hard_decision(bit_buffer& hard_bits, span create_ul_processor_factory(con std::shared_ptr pusch_factory = create_pusch_processor_factory_sw(pusch_config); report_fatal_error_if_not(pusch_factory, "Invalid PUSCH processor factory."); + // Create synchronous PUSCH processor for UCI only. + pusch_config.decoder_factory = + create_pusch_decoder_empty_factory(config.ul_bw_rb, pusch_config.ch_estimate_dimensions.nof_tx_layers); + report_fatal_error_if_not(pusch_config.decoder_factory, "Invalid PUSCH decoder factory for UCI."); + std::shared_ptr uci_proc_factory = create_pusch_processor_factory_sw(pusch_config); + report_fatal_error_if_not(uci_proc_factory, "Invalid PUSCH processor factory for UCI."); + // Create PUSCH processor pool factory. - pusch_factory = create_pusch_processor_pool(std::move(pusch_factory), config.max_pusch_concurrency); + pusch_processor_pool_factory_config pusch_proc_pool_config; + pusch_proc_pool_config.factory = pusch_factory; + pusch_proc_pool_config.uci_factory = uci_proc_factory; + pusch_proc_pool_config.nof_regular_processors = config.max_pusch_concurrency; + pusch_proc_pool_config.nof_uci_processors = config.max_ul_thread_concurrency; + pusch_proc_pool_config.blocking = false; + pusch_factory = create_pusch_processor_pool(pusch_proc_pool_config); report_fatal_error_if_not(pusch_factory, "Invalid PUSCH processor pool factory."); std::shared_ptr lpg_factory = create_low_papr_sequence_generator_sw_factory(); @@ -714,12 +727,12 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw // Create channel processors - PDSCH std::shared_ptr pdsch_proc_factory; - if (variant_holds_alternative(config.pdsch_processor)) { + if (std::holds_alternative(config.pdsch_processor)) { pdsch_proc_factory = create_pdsch_processor_factory_sw(pdsch_enc_factory, pdsch_mod_factory, dmrs_pdsch_proc_factory); - } else if (variant_holds_alternative(config.pdsch_processor)) { + } else if (std::holds_alternative(config.pdsch_processor)) { const pdsch_processor_concurrent_configuration& pdsch_processor_config = - variant_get(config.pdsch_processor); + std::get(config.pdsch_processor); report_fatal_error_if_not(pdsch_processor_config.nof_pdsch_codeblock_threads >= 2, "The number of threads (i.e., {}) must be equal to or greater than 2."); report_fatal_error_if_not(pdsch_processor_config.pdsch_codeblock_task_executor != nullptr, @@ -736,7 +749,7 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw *pdsch_processor_config.pdsch_codeblock_task_executor, pdsch_processor_config.nof_pdsch_codeblock_threads); report_fatal_error_if_not(pdsch_proc_factory, "Invalid PDSCH processor factory."); - } else if (variant_holds_alternative(config.pdsch_processor)) { + } else if (std::holds_alternative(config.pdsch_processor)) { pdsch_proc_factory = create_pdsch_lite_processor_factory_sw( ldpc_seg_tx_factory, ldpc_enc_factory, ldpc_rm_factory, prg_factory, mod_factory, dmrs_pdsch_proc_factory); } @@ -761,9 +774,9 @@ srsran::create_downlink_processor_factory_sw(const downlink_processor_factory_sw if (config.nof_concurrent_threads > 1) { // If the PDSCH instance is concurrent, the number of instances is given by the PDSCH processor configuration. unsigned max_nof_simultaneous_pdsch = config.nof_concurrent_threads; - if (variant_holds_alternative(config.pdsch_processor)) { + if (std::holds_alternative(config.pdsch_processor)) { max_nof_simultaneous_pdsch = - variant_get(config.pdsch_processor).max_nof_simultaneous_pdsch; + std::get(config.pdsch_processor).max_nof_simultaneous_pdsch; } pdcch_proc_factory = diff --git a/lib/phy/upper/upper_phy_impl.h b/lib/phy/upper/upper_phy_impl.h index eac43bfbba..2d123a7284 100644 --- a/lib/phy/upper/upper_phy_impl.h +++ b/lib/phy/upper/upper_phy_impl.h @@ -112,6 +112,9 @@ class upper_phy_impl : public upper_phy /// Constructs an upper PHY implementation object with the given configuration. explicit upper_phy_impl(upper_phy_impl_config&& config); + // See interface for documentation. + unsigned get_sector_id() const override { return sector_id; } + // See interface for documentation. upper_phy_error_handler& get_error_handler() override; diff --git a/lib/phy/upper/upper_phy_rx_symbol_handler_impl.cpp b/lib/phy/upper/upper_phy_rx_symbol_handler_impl.cpp index 38350f78c5..eacaf37c20 100644 --- a/lib/phy/upper/upper_phy_rx_symbol_handler_impl.cpp +++ b/lib/phy/upper/upper_phy_rx_symbol_handler_impl.cpp @@ -64,15 +64,12 @@ void upper_phy_rx_symbol_handler_impl::handle_rx_symbol(const upper_phy_rx_symbo // Process all the PDUs taken from the repository. for (const auto& pdu : pdus) { - if (variant_holds_alternative(pdu)) { - const auto& pusch_pdu = variant_get(pdu); - process_pusch(pusch_pdu, ul_processor, grid, context.slot); - } else if (variant_holds_alternative(pdu)) { - const auto& pucch_pdu = variant_get(pdu); - ul_processor.process_pucch(rx_results_notifier, grid, pucch_pdu); - } else if (variant_holds_alternative(pdu)) { - const auto& srs_pdu = variant_get(pdu); - ul_processor.process_srs(rx_results_notifier, grid, srs_pdu); + if (const auto* pusch_pdu = std::get_if(&pdu)) { + process_pusch(*pusch_pdu, ul_processor, grid, context.slot); + } else if (const auto* pucch_pdu = std::get_if(&pdu)) { + ul_processor.process_pucch(rx_results_notifier, grid, *pucch_pdu); + } else if (const auto* srs_pdu = std::get_if(&pdu)) { + ul_processor.process_srs(rx_results_notifier, grid, *srs_pdu); } } } diff --git a/lib/radio/uhd/radio_uhd_device.h b/lib/radio/uhd/radio_uhd_device.h index 6c429eef06..b95b45dc80 100644 --- a/lib/radio/uhd/radio_uhd_device.h +++ b/lib/radio/uhd/radio_uhd_device.h @@ -131,6 +131,10 @@ class radio_uhd_device : public uhd_exception_handler } break; case radio_uhd_device_type::types::B2xx: + if (!device_addr.has_key("master_clock_rate")) { + automatic_master_clock_rate = true; + } + break; case radio_uhd_device_type::types::UNKNOWN: default: // No default parameters are required. @@ -216,6 +220,11 @@ class radio_uhd_device : public uhd_exception_handler } bool set_automatic_master_clock_rate(double srate_Hz) { + // Skip automatic master clock rate if it is not available. + if (!automatic_master_clock_rate) { + return true; + } + return safe_execution([this, &srate_Hz]() { // Get range of valid master clock rates. uhd::meta_range_t range = usrp->get_master_clock_rate_range(); @@ -440,8 +449,9 @@ class radio_uhd_device : public uhd_exception_handler } private: - uhd::usrp::multi_usrp::sptr usrp = nullptr; - radio_uhd_device_type type = radio_uhd_device_type::types::UNKNOWN; + uhd::usrp::multi_usrp::sptr usrp = nullptr; + radio_uhd_device_type type = radio_uhd_device_type::types::UNKNOWN; + bool automatic_master_clock_rate = false; srslog::basic_logger& logger; }; diff --git a/lib/radio/uhd/radio_uhd_impl.cpp b/lib/radio/uhd/radio_uhd_impl.cpp index 30aea8d8a5..0c45d31852 100644 --- a/lib/radio/uhd/radio_uhd_impl.cpp +++ b/lib/radio/uhd/radio_uhd_impl.cpp @@ -263,12 +263,9 @@ radio_session_uhd_impl::radio_session_uhd_impl(const radio_configuration::radio& return; } - static const std::set automatic_mcr_devices = {radio_uhd_device_type::types::B2xx}; - if (automatic_mcr_devices.count(device.get_type())) { - if (!device.set_automatic_master_clock_rate(radio_config.sampling_rate_hz)) { - fmt::print("Error setting master clock rate. {}\n", device.get_error_message()); - return; - } + if (!device.set_automatic_master_clock_rate(radio_config.sampling_rate_hz)) { + fmt::print("Error setting master clock rate. {}\n", device.get_error_message()); + return; } // Set sync source. diff --git a/lib/ran/cause/e1ap_cause_converters.cpp b/lib/ran/cause/e1ap_cause_converters.cpp index ed39fb85de..cca5a990a2 100644 --- a/lib/ran/cause/e1ap_cause_converters.cpp +++ b/lib/ran/cause/e1ap_cause_converters.cpp @@ -25,7 +25,7 @@ using namespace srsran; -const uint8_t e1ap_to_ngap_cause_radio_network[] = { +static constexpr uint8_t e1ap_to_ngap_cause_radio_network[] = { (uint8_t)ngap_cause_radio_network_t::unspecified, // unspecified (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_cp_ue_e1ap_id (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_up_ue_e1ap_id @@ -65,13 +65,13 @@ const uint8_t e1ap_to_ngap_cause_radio_network[] = { (uint8_t)ngap_cause_radio_network_t::unspecified // meas_not_supported_for_the_obj }; -const uint8_t e1ap_to_ngap_cause_transport[] = { +static constexpr uint8_t e1ap_to_ngap_cause_transport[] = { (uint8_t)ngap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable (uint8_t)ngap_cause_transport_t::unspecified, // unspecified (uint8_t)ngap_cause_transport_t::unspecified // unknown_tnl_address_for_iab }; -const uint8_t e1ap_to_ngap_cause_misc[] = { +static constexpr uint8_t e1ap_to_ngap_cause_misc[] = { (uint8_t)ngap_cause_misc_t::ctrl_processing_overload, // ctrl_processing_overload (uint8_t)ngap_cause_misc_t::not_enough_user_plane_processing_res, // not_enough_user_plane_processing_res (uint8_t)ngap_cause_misc_t::hardware_fail, // hardware_fail @@ -81,21 +81,19 @@ const uint8_t e1ap_to_ngap_cause_misc[] = { ngap_cause_t srsran::e1ap_to_ngap_cause(e1ap_cause_t e1ap_cause) { - ngap_cause_t ngap_cause; - - if (variant_holds_alternative(e1ap_cause)) { - ngap_cause = ngap_cause_radio_network_t( - e1ap_to_ngap_cause_radio_network[uint8_t(variant_get(e1ap_cause))]); - } else if (variant_holds_alternative(e1ap_cause)) { - ngap_cause = - ngap_cause_transport_t(e1ap_to_ngap_cause_transport[uint8_t(variant_get(e1ap_cause))]); - } else if (variant_holds_alternative(e1ap_cause)) { - ngap_cause = variant_get(e1ap_cause); - } else if (variant_holds_alternative(e1ap_cause)) { - ngap_cause = ngap_cause_misc_t(e1ap_to_ngap_cause_misc[uint8_t(variant_get(e1ap_cause))]); - } else { - report_fatal_error("Cannot convert cause to NGAP type: {}", e1ap_cause); + if (const auto* result = std::get_if(&e1ap_cause)) { + return ngap_cause_radio_network_t(e1ap_to_ngap_cause_radio_network[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&e1ap_cause)) { + return ngap_cause_transport_t(e1ap_to_ngap_cause_transport[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&e1ap_cause)) { + return *result; + } + if (const auto* result = std::get_if(&e1ap_cause)) { + return ngap_cause_misc_t(e1ap_to_ngap_cause_misc[uint8_t(*result)]); } - return ngap_cause; -}; + report_fatal_error("Cannot convert cause to NGAP type: {}", e1ap_cause); + return {}; +} diff --git a/lib/ran/cause/f1ap_cause_converters.cpp b/lib/ran/cause/f1ap_cause_converters.cpp index 4563c67936..b31d8cfe4e 100644 --- a/lib/ran/cause/f1ap_cause_converters.cpp +++ b/lib/ran/cause/f1ap_cause_converters.cpp @@ -25,7 +25,7 @@ using namespace srsran; -const uint8_t f1ap_to_ngap_cause_radio_network[] = { +static constexpr uint8_t f1ap_to_ngap_cause_radio_network[] = { (uint8_t)ngap_cause_radio_network_t::unspecified, // unspecified (uint8_t)ngap_cause_radio_network_t::radio_conn_with_ue_lost, // rl_fail_rlc (uint8_t)ngap_cause_radio_network_t::unspecified, // unknown_or_already_allocated_gnb_cu_ue_f1ap_id @@ -69,14 +69,14 @@ const uint8_t f1ap_to_ngap_cause_radio_network[] = { (uint8_t)ngap_cause_radio_network_t::unspecified // tat_sdt_expiry }; -const uint8_t f1ap_to_ngap_cause_transport[] = { +static constexpr uint8_t f1ap_to_ngap_cause_transport[] = { (uint8_t)ngap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable (uint8_t)ngap_cause_transport_t::unspecified, // unspecified (uint8_t)ngap_cause_transport_t::unspecified, // unknown_tnl_address_for_iab (uint8_t)ngap_cause_transport_t::unspecified, // unknown_up_tnl_info_for_iab }; -const uint8_t f1ap_to_ngap_cause_misc[] = { +static constexpr uint8_t f1ap_to_ngap_cause_misc[] = { (uint8_t)ngap_cause_misc_t::ctrl_processing_overload, // ctrl_processing_overload (uint8_t)ngap_cause_misc_t::not_enough_user_plane_processing_res, // not_enough_user_plane_processing_res (uint8_t)ngap_cause_misc_t::hardware_fail, // hardware_fail @@ -88,19 +88,23 @@ ngap_cause_t srsran::f1ap_to_ngap_cause(f1ap_cause_t f1ap_cause) { ngap_cause_t ngap_cause; - if (variant_holds_alternative(f1ap_cause)) { - ngap_cause = ngap_cause_radio_network_t( - f1ap_to_ngap_cause_radio_network[uint8_t(variant_get(f1ap_cause))]); - } else if (variant_holds_alternative(f1ap_cause)) { - ngap_cause = - ngap_cause_transport_t(f1ap_to_ngap_cause_transport[uint8_t(variant_get(f1ap_cause))]); - } else if (variant_holds_alternative(f1ap_cause)) { - ngap_cause = variant_get(f1ap_cause); - } else if (variant_holds_alternative(f1ap_cause)) { - ngap_cause = ngap_cause_misc_t(f1ap_to_ngap_cause_misc[uint8_t(variant_get(f1ap_cause))]); - } else { - report_fatal_error("Cannot convert cause to F1AP type: {}", f1ap_cause); + if (const auto* result = std::get_if(&f1ap_cause)) { + ngap_cause = ngap_cause_radio_network_t(f1ap_to_ngap_cause_radio_network[uint8_t(*result)]); + return ngap_cause; + } + if (const auto* result = std::get_if(&f1ap_cause)) { + ngap_cause = ngap_cause_transport_t(f1ap_to_ngap_cause_transport[uint8_t(*result)]); + return ngap_cause; + } + if (const auto* result = std::get_if(&f1ap_cause)) { + ngap_cause = *result; + return ngap_cause; + } + if (const auto* result = std::get_if(&f1ap_cause)) { + ngap_cause = ngap_cause_misc_t(f1ap_to_ngap_cause_misc[uint8_t(*result)]); + return ngap_cause; } + report_fatal_error("Cannot convert cause to F1AP type: {}", f1ap_cause); return ngap_cause; -}; +} diff --git a/lib/ran/cause/ngap_cause_converters.cpp b/lib/ran/cause/ngap_cause_converters.cpp index 8c51416bbf..fc8c1aa442 100644 --- a/lib/ran/cause/ngap_cause_converters.cpp +++ b/lib/ran/cause/ngap_cause_converters.cpp @@ -25,7 +25,7 @@ using namespace srsran; -const uint8_t ngap_to_f1ap_cause_radio_network[] = { +static constexpr uint8_t ngap_to_f1ap_cause_radio_network[] = { (uint8_t)f1ap_cause_radio_network_t::unspecified, // unspecified (uint8_t)f1ap_cause_radio_network_t::unspecified, // txnrelocoverall_expiry (uint8_t)f1ap_cause_radio_network_t::unspecified, // successful_ho @@ -85,12 +85,12 @@ const uint8_t ngap_to_f1ap_cause_radio_network[] = { (uint8_t)f1ap_cause_radio_network_t::unspecified // misaligned_assoc_for_multicast_unicast }; -const uint8_t ngap_to_f1ap_cause_transport[] = { +static constexpr uint8_t ngap_to_f1ap_cause_transport[] = { (uint8_t)f1ap_cause_transport_t::transport_res_unavailable, // transport_res_unavailable (uint8_t)f1ap_cause_transport_t::unspecified // unspecified }; -const uint8_t ngap_to_f1ap_cause_misc[] = { +static constexpr uint8_t ngap_to_f1ap_cause_misc[] = { (uint8_t)cause_misc_t::unspecified, // ctrl_processing_overload (uint8_t)cause_misc_t::unspecified, // not_enough_user_plane_processing_res (uint8_t)cause_misc_t::unspecified, // hardware_fail @@ -101,43 +101,38 @@ const uint8_t ngap_to_f1ap_cause_misc[] = { f1ap_cause_t srsran::ngap_to_f1ap_cause(ngap_cause_t ngap_cause) { - f1ap_cause_t f1ap_cause; - - if (variant_holds_alternative(ngap_cause)) { - f1ap_cause = f1ap_cause_radio_network_t( - ngap_to_f1ap_cause_radio_network[uint8_t(variant_get(ngap_cause))]); - } else if (variant_holds_alternative(ngap_cause)) { - f1ap_cause = - f1ap_cause_transport_t(ngap_to_f1ap_cause_transport[uint8_t(variant_get(ngap_cause))]); - } else if (variant_holds_alternative(ngap_cause)) { - switch (variant_get(ngap_cause)) { + if (const auto* result = std::get_if(&ngap_cause)) { + return f1ap_cause_radio_network_t(ngap_to_f1ap_cause_radio_network[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&ngap_cause)) { + return f1ap_cause_transport_t(ngap_to_f1ap_cause_transport[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&ngap_cause)) { + switch (*result) { case cause_nas_t::normal_release: - f1ap_cause = f1ap_cause_radio_network_t::normal_release; - break; + return f1ap_cause_radio_network_t::normal_release; case cause_nas_t::authentication_fail: - f1ap_cause = f1ap_cause_radio_network_t::unspecified; - break; + return f1ap_cause_radio_network_t::unspecified; case cause_nas_t::deregister: - f1ap_cause = f1ap_cause_radio_network_t::normal_release; - break; + return f1ap_cause_radio_network_t::normal_release; case cause_nas_t::unspecified: - f1ap_cause = f1ap_cause_radio_network_t::unspecified; - break; + return f1ap_cause_radio_network_t::unspecified; default: report_fatal_error("Cannot convert cause to F1AP type: {}", ngap_cause); } - } else if (variant_holds_alternative(ngap_cause)) { - f1ap_cause = variant_get(ngap_cause); - } else if (variant_holds_alternative(ngap_cause)) { - f1ap_cause = cause_misc_t(ngap_to_f1ap_cause_misc[uint8_t(variant_get(ngap_cause))]); - } else { - report_fatal_error("Cannot convert cause to F1AP type: {}", ngap_cause); + } + if (const auto* result = std::get_if(&ngap_cause)) { + return *result; + } + if (const auto* result = std::get_if(&ngap_cause)) { + return cause_misc_t(ngap_to_f1ap_cause_misc[uint8_t(*result)]); } - return f1ap_cause; -}; + report_fatal_error("Cannot convert cause to F1AP type: {}", ngap_cause); + return {}; +} -const uint8_t ngap_to_e1ap_cause_radio_network[] = { +static constexpr uint8_t ngap_to_e1ap_cause_radio_network[] = { (uint8_t)e1ap_cause_radio_network_t::unspecified, // unspecified (uint8_t)e1ap_cause_radio_network_t::unspecified, // txnrelocoverall_expiry (uint8_t)e1ap_cause_radio_network_t::unspecified, // successful_ho @@ -213,38 +208,33 @@ const uint8_t ngap_to_e1ap_cause_misc[] = { e1ap_cause_t srsran::ngap_to_e1ap_cause(ngap_cause_t ngap_cause) { - e1ap_cause_t e1ap_cause; - - if (variant_holds_alternative(ngap_cause)) { - e1ap_cause = e1ap_cause_radio_network_t( - ngap_to_e1ap_cause_radio_network[uint8_t(variant_get(ngap_cause))]); - } else if (variant_holds_alternative(ngap_cause)) { - e1ap_cause = - e1ap_cause_transport_t(ngap_to_e1ap_cause_transport[uint8_t(variant_get(ngap_cause))]); - } else if (variant_holds_alternative(ngap_cause)) { - switch (variant_get(ngap_cause)) { + if (const auto* result = std::get_if(&ngap_cause)) { + return e1ap_cause_radio_network_t(ngap_to_e1ap_cause_radio_network[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&ngap_cause)) { + return e1ap_cause_transport_t(ngap_to_e1ap_cause_transport[uint8_t(*result)]); + } + if (const auto* result = std::get_if(&ngap_cause)) { + switch (*result) { case cause_nas_t::normal_release: - e1ap_cause = e1ap_cause_radio_network_t::normal_release; - break; + return e1ap_cause_radio_network_t::normal_release; case cause_nas_t::authentication_fail: - e1ap_cause = e1ap_cause_radio_network_t::unspecified; - break; + return e1ap_cause_radio_network_t::unspecified; case cause_nas_t::deregister: - e1ap_cause = e1ap_cause_radio_network_t::normal_release; - break; + return e1ap_cause_radio_network_t::normal_release; case cause_nas_t::unspecified: - e1ap_cause = e1ap_cause_radio_network_t::unspecified; - break; + return e1ap_cause_radio_network_t::unspecified; default: report_fatal_error("Cannot convert cause to E1AP type: {}", ngap_cause); } - } else if (variant_holds_alternative(ngap_cause)) { - e1ap_cause = variant_get(ngap_cause); - } else if (variant_holds_alternative(ngap_cause)) { - e1ap_cause = cause_misc_t(ngap_to_e1ap_cause_misc[uint8_t(variant_get(ngap_cause))]); - } else { - report_fatal_error("Cannot convert cause to E1AP type: {}", ngap_cause); + } + if (const auto* result = std::get_if(&ngap_cause)) { + return *result; + } + if (const auto* result = std::get_if(&ngap_cause)) { + return cause_misc_t(ngap_to_e1ap_cause_misc[uint8_t(*result)]); } - return e1ap_cause; -}; + report_fatal_error("Cannot convert cause to E1AP type: {}", ngap_cause); + return {}; +} diff --git a/lib/ran/csi_report/csi_report_config_helpers.cpp b/lib/ran/csi_report/csi_report_config_helpers.cpp index 8bc87feb8b..249ea2dfe7 100644 --- a/lib/ran/csi_report/csi_report_config_helpers.cpp +++ b/lib/ran/csi_report/csi_report_config_helpers.cpp @@ -35,7 +35,7 @@ csi_report_configuration srsran::create_csi_report_configuration(const csi_meas_ // TODO: support more CSI resource sets. nzp_csi_rs_res_set_id_t nzp_csi_set_id = - variant_get( + std::get( csi_meas.csi_res_cfg_list[csi_rep_cfg.res_for_channel_meas].csi_rs_res_set_list) .nzp_csi_rs_res_set_list[0]; csi_rep.nof_csi_rs_resources = csi_meas.nzp_csi_rs_res_set_list[nzp_csi_set_id].nzp_csi_rs_res.size(); @@ -59,22 +59,20 @@ csi_report_configuration srsran::create_csi_report_configuration(const csi_meas_ } if (csi_rep_cfg.codebook_cfg.has_value()) { - if (variant_holds_alternative(csi_rep_cfg.codebook_cfg->codebook_type)) { - const auto& type1 = variant_get(csi_rep_cfg.codebook_cfg->codebook_type); - if (variant_holds_alternative(type1.sub_type)) { + if (const auto* type1 = std::get_if(&csi_rep_cfg.codebook_cfg->codebook_type)) { + if (const auto* panel = std::get_if(&type1->sub_type)) { using single_panel = codebook_config::type1::single_panel; - const auto& panel = variant_get(type1.sub_type); - if (variant_holds_alternative( - panel.nof_antenna_ports)) { + if (std::holds_alternative( + panel->nof_antenna_ports)) { csi_rep.pmi_codebook = pmi_codebook_type::two; - } else if (variant_holds_alternative(panel.nof_antenna_ports)) { + } else if (std::holds_alternative(panel->nof_antenna_ports)) { csi_rep.pmi_codebook = pmi_codebook_type::typeI_single_panel_4ports_mode1; } else { csi_rep.pmi_codebook = pmi_codebook_type::other; } - csi_rep.ri_restriction = panel.typei_single_panel_ri_restriction; + csi_rep.ri_restriction = panel->typei_single_panel_ri_restriction; } else { report_fatal_error("Codebook panel type not supported"); } @@ -125,6 +123,6 @@ bool srsran::is_valid(const csi_report_configuration& config) bool srsran::is_pusch_configured(const csi_meas_config& csi_meas) { srsran_assert(csi_meas.csi_report_cfg_list.size() == 1, "Only one CSI report configuration is supported"); - return not variant_holds_alternative( + return not std::holds_alternative( csi_meas.csi_report_cfg_list[0].report_cfg_type); } diff --git a/lib/ran/pdcch/search_space.cpp b/lib/ran/pdcch/search_space.cpp index a6e572a6d2..340257839d 100644 --- a/lib/ran/pdcch/search_space.cpp +++ b/lib/ran/pdcch/search_space.cpp @@ -75,15 +75,15 @@ search_space_configuration::search_space_configuration(nr_band band, } search_space_configuration::search_space_configuration( - search_space_id id_, - coreset_id cs_id_, - std::array nof_candidates_, - variant dci_fmt_, - unsigned monitoring_slot_periodicity_, - unsigned monitoring_slot_offset_, - subcarrier_spacing scs_common, - unsigned duration_, - monitoring_symbols_within_slot_t monitoring_symbols_within_slot_) : + search_space_id id_, + coreset_id cs_id_, + std::array nof_candidates_, + std::variant dci_fmt_, + unsigned monitoring_slot_periodicity_, + unsigned monitoring_slot_offset_, + subcarrier_spacing scs_common, + unsigned duration_, + monitoring_symbols_within_slot_t monitoring_symbols_within_slot_) : id(id_), cs_id(cs_id_), nof_candidates(nof_candidates_), diff --git a/lib/ran/prach/prach_preamble_information.cpp b/lib/ran/prach/prach_preamble_information.cpp index 7d298d4ac1..fb9b3b3fdf 100644 --- a/lib/ran/prach/prach_preamble_information.cpp +++ b/lib/ran/prach/prach_preamble_information.cpp @@ -32,30 +32,14 @@ prach_preamble_information srsran::get_prach_preamble_long_info(prach_format_typ srsran_assert(is_long_preamble(format), "Invalid preamble format. It must be a long preamble."); switch (format) { case prach_format_type::zero: - return {839U, - prach_subcarrier_spacing::kHz1_25, - phy_time_unit::from_units_of_kappa(24576), - phy_time_unit::from_units_of_kappa(3168), - true}; + return {839U, prach_subcarrier_spacing::kHz1_25, 1, phy_time_unit::from_units_of_kappa(3168), true}; case prach_format_type::one: - return {839U, - prach_subcarrier_spacing::kHz1_25, - phy_time_unit::from_units_of_kappa(2 * 24576), - phy_time_unit::from_units_of_kappa(21024), - true}; + return {839U, prach_subcarrier_spacing::kHz1_25, 2, phy_time_unit::from_units_of_kappa(21024), true}; case prach_format_type::two: - return {839U, - prach_subcarrier_spacing::kHz1_25, - phy_time_unit::from_units_of_kappa(4 * 24576), - phy_time_unit::from_units_of_kappa(4688), - true}; + return {839U, prach_subcarrier_spacing::kHz1_25, 4, phy_time_unit::from_units_of_kappa(4688), true}; case prach_format_type::three: default: - return {839U, - prach_subcarrier_spacing::kHz5, - phy_time_unit::from_units_of_kappa(4 * 6144), - phy_time_unit::from_units_of_kappa(3168), - true}; + return {839U, prach_subcarrier_spacing::kHz5, 4, phy_time_unit::from_units_of_kappa(3168), true}; } } @@ -73,72 +57,67 @@ srsran::get_prach_preamble_short_info(prach_format_type format, prach_subcarrier info.scs = ra_scs; info.support_restricted_sets = false; - unsigned numerology = to_numerology_value(ra_scs); - phy_time_unit symbol_length_x0_kappa = phy_time_unit::from_units_of_kappa((1U * 2048) >> numerology); - phy_time_unit symbol_length_x1_kappa = phy_time_unit::from_units_of_kappa((2U * 2048) >> numerology); - phy_time_unit symbol_length_x2_kappa = phy_time_unit::from_units_of_kappa((4U * 2048) >> numerology); - phy_time_unit symbol_length_x3_kappa = phy_time_unit::from_units_of_kappa((6U * 2048) >> numerology); - phy_time_unit symbol_length_x4_kappa = phy_time_unit::from_units_of_kappa((12U * 2048) >> numerology); - phy_time_unit cp_length_A1_kappa = phy_time_unit::from_units_of_kappa(288U >> numerology); - phy_time_unit cp_length_A2_kappa = phy_time_unit::from_units_of_kappa(576U >> numerology); - phy_time_unit cp_length_A3_kappa = phy_time_unit::from_units_of_kappa(864U >> numerology); - phy_time_unit cp_length_B1_kappa = phy_time_unit::from_units_of_kappa(216U >> numerology); - phy_time_unit cp_length_B2_kappa = phy_time_unit::from_units_of_kappa(360U >> numerology); - phy_time_unit cp_length_B3_kappa = phy_time_unit::from_units_of_kappa(504U >> numerology); - phy_time_unit cp_length_B4_kappa = phy_time_unit::from_units_of_kappa(936U >> numerology); - phy_time_unit cp_length_C0_kappa = phy_time_unit::from_units_of_kappa(1240U >> numerology); - phy_time_unit cp_length_C2_kappa = phy_time_unit::from_units_of_kappa(2048U >> numerology); + unsigned numerology = to_numerology_value(ra_scs); + phy_time_unit cp_length_A1_kappa = phy_time_unit::from_units_of_kappa(288U >> numerology); + phy_time_unit cp_length_A2_kappa = phy_time_unit::from_units_of_kappa(576U >> numerology); + phy_time_unit cp_length_A3_kappa = phy_time_unit::from_units_of_kappa(864U >> numerology); + phy_time_unit cp_length_B1_kappa = phy_time_unit::from_units_of_kappa(216U >> numerology); + phy_time_unit cp_length_B2_kappa = phy_time_unit::from_units_of_kappa(360U >> numerology); + phy_time_unit cp_length_B3_kappa = phy_time_unit::from_units_of_kappa(504U >> numerology); + phy_time_unit cp_length_B4_kappa = phy_time_unit::from_units_of_kappa(936U >> numerology); + phy_time_unit cp_length_C0_kappa = phy_time_unit::from_units_of_kappa(1240U >> numerology); + phy_time_unit cp_length_C2_kappa = phy_time_unit::from_units_of_kappa(2048U >> numerology); switch (format) { case prach_format_type::A1: - info.symbol_length = symbol_length_x1_kappa; - info.cp_length = cp_length_A1_kappa; + info.nof_symbols = 2U; + info.cp_length = cp_length_A1_kappa; break; case prach_format_type::A2: - info.symbol_length = symbol_length_x2_kappa; - info.cp_length = cp_length_A2_kappa; + info.nof_symbols = 4U; + info.cp_length = cp_length_A2_kappa; break; case prach_format_type::A3: - info.symbol_length = symbol_length_x3_kappa; - info.cp_length = cp_length_A3_kappa; + info.nof_symbols = 6U; + info.cp_length = cp_length_A3_kappa; break; case prach_format_type::B1: - info.symbol_length = symbol_length_x1_kappa; - info.cp_length = cp_length_B1_kappa; + info.nof_symbols = 2U; + info.cp_length = cp_length_B1_kappa; break; case prach_format_type::B4: - info.symbol_length = symbol_length_x4_kappa; - info.cp_length = cp_length_B4_kappa; + info.nof_symbols = 12U; + info.cp_length = cp_length_B4_kappa; break; case prach_format_type::C0: - info.symbol_length = symbol_length_x0_kappa; - info.cp_length = cp_length_C0_kappa; + info.nof_symbols = 1U; + info.cp_length = cp_length_C0_kappa; break; case prach_format_type::C2: - info.symbol_length = symbol_length_x2_kappa; - info.cp_length = cp_length_C2_kappa; + info.nof_symbols = 4U; + info.cp_length = cp_length_C2_kappa; break; case prach_format_type::A1_B1: - info.symbol_length = symbol_length_x1_kappa; - info.cp_length = (last_occasion) ? cp_length_B1_kappa : cp_length_A1_kappa; + info.nof_symbols = 2U; + info.cp_length = (last_occasion) ? cp_length_B1_kappa : cp_length_A1_kappa; break; case prach_format_type::A2_B2: - info.symbol_length = symbol_length_x2_kappa; - info.cp_length = (last_occasion) ? cp_length_B2_kappa : cp_length_A2_kappa; + info.nof_symbols = 4U; + info.cp_length = (last_occasion) ? cp_length_B2_kappa : cp_length_A2_kappa; break; case prach_format_type::A3_B3: default: - info.symbol_length = symbol_length_x3_kappa; - info.cp_length = (last_occasion) ? cp_length_B3_kappa : cp_length_A3_kappa; + info.nof_symbols = 6U; + info.cp_length = (last_occasion) ? cp_length_B3_kappa : cp_length_A3_kappa; break; } return info; } -phy_time_unit srsran::get_prach_window_duration(srsran::prach_format_type format, - srsran::subcarrier_spacing pusch_scs, - unsigned start_symbol_index, - unsigned nof_td_occasions) +phy_time_unit srsran::get_prach_window_duration(prach_format_type format, + subcarrier_spacing pusch_scs, + unsigned start_symbol_index, + unsigned nof_td_occasions) { // Cyclic prefix extension for short preambles at 0 and 0.5 ms from the start of the subframe. static constexpr phy_time_unit sixteen_kappa = phy_time_unit::from_units_of_kappa(16); @@ -180,7 +159,7 @@ phy_time_unit srsran::get_prach_window_duration(srsran::prach_format_type forma } } else { prach_preamble_information preamble_info = get_prach_preamble_long_info(format); - t_end = t_start + preamble_info.cp_length + preamble_info.symbol_length; + t_end = t_start + preamble_info.cp_length + preamble_info.symbol_length(); // Round to t_end to the next subframe for long preambles. t_end = phy_time_unit::from_seconds(1e-3 * std::ceil(t_end.to_seconds() * 1e3)); @@ -206,7 +185,7 @@ prach_symbols_slots_duration srsran::get_prach_duration_info(const prach_configu // Derive PRACH subcarrier spacing and other parameters. const prach_preamble_information info = get_prach_preamble_long_info(prach_cfg.format); - const double length_msecs = (info.cp_length.to_seconds() + info.symbol_length.to_seconds()) * 1000; + const double length_msecs = (info.cp_length.to_seconds() + info.symbol_length().to_seconds()) * 1000; output.nof_symbols = ceil(length_msecs / symbol_duration_msec); // Map the starting symbol with from the SCS 15kHz FR1 reference for PRACH into PUSCH SCS. const unsigned start_symbol_pusch_scs_in_subframe = diff --git a/lib/rlc/rlc_am_entity.h b/lib/rlc/rlc_am_entity.h index 209ccc8adf..892c1e57b8 100644 --- a/lib/rlc/rlc_am_entity.h +++ b/lib/rlc/rlc_am_entity.h @@ -31,7 +31,7 @@ namespace srsran { class rlc_am_entity : public rlc_base_entity { public: - rlc_am_entity(uint32_t du_index_, + rlc_am_entity(gnb_du_id_t gnb_du_id_, du_ue_index_t ue_index_, rb_id_t rb_id_, const rlc_am_config& config, @@ -45,7 +45,7 @@ class rlc_am_entity : public rlc_base_entity task_executor& pcell_executor, task_executor& ue_executor, rlc_pcap& pcap) : - rlc_base_entity(du_index_, + rlc_base_entity(gnb_du_id_, ue_index_, rb_id_, metrics_period_, @@ -53,7 +53,7 @@ class rlc_am_entity : public rlc_base_entity timer_factory{timers, ue_executor}) { // Create AM entities - std::unique_ptr tx_am = std::make_unique(du_index_, + std::unique_ptr tx_am = std::make_unique(gnb_du_id_, ue_index_, rb_id_, config.tx, @@ -65,7 +65,7 @@ class rlc_am_entity : public rlc_base_entity ue_executor, metrics_period.count() != 0, pcap); - std::unique_ptr rx_am = std::make_unique(du_index_, + std::unique_ptr rx_am = std::make_unique(gnb_du_id_, ue_index_, rb_id_, config.rx, diff --git a/lib/rlc/rlc_base_entity.h b/lib/rlc/rlc_base_entity.h index e8fa9e1947..bfe91637be 100644 --- a/lib/rlc/rlc_base_entity.h +++ b/lib/rlc/rlc_base_entity.h @@ -39,13 +39,13 @@ namespace srsran { class rlc_base_entity : public rlc_entity { public: - rlc_base_entity(uint32_t du_index_, + rlc_base_entity(gnb_du_id_t gnb_du_id_, du_ue_index_t ue_index_, rb_id_t rb_id_, timer_duration metrics_period_, rlc_metrics_notifier* rlc_metrics_notifier_, timer_factory timers) : - logger("RLC", {du_index_, ue_index_, rb_id_, "DL/UL"}), + logger("RLC", {gnb_du_id_, ue_index_, rb_id_, "DL/UL"}), ue_index(ue_index_), rb_id(rb_id_), metrics_period(metrics_period_), diff --git a/lib/rlc/rlc_bearer_logger.h b/lib/rlc/rlc_bearer_logger.h index 9d2a153e5d..8c3e4498f5 100644 --- a/lib/rlc/rlc_bearer_logger.h +++ b/lib/rlc/rlc_bearer_logger.h @@ -21,6 +21,7 @@ */ #pragma once +#include "srsran/ran/gnb_du_id.h" #include "srsran/ran/lcid.h" #include "srsran/support/prefixed_logger.h" #include "fmt/format.h" @@ -31,10 +32,10 @@ namespace srsran { class rlc_bearer_log_prefix { public: - rlc_bearer_log_prefix(uint32_t du_index, uint32_t ue_index, rb_id_t rb_id, const char* dir) + rlc_bearer_log_prefix(gnb_du_id_t gnb_du_id, uint32_t ue_index, rb_id_t rb_id, const char* dir) { fmt::memory_buffer buffer; - fmt::format_to(buffer, "du={} ue={} {} {}: ", du_index, ue_index, rb_id, dir); + fmt::format_to(buffer, "du={} ue={} {} {}: ", gnb_du_id, ue_index, rb_id, dir); prefix = srsran::to_c_str(buffer); } const char* to_c_str() const { return prefix.c_str(); } diff --git a/lib/rlc/rlc_factory.cpp b/lib/rlc/rlc_factory.cpp index b3a9c8eb55..55f0d19adf 100644 --- a/lib/rlc/rlc_factory.cpp +++ b/lib/rlc/rlc_factory.cpp @@ -31,7 +31,7 @@ std::unique_ptr srsran::create_rlc_entity(const rlc_entity_creation_ { switch (msg.config.mode) { case rlc_mode::tm: - return std::make_unique(msg.du_index, + return std::make_unique(msg.gnb_du_id, msg.ue_index, msg.rb_id, msg.config.tm, @@ -48,7 +48,7 @@ std::unique_ptr srsran::create_rlc_entity(const rlc_entity_creation_ case rlc_mode::um_unidir_dl: case rlc_mode::um_unidir_ul: case rlc_mode::um_bidir: - return std::make_unique(msg.du_index, + return std::make_unique(msg.gnb_du_id, msg.ue_index, msg.rb_id, msg.config.um, @@ -63,7 +63,7 @@ std::unique_ptr srsran::create_rlc_entity(const rlc_entity_creation_ *msg.ue_executor, *msg.pcap_writer); case rlc_mode::am: - return std::make_unique(msg.du_index, + return std::make_unique(msg.gnb_du_id, msg.ue_index, msg.rb_id, msg.config.am, diff --git a/lib/rlc/rlc_rx_am_entity.cpp b/lib/rlc/rlc_rx_am_entity.cpp index b3001a4a84..4ba4bb96d6 100644 --- a/lib/rlc/rlc_rx_am_entity.cpp +++ b/lib/rlc/rlc_rx_am_entity.cpp @@ -27,7 +27,7 @@ using namespace srsran; -rlc_rx_am_entity::rlc_rx_am_entity(uint32_t du_index, +rlc_rx_am_entity::rlc_rx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_am_config& config, @@ -36,7 +36,7 @@ rlc_rx_am_entity::rlc_rx_am_entity(uint32_t du_index, task_executor& ue_executor_, bool metrics_enabled, rlc_pcap& pcap_) : - rlc_rx_entity(du_index, ue_index, rb_id, upper_dn_, metrics_enabled, pcap_), + rlc_rx_entity(gnb_du_id, ue_index, rb_id, upper_dn_, metrics_enabled, pcap_), cfg(config), mod(cardinality(to_number(cfg.sn_field_length))), am_window_size(window_size(to_number(cfg.sn_field_length))), @@ -380,11 +380,11 @@ bool rlc_rx_am_entity::store_segment(rlc_rx_am_sdu_info& sdu_info, rlc_rx_am_sdu { // Section 5.2.3.2.2, discard segments with overlapping bytes - if (!variant_holds_alternative(sdu_info.sdu_data)) { + if (!std::holds_alternative(sdu_info.sdu_data)) { // put an empty set sdu_info.sdu_data = rlc_rx_am_sdu_info::segment_set_t{}; } - rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(sdu_info.sdu_data); + rlc_rx_am_sdu_info::segment_set_t& segments = std::get(sdu_info.sdu_data); auto cur_segment = segments.begin(); while (cur_segment != segments.end()) { uint32_t cur_last_byte = cur_segment->so + cur_segment->payload.length() - 1; @@ -460,9 +460,9 @@ bool rlc_rx_am_entity::store_segment(rlc_rx_am_sdu_info& sdu_info, rlc_rx_am_sdu void rlc_rx_am_entity::update_segment_inventory(rlc_rx_am_sdu_info& rx_sdu) const { - srsran_assert(variant_holds_alternative(rx_sdu.sdu_data), + srsran_assert(std::holds_alternative(rx_sdu.sdu_data), "Invalid sdu_data variant for update of segment inventory"); - rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(rx_sdu.sdu_data); + rlc_rx_am_sdu_info::segment_set_t& segments = std::get(rx_sdu.sdu_data); if (segments.empty()) { rx_sdu.fully_received = false; rx_sdu.has_gap = false; @@ -505,15 +505,15 @@ expected rlc_rx_am_entity::reassemble_sdu(rlc_rx_am_sdu_info& return {default_error_t{}}; } - if (variant_holds_alternative(sdu_info.sdu_data)) { + if (std::holds_alternative(sdu_info.sdu_data)) { // Handling for full SDU - byte_buffer_slice& payload = variant_get(sdu_info.sdu_data); + byte_buffer_slice& payload = std::get(sdu_info.sdu_data); if (!sdu.value().append(std::move(payload))) { logger.log_error("Failed to append segment in SDU buffer. sn={} {}", sn, sdu_info); return {default_error_t{}}; } - } else if (variant_holds_alternative(sdu_info.sdu_data)) { - rlc_rx_am_sdu_info::segment_set_t& segments = variant_get(sdu_info.sdu_data); + } else if (std::holds_alternative(sdu_info.sdu_data)) { + rlc_rx_am_sdu_info::segment_set_t& segments = std::get(sdu_info.sdu_data); for (const rlc_rx_am_sdu_segment& segm : segments) { logger.log_debug("Chaining segment. sn={} so={} len={}", sn, segm.so, segm.payload.length()); if (!sdu.value().append(segm.payload.copy())) { @@ -561,11 +561,11 @@ void rlc_rx_am_entity::refresh_status_report() logger.log_debug("Adding nack={}.", nack); status_builder->push_nack(nack); } else if (not(*rx_window)[i].fully_received) { - srsran_assert(variant_holds_alternative((*rx_window)[i].sdu_data), + srsran_assert(std::holds_alternative((*rx_window)[i].sdu_data), "Invalid sdu_data variant of incomplete SDU in rx_window. sn={}", i); rlc_rx_am_sdu_info::segment_set_t& segments = - variant_get((*rx_window)[i].sdu_data); + std::get((*rx_window)[i].sdu_data); // Some segments were received, but not all. // NACK non consecutive missing bytes uint32_t last_so = 0; diff --git a/lib/rlc/rlc_rx_am_entity.h b/lib/rlc/rlc_rx_am_entity.h index 82dd285476..78b5927667 100644 --- a/lib/rlc/rlc_rx_am_entity.h +++ b/lib/rlc/rlc_rx_am_entity.h @@ -55,7 +55,7 @@ struct rlc_rx_am_sdu_info { /// Indicates a gap (i.e. a missing segment) among all already received segments. bool has_gap = false; /// Buffer for either a full SDU or a set of SDU segments. - variant sdu_data; + std::variant sdu_data; }; /// \brief Rx state variables @@ -148,7 +148,7 @@ class rlc_rx_am_entity : public rlc_rx_entity, public rlc_rx_am_status_provider pcap_rlc_pdu_context pcap_context; public: - rlc_rx_am_entity(uint32_t du_index_, + rlc_rx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_am_config& config, @@ -332,15 +332,15 @@ struct formatter { auto format(const srsran::rlc_rx_am_sdu_info& info, FormatContext& ctx) -> decltype(std::declval().out()) { - if (srsran::variant_holds_alternative(info.sdu_data)) { + if (std::holds_alternative(info.sdu_data)) { // full SDU - const srsran::byte_buffer_slice& payload = srsran::variant_get(info.sdu_data); + const srsran::byte_buffer_slice& payload = std::get(info.sdu_data); return format_to( ctx.out(), "has_gap={} fully_received={} sdu_len={}", info.has_gap, info.fully_received, payload.length()); - } else if (srsran::variant_holds_alternative(info.sdu_data)) { + } else if (std::holds_alternative(info.sdu_data)) { // segmented SDU const srsran::rlc_rx_am_sdu_info::segment_set_t& segments = - srsran::variant_get(info.sdu_data); + std::get(info.sdu_data); return format_to(ctx.out(), "has_gap={} fully_received={} nof_segments={}", info.has_gap, diff --git a/lib/rlc/rlc_rx_entity.h b/lib/rlc/rlc_rx_entity.h index 02ece65ad3..3d33e5d791 100644 --- a/lib/rlc/rlc_rx_entity.h +++ b/lib/rlc/rlc_rx_entity.h @@ -34,13 +34,13 @@ namespace srsran { class rlc_rx_entity : public rlc_rx_lower_layer_interface, public rlc_rx_metrics { protected: - rlc_rx_entity(uint32_t du_index, + rlc_rx_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, rlc_rx_upper_layer_data_notifier& upper_dn_, bool metrics_enable, rlc_pcap& pcap_) : - logger("RLC", {du_index, ue_index, rb_id, "UL"}), upper_dn(upper_dn_), metrics(metrics_enable), pcap(pcap_) + logger("RLC", {gnb_du_id, ue_index, rb_id, "UL"}), upper_dn(upper_dn_), metrics(metrics_enable), pcap(pcap_) { } diff --git a/lib/rlc/rlc_rx_tm_entity.cpp b/lib/rlc/rlc_rx_tm_entity.cpp index 94bf80e855..2e16176aea 100644 --- a/lib/rlc/rlc_rx_tm_entity.cpp +++ b/lib/rlc/rlc_rx_tm_entity.cpp @@ -24,14 +24,14 @@ using namespace srsran; -rlc_rx_tm_entity::rlc_rx_tm_entity(uint32_t du_index, +rlc_rx_tm_entity::rlc_rx_tm_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_tm_config& config, rlc_rx_upper_layer_data_notifier& upper_dn_, bool metrics_enabled, rlc_pcap& pcap_) : - rlc_rx_entity(du_index, ue_index, rb_id, upper_dn_, metrics_enabled, pcap_), + rlc_rx_entity(gnb_du_id, ue_index, rb_id, upper_dn_, metrics_enabled, pcap_), cfg(config), pcap_context(ue_index, rb_id, /* is_uplink */ true) { diff --git a/lib/rlc/rlc_rx_tm_entity.h b/lib/rlc/rlc_rx_tm_entity.h index f23fad6051..5deef19a17 100644 --- a/lib/rlc/rlc_rx_tm_entity.h +++ b/lib/rlc/rlc_rx_tm_entity.h @@ -35,7 +35,7 @@ class rlc_rx_tm_entity : public rlc_rx_entity pcap_rlc_pdu_context pcap_context; public: - rlc_rx_tm_entity(uint32_t du_index, + rlc_rx_tm_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_tm_config& config, @@ -43,9 +43,10 @@ class rlc_rx_tm_entity : public rlc_rx_entity bool metrics_enabled, rlc_pcap& pcap_); - void stop() final{ - // There are no timers to be stopped here. - }; + void stop() final + { + // There are no timers to be stopped here. + } // Interfaces for higher layers void handle_pdu(byte_buffer_slice buf) override; diff --git a/lib/rlc/rlc_rx_um_entity.cpp b/lib/rlc/rlc_rx_um_entity.cpp index 885900d4f6..f3f0bd8d10 100644 --- a/lib/rlc/rlc_rx_um_entity.cpp +++ b/lib/rlc/rlc_rx_um_entity.cpp @@ -25,7 +25,7 @@ using namespace srsran; -rlc_rx_um_entity::rlc_rx_um_entity(uint32_t du_index, +rlc_rx_um_entity::rlc_rx_um_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_um_config& config, @@ -34,7 +34,7 @@ rlc_rx_um_entity::rlc_rx_um_entity(uint32_t du_index, task_executor& ue_executor, bool metrics_enable, rlc_pcap& pcap_) : - rlc_rx_entity(du_index, ue_index, rb_id, upper_dn_, metrics_enable, pcap_), + rlc_rx_entity(gnb_du_id, ue_index, rb_id, upper_dn_, metrics_enable, pcap_), cfg(config), mod(cardinality(to_number(cfg.sn_field_length))), um_window_size(window_size(to_number(cfg.sn_field_length))), diff --git a/lib/rlc/rlc_rx_um_entity.h b/lib/rlc/rlc_rx_um_entity.h index 584de56bd1..3dd8cce99b 100644 --- a/lib/rlc/rlc_rx_um_entity.h +++ b/lib/rlc/rlc_rx_um_entity.h @@ -101,7 +101,7 @@ class rlc_rx_um_entity : public rlc_rx_entity pcap_rlc_pdu_context pcap_context; public: - rlc_rx_um_entity(uint32_t du_index, + rlc_rx_um_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_rx_um_config& config, diff --git a/lib/rlc/rlc_tm_entity.h b/lib/rlc/rlc_tm_entity.h index 4ad80ca205..edaa2ca6d0 100644 --- a/lib/rlc/rlc_tm_entity.h +++ b/lib/rlc/rlc_tm_entity.h @@ -31,7 +31,7 @@ namespace srsran { class rlc_tm_entity : public rlc_base_entity { public: - rlc_tm_entity(uint32_t du_index_, + rlc_tm_entity(gnb_du_id_t gnb_du_id_, du_ue_index_t ue_index_, rb_id_t rb_id_, const rlc_tm_config& config, @@ -45,14 +45,14 @@ class rlc_tm_entity : public rlc_base_entity task_executor& pcell_executor, task_executor& ue_executor, rlc_pcap& pcap) : - rlc_base_entity(du_index_, + rlc_base_entity(gnb_du_id_, ue_index_, rb_id_, metrics_period_, rlc_metrics_notifier_, timer_factory{timers, ue_executor}) { - tx = std::make_unique(du_index_, + tx = std::make_unique(gnb_du_id_, ue_index_, rb_id_, config.tx, @@ -63,7 +63,7 @@ class rlc_tm_entity : public rlc_base_entity metrics_period.count() != 0, pcap); rx = std::make_unique( - du_index_, ue_index_, rb_id_, config.rx, rx_upper_dn, metrics_period.count() != 0, pcap); + gnb_du_id_, ue_index_, rb_id_, config.rx, rx_upper_dn, metrics_period.count() != 0, pcap); } }; diff --git a/lib/rlc/rlc_tx_am_entity.cpp b/lib/rlc/rlc_tx_am_entity.cpp index 59aec1e35c..af9bdb0a1f 100644 --- a/lib/rlc/rlc_tx_am_entity.cpp +++ b/lib/rlc/rlc_tx_am_entity.cpp @@ -31,7 +31,7 @@ using namespace srsran; -rlc_tx_am_entity::rlc_tx_am_entity(uint32_t du_index, +rlc_tx_am_entity::rlc_tx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_am_config& config, @@ -43,7 +43,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(uint32_t du_index task_executor& ue_executor_, bool metrics_enabled_, rlc_pcap& pcap_) : - rlc_tx_entity(du_index, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), + rlc_tx_entity(gnb_du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), retx_queue(window_size(to_number(cfg.sn_field_length))), @@ -65,7 +65,7 @@ rlc_tx_am_entity::rlc_tx_am_entity(uint32_t du_index srsran_assert(config.pdcp_sn_len == pdcp_sn_size::size12bits || config.pdcp_sn_len == pdcp_sn_size::size18bits, "Cannot create RLC TX AM, unsupported pdcp_sn_len={}. du={} ue={} {}", config.pdcp_sn_len, - du_index, + gnb_du_id, ue_index, rb_id); diff --git a/lib/rlc/rlc_tx_am_entity.h b/lib/rlc/rlc_tx_am_entity.h index 1840aa40cd..fab0b5489d 100644 --- a/lib/rlc/rlc_tx_am_entity.h +++ b/lib/rlc/rlc_tx_am_entity.h @@ -137,7 +137,7 @@ class rlc_tx_am_entity : public rlc_tx_entity, public rlc_tx_am_status_handler, std::atomic_flag pending_buffer_state = ATOMIC_FLAG_INIT; public: - rlc_tx_am_entity(uint32_t du_index, + rlc_tx_am_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_am_config& config, diff --git a/lib/rlc/rlc_tx_entity.h b/lib/rlc/rlc_tx_entity.h index 2273406d00..763714e286 100644 --- a/lib/rlc/rlc_tx_entity.h +++ b/lib/rlc/rlc_tx_entity.h @@ -38,7 +38,7 @@ class rlc_tx_entity : public rlc_tx_upper_layer_data_interface, public rlc_tx_metrics { protected: - rlc_tx_entity(uint32_t du_index, + rlc_tx_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, rlc_tx_upper_layer_data_notifier& upper_dn_, @@ -46,7 +46,7 @@ class rlc_tx_entity : public rlc_tx_upper_layer_data_interface, rlc_tx_lower_layer_notifier& lower_dn_, bool metrics_enabled, rlc_pcap& pcap_) : - logger("RLC", {du_index, ue_index, rb_id, "DL"}), + logger("RLC", {gnb_du_id, ue_index, rb_id, "DL"}), metrics(metrics_enabled), upper_dn(upper_dn_), upper_cn(upper_cn_), diff --git a/lib/rlc/rlc_tx_tm_entity.cpp b/lib/rlc/rlc_tx_tm_entity.cpp index f4c25ed3a1..6457eb902f 100644 --- a/lib/rlc/rlc_tx_tm_entity.cpp +++ b/lib/rlc/rlc_tx_tm_entity.cpp @@ -24,7 +24,7 @@ using namespace srsran; -rlc_tx_tm_entity::rlc_tx_tm_entity(uint32_t du_index, +rlc_tx_tm_entity::rlc_tx_tm_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_tm_config& config, @@ -34,7 +34,7 @@ rlc_tx_tm_entity::rlc_tx_tm_entity(uint32_t du_index task_executor& pcell_executor_, bool metrics_enabled_, rlc_pcap& pcap_) : - rlc_tx_entity(du_index, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), + rlc_tx_entity(du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled_, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), pcell_executor(pcell_executor_), diff --git a/lib/rlc/rlc_tx_tm_entity.h b/lib/rlc/rlc_tx_tm_entity.h index 1776871e4f..165009d186 100644 --- a/lib/rlc/rlc_tx_tm_entity.h +++ b/lib/rlc/rlc_tx_tm_entity.h @@ -48,7 +48,7 @@ class rlc_tx_tm_entity : public rlc_tx_entity std::atomic_flag pending_buffer_state = ATOMIC_FLAG_INIT; public: - rlc_tx_tm_entity(uint32_t du_index, + rlc_tx_tm_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_tm_config& config, diff --git a/lib/rlc/rlc_tx_um_entity.cpp b/lib/rlc/rlc_tx_um_entity.cpp index 18cbe957ab..b33cedb17b 100644 --- a/lib/rlc/rlc_tx_um_entity.cpp +++ b/lib/rlc/rlc_tx_um_entity.cpp @@ -27,7 +27,7 @@ using namespace srsran; -rlc_tx_um_entity::rlc_tx_um_entity(uint32_t du_index, +rlc_tx_um_entity::rlc_tx_um_entity(gnb_du_id_t du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_um_config& config, @@ -37,7 +37,7 @@ rlc_tx_um_entity::rlc_tx_um_entity(uint32_t du_index task_executor& pcell_executor_, bool metrics_enabled, rlc_pcap& pcap_) : - rlc_tx_entity(du_index, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled, pcap_), + rlc_tx_entity(du_id, ue_index, rb_id, upper_dn_, upper_cn_, lower_dn_, metrics_enabled, pcap_), cfg(config), sdu_queue(cfg.queue_size, logger), mod(cardinality(to_number(cfg.sn_field_length))), @@ -53,7 +53,7 @@ rlc_tx_um_entity::rlc_tx_um_entity(uint32_t du_index srsran_assert(config.pdcp_sn_len == pdcp_sn_size::size12bits || config.pdcp_sn_len == pdcp_sn_size::size18bits, "Cannot create RLC TX AM, unsupported pdcp_sn_len={}. du={} ue={} {}", config.pdcp_sn_len, - du_index, + du_id, ue_index, rb_id); diff --git a/lib/rlc/rlc_tx_um_entity.h b/lib/rlc/rlc_tx_um_entity.h index 555c47c3fa..67b7c80b79 100644 --- a/lib/rlc/rlc_tx_um_entity.h +++ b/lib/rlc/rlc_tx_um_entity.h @@ -82,7 +82,7 @@ class rlc_tx_um_entity : public rlc_tx_entity std::atomic_flag pending_buffer_state = ATOMIC_FLAG_INIT; public: - rlc_tx_um_entity(uint32_t du_index, + rlc_tx_um_entity(gnb_du_id_t gnb_du_id, du_ue_index_t ue_index, rb_id_t rb_id, const rlc_tx_um_config& config, diff --git a/lib/rlc/rlc_um_entity.h b/lib/rlc/rlc_um_entity.h index fbe00e3b5d..97ce25debd 100644 --- a/lib/rlc/rlc_um_entity.h +++ b/lib/rlc/rlc_um_entity.h @@ -31,7 +31,7 @@ namespace srsran { class rlc_um_entity : public rlc_base_entity { public: - rlc_um_entity(uint32_t du_index_, + rlc_um_entity(gnb_du_id_t gnb_du_id_, du_ue_index_t ue_index_, rb_id_t rb_id_, const rlc_um_config& config, @@ -45,14 +45,14 @@ class rlc_um_entity : public rlc_base_entity task_executor& pcell_executor, task_executor& ue_executor, rlc_pcap& pcap) : - rlc_base_entity(du_index_, + rlc_base_entity(gnb_du_id_, ue_index_, rb_id_, metrics_period_, rlc_metrics_notifier_, timer_factory{timers, ue_executor}) { - tx = std::make_unique(du_index_, + tx = std::make_unique(gnb_du_id_, ue_index_, rb_id_, config.tx, @@ -62,7 +62,7 @@ class rlc_um_entity : public rlc_base_entity pcell_executor, metrics_period.count() != 0, pcap); - rx = std::make_unique(du_index_, + rx = std::make_unique(gnb_du_id_, ue_index_, rb_id_, config.rx, diff --git a/lib/rrc/ue/adapters/pdcp_adapters.h b/lib/rrc/ue/adapters/pdcp_adapters.h index 60c120b1b8..652d6b77e5 100644 --- a/lib/rrc/ue/adapters/pdcp_adapters.h +++ b/lib/rrc/ue/adapters/pdcp_adapters.h @@ -55,7 +55,7 @@ class pdcp_rrc_ue_rx_adapter : public pdcp_rx_upper_data_notifier, public pdcp_r cause = cause_protocol_t::unspecified; } - variant, ngap_cause_t> pop_result() + std::variant, ngap_cause_t> pop_result() { if (cause.has_value()) { auto ret = *cause; diff --git a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp index 8c897ec56b..834342305f 100644 --- a/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp +++ b/lib/rrc/ue/procedures/rrc_reestablishment_procedure.cpp @@ -185,6 +185,11 @@ bool rrc_reestablishment_procedure::is_reestablishment_accepted() return false; } + if (old_ue_reest_context.reestablishment_ongoing) { + log_rejected_reestablishment("Old UE is already in reestablishment procedure"); + return false; + } + // Check if the old UE completed the SRB2 and DRB establishment. if (not old_ue_reest_context.old_ue_fully_attached) { log_rejected_reestablishment("Old UE bearers were not fully established"); diff --git a/lib/rrc/ue/rrc_measurement_types_asn1_converters.h b/lib/rrc/ue/rrc_measurement_types_asn1_converters.h index 447e9d8e65..9f398bbf64 100644 --- a/lib/rrc/ue/rrc_measurement_types_asn1_converters.h +++ b/lib/rrc/ue/rrc_measurement_types_asn1_converters.h @@ -1270,7 +1270,8 @@ inline rrc_meas_result_nr asn1_to_meas_result_nr(const asn1::rrc_nr::meas_result return meas_result_nr; }; -inline rrc_meas_results asn1_to_measurement_results(const asn1::rrc_nr::meas_results_s& asn1_meas_results) +inline rrc_meas_results asn1_to_measurement_results(const asn1::rrc_nr::meas_results_s& asn1_meas_results, + srslog::basic_logger& logger) { rrc_meas_results meas_results; @@ -1307,8 +1308,8 @@ inline rrc_meas_results asn1_to_measurement_results(const asn1::rrc_nr::meas_res } } else { // error - report_fatal_error("Invalid meas result neigh cells type = {}.", - asn1_meas_results.meas_result_neigh_cells.type()); + logger.error("Ignoring neighbor cell measurement. Cause: Unsupported cell type {}", + asn1_meas_results.meas_result_neigh_cells.type().to_string()); } meas_results.meas_result_neigh_cells = meas_result_neigh_cell; diff --git a/lib/rrc/ue/rrc_ue_context.h b/lib/rrc/ue/rrc_ue_context.h index ae7594404f..050d6be118 100644 --- a/lib/rrc/ue/rrc_ue_context.h +++ b/lib/rrc/ue/rrc_ue_context.h @@ -61,6 +61,7 @@ class rrc_ue_context_t std::optional capabilities; std::optional capabilities_list; std::optional transfer_context; // Context of old UE when created through mobility. + bool reestablishment_ongoing = false; srslog::basic_logger& logger; }; diff --git a/lib/rrc/ue/rrc_ue_message_handlers.cpp b/lib/rrc/ue/rrc_ue_message_handlers.cpp index 009912cc39..e05debcc53 100644 --- a/lib/rrc/ue/rrc_ue_message_handlers.cpp +++ b/lib/rrc/ue/rrc_ue_message_handlers.cpp @@ -234,9 +234,13 @@ void rrc_ue_impl::handle_ul_info_transfer(const ul_info_transfer_ies_s& ul_info_ void rrc_ue_impl::handle_measurement_report(const asn1::rrc_nr::meas_report_s& msg) { // convert asn1 to common type - rrc_meas_results meas_results = asn1_to_measurement_results(msg.crit_exts.meas_report().meas_results); - // send measurement results to cell measurement manager - measurement_notifier.on_measurement_report(meas_results); + rrc_meas_results meas_results = + asn1_to_measurement_results(msg.crit_exts.meas_report().meas_results, srslog::fetch_basic_logger("RRC")); + // Send measurement results to cell measurement manager only measurements are not empty. + if (meas_results.meas_result_neigh_cells.has_value() and + not meas_results.meas_result_neigh_cells->meas_result_list_nr.empty()) { + measurement_notifier.on_measurement_report(meas_results); + } } void rrc_ue_impl::handle_dl_nas_transport_message(byte_buffer nas_pdu) @@ -429,6 +433,14 @@ rrc_ue_reestablishment_context_response rrc_ue_impl::get_context() } rrc_reest_context.up_ctx = up_resource_mng.get_up_context(); + // TODO: Handle scenario with multiple reestablishments for the same UE + rrc_reest_context.reestablishment_ongoing = context.reestablishment_ongoing; + + // If no reestablishment is ongoing, set it to true. + if (not context.reestablishment_ongoing) { + context.reestablishment_ongoing = true; + } + return rrc_reest_context; } diff --git a/lib/rrc/ue/rrc_ue_srb_context.h b/lib/rrc/ue/rrc_ue_srb_context.h index d6af9a0e5a..5a21b4b85a 100644 --- a/lib/rrc/ue/rrc_ue_srb_context.h +++ b/lib/rrc/ue/rrc_ue_srb_context.h @@ -29,25 +29,25 @@ namespace srsran { namespace srs_cu_cp { struct pdcp_tx_result { - variant result; + std::variant result; /// Whether the packing was successful. - bool is_successful() const { return variant_holds_alternative(result); } + bool is_successful() const { return std::holds_alternative(result); } - ngap_cause_t get_failure_cause() const { return variant_get(result); } + ngap_cause_t get_failure_cause() const { return std::get(result); } - byte_buffer pop_pdu() { return std::move(variant_get(result)); } + byte_buffer pop_pdu() { return std::move(std::get(result)); } }; struct pdcp_rx_result { - variant, ngap_cause_t> result; + std::variant, ngap_cause_t> result; /// Whether the unpacking was successful. - bool is_successful() const { return variant_holds_alternative>(result); } + bool is_successful() const { return std::holds_alternative>(result); } - ngap_cause_t get_failure_cause() const { return variant_get(result); } + ngap_cause_t get_failure_cause() const { return std::get(result); } - std::vector pop_pdus() { return std::move(variant_get>(result)); } + std::vector pop_pdus() { return std::move(std::get>(result)); } }; /// Additional context of a SRB containing notifiers to PDCP, i.e. SRB1 and SRB2. @@ -157,7 +157,7 @@ class ue_srb_context // Return unpacked PDCP PDUs or error with cause. // Note: List of byte_buffers (in case of success) can be empty if the PDCP Rx PDU is out-of-order. - variant, ngap_cause_t> unpacked_pdus = pdcp_context.rrc_rx_data_notifier.pop_result(); + std::variant, ngap_cause_t> unpacked_pdus = pdcp_context.rrc_rx_data_notifier.pop_result(); return pdcp_rx_result{unpacked_pdus}; } diff --git a/lib/ru/dummy/ru_dummy_rx_prach_buffer.h b/lib/ru/dummy/ru_dummy_rx_prach_buffer.h index e9cfd61950..b1b912e752 100644 --- a/lib/ru/dummy/ru_dummy_rx_prach_buffer.h +++ b/lib/ru/dummy/ru_dummy_rx_prach_buffer.h @@ -60,12 +60,12 @@ class ru_dummy_rx_prach_buffer : private prach_buffer // Derive parameters from the PRACH format. if (is_long_preamble(context.format)) { prach_preamble_information info = get_prach_preamble_long_info(context.format); - nof_symbols = static_cast(info.symbol_length.to_seconds() * ra_scs_to_Hz(info.scs)); + nof_symbols = info.nof_symbols; sequence_length = info.sequence_length; } else { prach_preamble_information info = get_prach_preamble_short_info(context.format, to_ra_subcarrier_spacing(context.pusch_scs), false); - nof_symbols = static_cast(info.symbol_length.to_seconds() * ra_scs_to_Hz(info.scs)); + nof_symbols = info.nof_symbols; sequence_length = info.sequence_length; } diff --git a/lib/ru/dummy/ru_dummy_rx_resource_grid.h b/lib/ru/dummy/ru_dummy_rx_resource_grid.h index 6256c8c930..8166fd5cf8 100644 --- a/lib/ru/dummy/ru_dummy_rx_resource_grid.h +++ b/lib/ru/dummy/ru_dummy_rx_resource_grid.h @@ -67,16 +67,6 @@ class ru_dummy_rx_resource_grid : public resource_grid_reader return false; } - // See resource_grid_reader for documentation. - span get(span symbols, unsigned port, unsigned l, unsigned k_init, span mask) const override - { - // Count number of elements. - unsigned nof_symbols = std::accumulate( - mask.begin(), mask.end(), 0U, [](unsigned count, bool active_re) { return count + (active_re ? 1U : 0U); }); - - return get(symbols, nof_symbols); - } - // See resource_grid_reader for documentation. span get(span symbols, unsigned port, diff --git a/lib/ru/ofh/ru_ofh_factory.cpp b/lib/ru/ofh/ru_ofh_factory.cpp index acc59a9d53..49d53a7ab2 100644 --- a/lib/ru/ofh/ru_ofh_factory.cpp +++ b/lib/ru/ofh/ru_ofh_factory.cpp @@ -79,13 +79,13 @@ static ofh::sector_dependencies generate_sector_dependencies(ru_ofh_sector_depen { // Prepare sector configuration. ofh::sector_dependencies ofh_sector_dependencies; - ofh_sector_dependencies.logger = dependencies.logger; - ofh_sector_dependencies.receiver_executor = dependencies.receiver_executor; - ofh_sector_dependencies.transmitter_executor = dependencies.transmitter_executor; - ofh_sector_dependencies.downlink_executor = dependencies.downlink_executor; - ofh_sector_dependencies.notifier = std::move(notifier); - ofh_sector_dependencies.eth_gateway = std::move(dependencies.eth_gateway); - ofh_sector_dependencies.eth_receiver = std::move(dependencies.eth_receiver); + ofh_sector_dependencies.logger = dependencies.logger; + ofh_sector_dependencies.uplink_executor = dependencies.uplink_executor; + ofh_sector_dependencies.txrx_executor = dependencies.txrx_executor; + ofh_sector_dependencies.downlink_executor = dependencies.downlink_executor; + ofh_sector_dependencies.notifier = std::move(notifier); + ofh_sector_dependencies.eth_gateway = std::move(dependencies.eth_gateway); + ofh_sector_dependencies.eth_receiver = std::move(dependencies.eth_receiver); return ofh_sector_dependencies; } diff --git a/lib/scheduler/cell_scheduler.cpp b/lib/scheduler/cell_scheduler.cpp index 6fe562599f..6d5809777d 100644 --- a/lib/scheduler/cell_scheduler.cpp +++ b/lib/scheduler/cell_scheduler.cpp @@ -30,19 +30,18 @@ cell_scheduler::cell_scheduler(const scheduler_expert_config& s const sched_cell_configuration_request_message& msg, const cell_configuration& cell_cfg_, ue_scheduler& ue_sched_, - scheduler_event_logger& ev_logger, scheduler_metrics_handler& metrics_handler) : cell_cfg(cell_cfg_), ue_sched(ue_sched_), res_grid(cell_cfg), - event_logger(ev_logger), + event_logger(cell_cfg.cell_index, cell_cfg.pci), metrics(metrics_handler), result_logger(sched_cfg.log_broadcast_messages, cell_cfg.pci), logger(srslog::fetch_basic_logger("SCHED")), ssb_sch(cell_cfg), pdcch_sch(cell_cfg), csi_sch(cell_cfg), - ra_sch(sched_cfg.ra, cell_cfg, pdcch_sch, ev_logger), + ra_sch(sched_cfg.ra, cell_cfg, pdcch_sch, event_logger), prach_sch(cell_cfg), pucch_alloc(cell_cfg, sched_cfg.ue.max_pucchs_per_slot, sched_cfg.ue.max_ul_grants_per_slot), uci_alloc(pucch_alloc), @@ -52,7 +51,8 @@ cell_scheduler::cell_scheduler(const scheduler_expert_config& s pg_sch(sched_cfg, cell_cfg, pdcch_sch, msg) { // Register new cell in the UE scheduler. - ue_sched.add_cell(ue_scheduler_cell_params{msg.cell_index, &pdcch_sch, &pucch_alloc, &uci_alloc, &res_grid}); + ue_sched.add_cell( + ue_scheduler_cell_params{msg.cell_index, &pdcch_sch, &pucch_alloc, &uci_alloc, &res_grid, &event_logger}); } void cell_scheduler::handle_crc_indication(const ul_crc_indication& crc_ind) diff --git a/lib/scheduler/cell_scheduler.h b/lib/scheduler/cell_scheduler.h index 2d628540d8..a5a4650e92 100644 --- a/lib/scheduler/cell_scheduler.h +++ b/lib/scheduler/cell_scheduler.h @@ -31,6 +31,7 @@ #include "common_scheduling/sib_scheduler.h" #include "common_scheduling/ssb_scheduler.h" #include "config/cell_configuration.h" +#include "logging/scheduler_event_logger.h" #include "logging/scheduler_result_logger.h" #include "pdcch_scheduling/pdcch_resource_allocator_impl.h" #include "pucch_scheduling/pucch_allocator_impl.h" @@ -52,7 +53,6 @@ class cell_scheduler const sched_cell_configuration_request_message& msg, const cell_configuration& cell_cfg, ue_scheduler& ue_sched, - scheduler_event_logger& ev_logger, scheduler_metrics_handler& metrics); void run_slot(slot_point sl_tx); @@ -75,7 +75,7 @@ class cell_scheduler cell_resource_allocator res_grid; /// Logger of cell events and scheduling results. - scheduler_event_logger& event_logger; + scheduler_event_logger event_logger; scheduler_metrics_handler& metrics; scheduler_result_logger result_logger; srslog::basic_logger& logger; diff --git a/lib/scheduler/common_scheduling/ra_scheduler.cpp b/lib/scheduler/common_scheduling/ra_scheduler.cpp index fbc4ac1226..01ca0ccc62 100644 --- a/lib/scheduler/common_scheduling/ra_scheduler.cpp +++ b/lib/scheduler/common_scheduling/ra_scheduler.cpp @@ -321,9 +321,7 @@ void ra_scheduler::handle_pending_crc_indications_impl(cell_resource_allocator& void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) { - const slot_point pdcch_slot = res_alloc.slot_tx(); - - // Handle pending CRCs. + // Handle pending CRCs, which may lead to Msg3 reTxs. handle_pending_crc_indications_impl(res_alloc); // Pop pending RACHs and process them. @@ -333,13 +331,80 @@ void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) handle_rach_indication_impl(rach); } + if (not pending_rars.empty()) { + // In case there were attempts to schedule a pending RAR in an earlier slot, we resume the scheduling of the same + // RAR from where we left off to avoid unnecessary work. + // In case it is the first attempt at scheduling a pending RAR, we start from the current PDCCH slot. + unsigned sched_start_delay = pending_rars.front().last_sched_try_slot.valid() + ? pending_rars.front().last_sched_try_slot + 1 - res_alloc.slot_tx() + : 0; + + for (unsigned n = sched_start_delay; n <= max_dl_slots_ahead_sched and not pending_rars.empty(); ++n) { + // Schedule RARs for the given PDCCH slot. + schedule_pending_rars(res_alloc, res_alloc.slot_tx() + n); + } + + // For the RARs that were not scheduled, save the last slot when an allocation was attempted. This avoids redundant + // scheduling attempts. + for (pending_rar_t& rar : pending_rars) { + rar.last_sched_try_slot = res_alloc.slot_tx() + max_dl_slots_ahead_sched; + } + } +} + +void ra_scheduler::update_pending_rars(slot_point pdcch_slot) +{ + for (auto it = pending_rars.begin(); it != pending_rars.end();) { + pending_rar_t& rar_req = *it; + + // In case of RAR being outside RAR window: + // - if window has passed, discard RAR + // - if window hasn't started, stop loop, as RARs are ordered by slot + if (not rar_req.rar_window.contains(pdcch_slot)) { + if (pdcch_slot >= rar_req.rar_window.stop()) { + logger.warning("Could not transmit RAR within the window={}, prach_slot={}, slot_tx={}", + rar_req.rar_window, + rar_req.prach_slot_rx, + pdcch_slot); + it = pending_rars.erase(it); + continue; + } + break; + } + ++it; + } +} + +bool ra_scheduler::is_slot_candidate_for_rar(cell_slot_resource_allocator& slot_res_alloc) +{ + slot_point pdcch_slot = slot_res_alloc.slot; + + // Check there are any RARs to schedule. + if (pending_rars.empty() or not pending_rars.front().rar_window.contains(pdcch_slot)) { + // There are no RARs to schedule with RAR window containing this slot. + // Note: The pending RARs are ordered by slot, and this function should be called after outdated RARs have already + // been removed. + return false; + } + // Ensure slot for RAR PDCCH has DL enabled. - if (not cell_cfg.is_dl_enabled(pdcch_slot)) { - // Early exit. - return; + if (not cell_cfg.is_dl_enabled(slot_res_alloc.slot)) { + return false; } - // Ensure (i) RA SearchSpace PDCCH monitoring is active for this slot and (ii) there are enough UL symbols to allocate + if (not slot_res_alloc.result.dl.csi_rs.empty()) { + // TODO: Remove this once multiplexing is possible. + // At the moment, we do not multiple PDSCH and CSI-RS. + return false; + } + + // Check for space in PDCCH result list. We check for space in PDSCH later, once the k0 is known. + if (slot_res_alloc.result.dl.dl_pdcchs.full()) { + log_postponed_rar(pending_rars.front(), "No PDCCH space for RAR."); + return false; + } + + // Ensure (i) RA SearchSpace PDCCH monitoring is active for this slot and (ii) there are enough DL symbols to allocate // the PDCCH. const search_space_id ss_id = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.ra_search_space_id; const search_space_configuration& ss_cfg = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces[ss_id]; @@ -347,20 +412,14 @@ void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) // TODO: Handle the case when ra_search_space_id is set to 0. if (not pdcch_helper::is_pdcch_monitoring_active(pdcch_slot, ss_cfg) or ss_cfg.get_first_symbol_index() + cs_cfg.duration > cell_cfg.get_nof_dl_symbol_per_slot(pdcch_slot)) { - // Early exit. RAR scheduling only possible when PDCCH monitoring is active. - return; - } - - if (not res_alloc[0].result.dl.csi_rs.empty()) { - // TODO: Remove this once multiplexing is possible. - // Early exit. At the moment, we do not multiple PDSCH and CSI-RS. - return; + // RAR scheduling only possible when PDCCH monitoring is active. + return false; } // Ensure there are UL slots where Msg3s can be allocated. bool pusch_slots_available = false; for (const auto& pusch_td_alloc : get_pusch_time_domain_resource_table(get_pusch_cfg())) { - const unsigned msg3_delay = get_msg3_delay(pusch_td_alloc, get_ul_bwp_cfg().scs) + res_alloc.cfg.ntn_cs_koffset; + const unsigned msg3_delay = get_msg3_delay(pusch_td_alloc, get_ul_bwp_cfg().scs) + cell_cfg.ntn_cs_koffset; const unsigned start_ul_symbols = NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg.get_nof_ul_symbol_per_slot(pdcch_slot + msg3_delay); if (cell_cfg.is_ul_enabled(pdcch_slot + msg3_delay) and pusch_td_alloc.symbols.start() >= start_ul_symbols) { @@ -369,38 +428,39 @@ void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) } } if (not pusch_slots_available) { - // Early exit. Msg3 scheduling only possible when PUSCH is available. + // Msg3 scheduling only possible when PUSCH is available. + return false; + } + + return true; +} + +void ra_scheduler::schedule_pending_rars(cell_resource_allocator& res_alloc, slot_point pdcch_slot) +{ + // Remove outdated RARs. + update_pending_rars(pdcch_slot); + + // Check if slot is a valid candidate for RAR scheduling. + if (not is_slot_candidate_for_rar(res_alloc[pdcch_slot])) { return; } for (auto it = pending_rars.begin(); it != pending_rars.end();) { pending_rar_t& rar_req = *it; - - // In case of RAR being outside RAR window: - // - if window has passed, discard RAR - // - if window hasn't started, stop loop, as RARs are ordered by slot if (not rar_req.rar_window.contains(pdcch_slot)) { - if (pdcch_slot >= rar_req.rar_window.stop()) { - logger.warning("Could not transmit RAR within the window={}, prach_slot={}, slot_tx={}", - rar_req.rar_window, - rar_req.prach_slot_rx, - pdcch_slot); - it = pending_rars.erase(it); - continue; - } + // RAR window hasn't started yet for this RAR. Given that the RARs are in order of slot, we can stop here. break; } // Try to schedule DCIs + RBGs for RAR Grants - const size_t nof_allocs = schedule_rar(rar_req, res_alloc); - srsran_sanity_check(nof_allocs <= rar_req.tc_rntis.size(), "Invalid number of RAR allocs"); + const size_t nof_allocs = schedule_rar(rar_req, res_alloc, pdcch_slot); if (nof_allocs > 0) { // If RAR allocation was successful: // - in case all Msg3 grants were allocated, remove pending RAR, and continue with following RAR // - otherwise, erase only Msg3 grants that were allocated, and stop iteration - if (nof_allocs == rar_req.tc_rntis.size()) { + if (nof_allocs >= rar_req.tc_rntis.size()) { it = pending_rars.erase(it); } else { // Remove only allocated Msg3 grants @@ -421,16 +481,11 @@ void ra_scheduler::run_slot(cell_resource_allocator& res_alloc) } } -unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allocator& res_alloc) +unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allocator& res_alloc, slot_point pdcch_slot) { - cell_slot_resource_allocator& pdcch_alloc = res_alloc[0]; - if (pdcch_alloc.result.dl.dl_pdcchs.full()) { - // Early exit. - log_postponed_rar(rar, "No PDCCH space for RAR."); - return 0; - } + cell_slot_resource_allocator& pdcch_alloc = res_alloc[pdcch_slot]; - const auto& pdsch_td_res_alloc_list = + span pdsch_td_res_alloc_list = get_ra_rnti_pdsch_time_domain_list(cell_cfg.dl_cfg_common.init_dl_bwp.pdsch_common, cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.cp, cell_cfg.dmrs_typeA_pos); @@ -444,7 +499,7 @@ unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allo crb_interval rar_crbs{}; for (const auto& pdsch_td_res : pdsch_td_res_alloc_list) { const unsigned time_resource = std::distance(pdsch_td_res_alloc_list.begin(), &pdsch_td_res); - const cell_slot_resource_allocator& pdsch_alloc = res_alloc[pdsch_td_res.k0]; + const cell_slot_resource_allocator& pdsch_alloc = res_alloc[pdcch_slot + pdsch_td_res.k0]; // > Check space in DL sched result for RAR. if (pdsch_alloc.result.dl.rar_grants.full()) { @@ -476,7 +531,7 @@ unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allo const unsigned nof_allocs = rb_helper::find_empty_interval_of_length(used_crbs, nof_rar_rbs, 0).length() / get_nof_pdsch_prbs_required(time_resource, 1).nof_prbs; - // > Try for maximum allocations as possible. + // > Pick the TD resource that leads to the maximum allocations possible. if (nof_allocs > max_nof_allocs) { max_nof_allocs = nof_allocs; rar_crbs = rb_helper::find_empty_interval_of_length(used_crbs, nof_rar_rbs, 0); @@ -486,7 +541,7 @@ unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allo if (max_nof_allocs == 0) { // Early exit. - log_postponed_rar(rar, "Not enough PRBs for RAR."); + log_postponed_rar(rar, "Not enough PRBs available for RAR PDSCH."); return 0; } @@ -504,9 +559,8 @@ unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allo unsigned pusch_res_max_allocs = max_nof_allocs - msg3_candidates.size(); // >> Verify if Msg3 delay provided by current PUSCH-TimeDomainResourceAllocation corresponds to an UL slot. - const unsigned msg3_delay = - get_msg3_delay(pusch_list[puschidx], get_ul_bwp_cfg().scs) + res_alloc.cfg.ntn_cs_koffset; - const cell_slot_resource_allocator& msg3_alloc = res_alloc[msg3_delay]; + const unsigned msg3_delay = get_msg3_delay(pusch_list[puschidx], get_ul_bwp_cfg().scs) + cell_cfg.ntn_cs_koffset; + const cell_slot_resource_allocator& msg3_alloc = res_alloc[pdcch_slot + msg3_delay]; const unsigned start_ul_symbols = NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg.get_nof_ul_symbol_per_slot(msg3_alloc.slot); if (not cell_cfg.is_ul_enabled(msg3_alloc.slot) or pusch_list[puschidx].symbols.start() < start_ul_symbols) { @@ -557,13 +611,14 @@ unsigned ra_scheduler::schedule_rar(const pending_rar_t& rar, cell_resource_allo // Status: RAR allocation is successful. // > Fill RAR and Msg3 PDSCH, PUSCH and DCI. - fill_rar_grant(res_alloc, rar, rar_crbs, pdsch_time_res_index, msg3_candidates); + fill_rar_grant(res_alloc, rar, pdcch_slot, rar_crbs, pdsch_time_res_index, msg3_candidates); return msg3_candidates.size(); } void ra_scheduler::fill_rar_grant(cell_resource_allocator& res_alloc, const pending_rar_t& rar_request, + slot_point pdcch_slot, crb_interval rar_crbs, unsigned pdsch_time_res_index, span msg3_candidates) @@ -573,8 +628,8 @@ void ra_scheduler::fill_rar_grant(cell_resource_allocator& res_alloc, cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.cp, cell_cfg.dmrs_typeA_pos); - cell_slot_resource_allocator& pdcch_alloc = res_alloc[0]; - cell_slot_resource_allocator& rar_alloc = res_alloc[pdsch_td_res_alloc_list[pdsch_time_res_index].k0]; + cell_slot_resource_allocator& pdcch_alloc = res_alloc[pdcch_slot]; + cell_slot_resource_allocator& rar_alloc = res_alloc[pdcch_slot + pdsch_td_res_alloc_list[pdsch_time_res_index].k0]; // Fill RAR DCI. pdcch_dl_information& pdcch = pdcch_alloc.result.dl.dl_pdcchs.back(); @@ -599,8 +654,8 @@ void ra_scheduler::fill_rar_grant(cell_resource_allocator& res_alloc, for (unsigned i = 0; i < msg3_candidates.size(); ++i) { const auto& msg3_candidate = msg3_candidates[i]; const auto& pusch_res = pusch_td_alloc_list[msg3_candidate.pusch_td_res_index]; - const unsigned msg3_delay = get_msg3_delay(pusch_res, get_ul_bwp_cfg().scs) + res_alloc.cfg.ntn_cs_koffset; - cell_slot_resource_allocator& msg3_alloc = res_alloc[msg3_delay]; + const unsigned msg3_delay = get_msg3_delay(pusch_res, get_ul_bwp_cfg().scs) + cell_cfg.ntn_cs_koffset; + cell_slot_resource_allocator& msg3_alloc = res_alloc[pdcch_slot + msg3_delay]; const vrb_interval vrbs = msg3_crb_to_vrb(cell_cfg, msg3_candidate.crbs); auto& pending_msg3 = pending_msg3s[to_value(rar_request.tc_rntis[i]) % MAX_NOF_MSG3]; diff --git a/lib/scheduler/common_scheduling/ra_scheduler.h b/lib/scheduler/common_scheduling/ra_scheduler.h index 1c6a2fee47..d2dd14842d 100644 --- a/lib/scheduler/common_scheduling/ra_scheduler.h +++ b/lib/scheduler/common_scheduling/ra_scheduler.h @@ -75,6 +75,7 @@ class ra_scheduler struct pending_rar_t { rnti_t ra_rnti = rnti_t::INVALID_RNTI; slot_point prach_slot_rx; + slot_point last_sched_try_slot; slot_interval rar_window; static_vector tc_rntis; }; @@ -110,18 +111,29 @@ class ra_scheduler void log_postponed_rar(const pending_rar_t& rar, const char* cause_str) const; + /// Delete RARs that are out of the RAR window. + void update_pending_rars(slot_point pdcch_slot); + + /// Determines whether the resource grid for the provided slot has the conditions for RAR scheduling. + bool is_slot_candidate_for_rar(cell_slot_resource_allocator& slot_res_alloc); + + /// Try scheduling pending RARs for the provided slot. + void schedule_pending_rars(cell_resource_allocator& res_alloc, slot_point pdcch_slot); + /// Find and allocate DL and UL resources for pending RAR and associated Msg3 grants. /// \return The number of allocated Msg3 grants. - unsigned schedule_rar(const pending_rar_t& rar, cell_resource_allocator& res_alloc); + unsigned schedule_rar(const pending_rar_t& rar, cell_resource_allocator& res_alloc, slot_point pdcch_slot); /// Schedule RAR grant and associated Msg3 grants in the provided scheduling resources. /// \param res_alloc Cell Resource Allocator. - /// \param rar pending RAR with an associated RA-RNTI that is going to be scheduled. + /// \param pending_rar pending RAR with an associated RA-RNTI that is going to be scheduled. + /// \param pdcch_slot Slot where the PDCCH is going to be scheduled. /// \param rar_crbs CRBs of the RAR to be scheduled. /// \param pdsch_time_res_index Index of PDSCH time domain resource. /// \param msg3_candidates List of Msg3s with respective resource information (e.g. RBs and symbols) to allocate. void fill_rar_grant(cell_resource_allocator& res_alloc, const pending_rar_t& pending_rar, + slot_point pdcch_slot, crb_interval rar_crbs, unsigned pdsch_time_res_index, span msg3_candidates); @@ -131,6 +143,10 @@ class ra_scheduler sch_prbs_tbs get_nof_pdsch_prbs_required(unsigned time_res_idx, unsigned nof_ul_grants) const; + // Set the max number of slots the scheduler can look ahead in the resource grid (with respect to the current slot) to + // find PDSCH space for RAR. + static const unsigned max_dl_slots_ahead_sched = 8U; + // args const scheduler_ra_expert_config& sched_cfg; const cell_configuration& cell_cfg; diff --git a/lib/scheduler/common_scheduling/si_message_scheduler.cpp b/lib/scheduler/common_scheduling/si_message_scheduler.cpp index a5b5e6305b..d2f61bf8e4 100644 --- a/lib/scheduler/common_scheduling/si_message_scheduler.cpp +++ b/lib/scheduler/common_scheduling/si_message_scheduler.cpp @@ -80,16 +80,21 @@ void si_message_scheduler::update_si_message_windows(slot_point sl_tx) // Check for SI window start, as per TS 38.331, Section 5.2.2.3.2. - // 2> for the concerned SI message, determine the number n which corresponds to the order of entry in the list of SI - // messages configured by schedulingInfoList in si-SchedulingInfo in SIB1; + // 2> For the concerned SI message, determine the number n which corresponds to the order of entry in the list of SI + // messages configured by schedulingInfoList in si-SchedulingInfo in SIB1. const unsigned n = i + 1; - // 2> determine the integer value x = (n – 1) × w, where w is the si-WindowLength - const unsigned x = (n - 1) * si_sched_cfg->si_window_len_slots; + // 3> Determine the integer value x = (n – 1) × w, where w is the si-WindowLength. + unsigned x = (n - 1) * si_sched_cfg->si_window_len_slots; + if (si_msg.si_window_position.has_value()) { + // 3> Determine the integer value x = (si-WindowPosition -1) × w, where w is the si-WindowLength. See TS 38 331 + // V17.0.0. + x = (si_msg.si_window_position.value() - 1) * si_sched_cfg->si_window_len_slots; + } - // 2> the SI-window starts at the slot #a, where a = x mod N, in the radio frame for which SFN mod T = FLOOR(x/N), + // 3> The SI-window starts at the slot #a, where a = x mod N, in the radio frame for which SFN mod T = FLOOR(x/N), // where T is the si-Periodicity of the concerned SI message and N is the number of slots in a radio frame as - // specified in TS 38.213 + // specified in TS 38.213. const unsigned N = sl_tx.nof_slots_per_frame(); const unsigned a = x % N; if (sl_tx.slot_index() != a) { diff --git a/lib/scheduler/config/sched_cell_config_helpers.cpp b/lib/scheduler/config/sched_cell_config_helpers.cpp index 9944e8e062..07a13171b5 100644 --- a/lib/scheduler/config/sched_cell_config_helpers.cpp +++ b/lib/scheduler/config/sched_cell_config_helpers.cpp @@ -43,31 +43,32 @@ srsran::config_helpers::build_pucch_guardbands_list(const pucch_builder_params& srsran_assert(not res_list.empty(), "The PUCCH resource list cannot be empty"); - std::vector pucch_guardbands{}; + std::vector pucch_guardbands; auto list_contains_resource = [&pucch_guardbands](const sched_grid_resource& res) { return std::find(pucch_guardbands.begin(), pucch_guardbands.end(), res) != pucch_guardbands.end(); }; for (const auto& pucch_res : res_list) { - srsran_assert(variant_holds_alternative(pucch_res.format_params) or - variant_holds_alternative(pucch_res.format_params), + srsran_assert(std::holds_alternative(pucch_res.format_params) or + std::holds_alternative(pucch_res.format_params), "Only PUCCH format 1 and 2 are currently supported"); - const unsigned starting_sym = variant_holds_alternative(pucch_res.format_params) - ? variant_get(pucch_res.format_params).starting_sym_idx - : variant_get(pucch_res.format_params).starting_sym_idx; - const unsigned nof_symbols = variant_holds_alternative(pucch_res.format_params) - ? variant_get(pucch_res.format_params).nof_symbols - : variant_get(pucch_res.format_params).nof_symbols; + const unsigned starting_sym = std::holds_alternative(pucch_res.format_params) + ? std::get(pucch_res.format_params).starting_sym_idx + : std::get(pucch_res.format_params).starting_sym_idx; + const unsigned nof_symbols = std::holds_alternative(pucch_res.format_params) + ? std::get(pucch_res.format_params).nof_symbols + : std::get(pucch_res.format_params).nof_symbols; // For PUCCH format 1, the resource has 1 PRB only. - const unsigned nof_prbs = variant_holds_alternative(pucch_res.format_params) + const unsigned nof_prbs = std::holds_alternative(pucch_res.format_params) ? 1U - : variant_get(pucch_res.format_params).nof_prbs; + : std::get(pucch_res.format_params).nof_prbs; // In the following, \c res_no_freq_hop contains the PRBs/symbols of the PUCCH resource with no frequency hopping, // or, if frequency hopping is enabled, the PRBs/symbols of the first hop. // \c res_freq_hop is only used if frequency hopping is enabled and contains the PRBs/symbols of the second hop. - sched_grid_resource res_no_freq_hop, res_freq_hop; + sched_grid_resource res_no_freq_hop; + sched_grid_resource res_freq_hop; res_no_freq_hop.prbs.set(pucch_res.starting_prb, pucch_res.starting_prb + nof_prbs); if (pucch_res.second_hop_prb.has_value()) { diff --git a/lib/scheduler/config/serving_cell_config_factory.cpp b/lib/scheduler/config/serving_cell_config_factory.cpp index e26859a783..d7e3a1197f 100644 --- a/lib/scheduler/config/serving_cell_config_factory.cpp +++ b/lib/scheduler/config/serving_cell_config_factory.cpp @@ -580,36 +580,36 @@ uplink_config srsran::config_helpers::make_default_ue_uplink_config(const cell_c pucch_cfg.pucch_res_list.push_back(res_basic_f2); // >>> PUCCH resource 4. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res4 = pucch_cfg.pucch_res_list.back(); - res4.res_id = pucch_res_id_t{4, 4}; - variant_get(res4.format_params).starting_sym_idx = 2; + pucch_resource& res4 = pucch_cfg.pucch_res_list.back(); + res4.res_id = pucch_res_id_t{4, 4}; + std::get(res4.format_params).starting_sym_idx = 2; // >>> PUCCH resource 5. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res5 = pucch_cfg.pucch_res_list.back(); - res5.res_id = pucch_res_id_t{5, 5}; - variant_get(res5.format_params).starting_sym_idx = 4; + pucch_resource& res5 = pucch_cfg.pucch_res_list.back(); + res5.res_id = pucch_res_id_t{5, 5}; + std::get(res5.format_params).starting_sym_idx = 4; // >>> PUCCH resource 6. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res6 = pucch_cfg.pucch_res_list.back(); - res6.res_id = pucch_res_id_t{6, 6}; - variant_get(res6.format_params).starting_sym_idx = 6; + pucch_resource& res6 = pucch_cfg.pucch_res_list.back(); + res6.res_id = pucch_res_id_t{6, 6}; + std::get(res6.format_params).starting_sym_idx = 6; // >>> PUCCH resource 7. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res7 = pucch_cfg.pucch_res_list.back(); - res7.res_id = pucch_res_id_t{7, 7}; - variant_get(res7.format_params).starting_sym_idx = 8; + pucch_resource& res7 = pucch_cfg.pucch_res_list.back(); + res7.res_id = pucch_res_id_t{7, 7}; + std::get(res7.format_params).starting_sym_idx = 8; // >>> PUCCH resource 8. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res8 = pucch_cfg.pucch_res_list.back(); - res8.res_id = pucch_res_id_t{8, 8}; - variant_get(res8.format_params).starting_sym_idx = 10; + pucch_resource& res8 = pucch_cfg.pucch_res_list.back(); + res8.res_id = pucch_res_id_t{8, 8}; + std::get(res8.format_params).starting_sym_idx = 10; // PUCCH resource format 2, for CSI and optionally for SR. // >>> PUCCH resource 9. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res9 = pucch_cfg.pucch_res_list.back(); - res9.res_id = pucch_res_id_t{9, 9}; - variant_get(res9.format_params).starting_sym_idx = 12; + pucch_resource& res9 = pucch_cfg.pucch_res_list.back(); + res9.res_id = pucch_res_id_t{9, 9}; + std::get(res9.format_params).starting_sym_idx = 12; // PUCCH resource format 1, for SR only. // >>> PUCCH resource 10. diff --git a/lib/scheduler/config/serving_cell_config_validator.cpp b/lib/scheduler/config/serving_cell_config_validator.cpp index 51af99b903..4dffcbffee 100644 --- a/lib/scheduler/config/serving_cell_config_validator.cpp +++ b/lib/scheduler/config/serving_cell_config_validator.cpp @@ -64,6 +64,7 @@ validator_result srsran::config_validators::validate_pdcch_cfg(const serving_cel ss.get_id()); } } + // TODO: Validate other parameters. return {}; } @@ -106,6 +107,7 @@ static validator_result validate_zp_csi_rs(const serving_cell_config& ue_cell_cf csi_im.freq_band_rbs, csi_im.csi_im_res_element_pattern->symbol_location); } + return {}; } @@ -166,11 +168,11 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel // Verify each resource format matches the corresponding parameters. for (auto res : pucch_cfg.pucch_res_list) { const bool format_match_format_params = - (res.format == pucch_format::FORMAT_0 and variant_holds_alternative(res.format_params)) or - (res.format == pucch_format::FORMAT_1 and variant_holds_alternative(res.format_params)) or - (res.format == pucch_format::FORMAT_2 and variant_holds_alternative(res.format_params)) or - (res.format == pucch_format::FORMAT_3 and variant_holds_alternative(res.format_params)) or - (res.format == pucch_format::FORMAT_4 and variant_holds_alternative(res.format_params)); + (res.format == pucch_format::FORMAT_0 and std::holds_alternative(res.format_params)) or + (res.format == pucch_format::FORMAT_1 and std::holds_alternative(res.format_params)) or + (res.format == pucch_format::FORMAT_2 and std::holds_alternative(res.format_params)) or + (res.format == pucch_format::FORMAT_3 and std::holds_alternative(res.format_params)) or + (res.format == pucch_format::FORMAT_4 and std::holds_alternative(res.format_params)); VERIFY(format_match_format_params, "PUCCH cell res id={} format does not match the PUCCH format parameters", res.res_id.cell_res_id); @@ -205,14 +207,14 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel if (ue_cell_cfg.csi_meas_cfg.has_value()) { const auto& csi_cfg = ue_cell_cfg.csi_meas_cfg.value(); VERIFY(not csi_cfg.csi_report_cfg_list.empty() and - variant_holds_alternative( + std::holds_alternative( csi_cfg.csi_report_cfg_list.front().report_cfg_type) and - not variant_get( + not std::get( csi_cfg.csi_report_cfg_list.front().report_cfg_type) .pucch_csi_res_list.empty(), "PUCCH-CSI-ResourceList has not been configured in the CSI-reportConfig"); - const auto& csi = variant_get( + const auto& csi = std::get( csi_cfg.csi_report_cfg_list.front().report_cfg_type); const unsigned csi_res_id = csi.pucch_csi_res_list.front().pucch_res_id.cell_res_id; // Verify the PUCCH resource id that indicated in the CSI resource config exists in the PUCCH resource list. @@ -224,7 +226,7 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel "PUCCH resource used for CSI is expected to be Format 2"); // Verify the CSI/SR bits do not exceed the PUCCH F2 payload. - const auto& csi_pucch_res_params = variant_get(csi_pucch_res_id->format_params); + const auto& csi_pucch_res_params = std::get(csi_pucch_res_id->format_params); const unsigned pucch_f2_max_payload = get_pucch_format2_max_payload(csi_pucch_res_params.nof_prbs, csi_pucch_res_params.nof_symbols, @@ -253,7 +255,7 @@ validator_result srsran::config_validators::validate_pucch_cfg(const serving_cel 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& harq_f2_pucch_res_params = variant_get(res_f2_it->format_params); + 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, harq_f2_pucch_res_params.nof_symbols, @@ -299,9 +301,9 @@ srsran::config_validators::validate_nzp_csi_rs_list(spancsi_res_offset.has_value() and it->csi_res_period.has_value()) { - for (auto it2 = it + 1; it2 != nzp_csi_rs_res_list.end(); ++it2) { + for (const auto* it2 = it + 1; it2 != nzp_csi_rs_res_list.end(); ++it2) { if (it2->csi_res_offset.has_value() and it2->csi_res_period.has_value()) { VERIFY(it->csi_res_period.value() == it2->csi_res_period.value(), "NZP-CSI-RS resources with different periods not supported"); @@ -418,8 +420,8 @@ srsran::config_validators::validate_csi_meas_cfg(const serving_cell_config& // CSI-ResourceConfig. for (const auto& res_cfg : csi_meas_cfg.csi_res_cfg_list) { - if (variant_holds_alternative(res_cfg.csi_rs_res_set_list)) { - const auto& variant_value = variant_get(res_cfg.csi_rs_res_set_list); + if (std::holds_alternative(res_cfg.csi_rs_res_set_list)) { + const auto& variant_value = std::get(res_cfg.csi_rs_res_set_list); for (const auto& res_set_id : variant_value.nzp_csi_rs_res_set_list) { VERIFY_ID_EXISTS([res_set_id](const nzp_csi_rs_resource_set& rhs) { return rhs.res_set_id == res_set_id; }, csi_meas_cfg.nzp_csi_rs_res_set_list, @@ -432,9 +434,8 @@ srsran::config_validators::validate_csi_meas_cfg(const serving_cell_config& "CSI SSB resource set id={} does not exist", res_set_id); } - } else if (variant_holds_alternative(res_cfg.csi_rs_res_set_list)) { - const auto& variant_value = - variant_get(res_cfg.csi_rs_res_set_list); + } else if (std::holds_alternative(res_cfg.csi_rs_res_set_list)) { + const auto& variant_value = std::get(res_cfg.csi_rs_res_set_list); for (const auto& res_set_id : variant_value) { VERIFY_ID_EXISTS([res_set_id](const csi_im_resource_set& rhs) { return rhs.res_set_id == res_set_id; }, csi_meas_cfg.csi_im_res_set_list, @@ -473,10 +474,10 @@ srsran::config_validators::validate_csi_meas_cfg(const serving_cell_config& nzp_csi_rs_res_for_interference); } - if (variant_holds_alternative( + if (std::holds_alternative( rep_cfg.report_cfg_type)) { const auto& pucch_csi = - variant_get(rep_cfg.report_cfg_type); + std::get(rep_cfg.report_cfg_type); VERIFY(ue_cell_cfg.ul_config.has_value(), "Cell does not define a UL Config"); VERIFY(ue_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.has_value(), "Cell={} does not define a PUCCH Config"); const auto& pucch_resources = ue_cell_cfg.ul_config.value().init_ul_bwp.pucch_cfg.value().pucch_res_list; diff --git a/lib/scheduler/config/ue_configuration.cpp b/lib/scheduler/config/ue_configuration.cpp index 9d5221711c..214af7e934 100644 --- a/lib/scheduler/config/ue_configuration.cpp +++ b/lib/scheduler/config/ue_configuration.cpp @@ -21,12 +21,14 @@ */ #include "ue_configuration.h" + #include "../support/pdcch/pdcch_mapping.h" #include "../support/pdsch/pdsch_default_time_allocation.h" #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 using namespace srsran; @@ -51,11 +53,13 @@ void search_space_info::update_pdcch_candidates( ss_pdcch_candidates = candidates; crbs_of_candidates.resize(ss_pdcch_candidates.size()); - for (unsigned sl = 0; sl < ss_pdcch_candidates.size(); sl++) { - for (unsigned lidx = 0; lidx < ss_pdcch_candidates[sl].size(); lidx++) { + for (unsigned sl = 0, sl_end = ss_pdcch_candidates.size(); sl != sl_end; ++sl) { + for (unsigned lidx = 0, lidx_end = ss_pdcch_candidates[sl].size(); lidx != lidx_end; ++lidx) { const aggregation_level aggr_lvl = aggregation_index_to_level(lidx); crbs_of_candidates[sl][lidx].resize(ss_pdcch_candidates[sl][lidx].size()); - for (unsigned candidate_idx = 0; candidate_idx != ss_pdcch_candidates[sl][lidx].size(); ++candidate_idx) { + for (unsigned candidate_idx = 0, candidate_idx_end = ss_pdcch_candidates[sl][lidx].size(); + candidate_idx != candidate_idx_end; + ++candidate_idx) { uint8_t ncce = ss_pdcch_candidates[sl][lidx][candidate_idx]; auto& crb_list = crbs_of_candidates[sl][lidx][candidate_idx]; @@ -85,7 +89,7 @@ static bool is_dci_format_monitored_in_ue_ss(const ue_cell_configuration& ue_cel ? search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0 : search_space_configuration::ue_specific_dci_format::f0_1_and_1_1; return std::any_of(bwp_search_spaces.begin(), bwp_search_spaces.end(), [dci_format](const search_space_info* ss) { - return (not ss->cfg->is_common_search_space()) and (variant_get( + return (not ss->cfg->is_common_search_space()) and (std::get( ss->cfg->get_monitored_dci_formats()) == dci_format); }); } @@ -162,7 +166,7 @@ static dci_size_config get_dci_size_config(const ue_cell_configuration& ue_cell_ } if (opt_pusch_cfg.value().uci_cfg.has_value() and opt_pusch_cfg.value().uci_cfg.value().beta_offsets_cfg.has_value() and - variant_holds_alternative( + std::holds_alternative( opt_pusch_cfg.value().uci_cfg.value().beta_offsets_cfg.value())) { dci_sz_cfg.dynamic_beta_offsets = true; } @@ -270,7 +274,7 @@ static dci_size_config get_dci_size_config(const ue_cell_configuration& ue_cell_ dci_sz_cfg.pdsch_res_allocation_type = resource_allocation::resource_allocation_type_1; if (opt_pdsch_cfg.has_value()) { dci_sz_cfg.dynamic_prb_bundling = - variant_holds_alternative(opt_pdsch_cfg.value().prb_bndlg.bundling); + std::holds_alternative(opt_pdsch_cfg.value().prb_bndlg.bundling); dci_sz_cfg.pdsch_two_codewords = opt_pdsch_cfg.value().is_max_cw_sched_by_dci_is_two; switch (opt_pdsch_cfg.value().res_alloc) { case pdsch_config::resource_allocation::resource_allocation_type_0: { @@ -340,9 +344,7 @@ static unsigned compute_nof_dl_ports(const serving_cell_config& serv_cell_cfg) unsigned max_ports = 1; for (const auto& nzp : serv_cell_cfg.csi_meas_cfg->nzp_csi_rs_res_list) { - if (max_ports < nzp.res_mapping.nof_ports) { - max_ports = nzp.res_mapping.nof_ports; - } + max_ports = std::max(max_ports, nzp.res_mapping.nof_ports); } return max_ports; } @@ -356,11 +358,13 @@ static units::bits get_dl_dci_size(const search_space_info& ss_info) : ss_info.dci_sz.format1_1_ue_size->total; } +namespace { /// List of PDCCH candidates for different SearchSpaces of a BWP for different slot indexes. using frame_pdcch_candidate_list = slotted_id_table>, MAX_NOF_SEARCH_SPACE_PER_BWP>; +} // namespace /// \brief Implement TS 38.213, section 10.1 rules to remove ambiguous PDCCH candidates. /// @@ -414,7 +418,7 @@ static void remove_ambiguous_pdcch_candidates(frame_pdcch_candidate_list& candid pdcch_candidate_list& ss_candidates2 = candidates[ss2.cfg->get_id()][slot_index][i]; for (const uint8_t candidate1 : ss_candidates1) { - for (auto it2 = ss_candidates2.begin(); it2 != ss_candidates2.end();) { + for (auto* it2 = ss_candidates2.begin(); it2 != ss_candidates2.end();) { if (*it2 == candidate1) { // Case: *candidate_it1 is PDCCH candidate with index m^{(L)}_{s_i,n_{CI}} (See above). it2 = ss_candidates2.erase(it2); @@ -434,9 +438,9 @@ static void remove_ambiguous_pdcch_candidates(frame_pdcch_candidate_list& candid static void apply_pdcch_candidate_monitoring_limits(frame_pdcch_candidate_list& candidates, const bwp_info& bwp) { // TS 38.213, Table 10.1-3 - Maximum number of non-overlapped CCEs. - const static std::array max_non_overlapped_cces_per_slot = {56, 56, 48, 32}; + static const std::array max_non_overlapped_cces_per_slot = {56, 56, 48, 32}; // Maximum nof. CCEs in a CORESET = maximum PDCCH frequency resources * maximum CORESET duration. - const static unsigned maximum_nof_cces = + static const unsigned maximum_nof_cces = pdcch_constants::MAX_NOF_FREQ_RESOURCES * pdcch_constants::MAX_CORESET_DURATION; const unsigned max_pdcch_candidates = max_nof_monitored_pdcch_candidates(bwp.dl_common->generic_params.scs); @@ -465,22 +469,22 @@ static void apply_pdcch_candidate_monitoring_limits(frame_pdcch_candidate_list& // TODO: Account for multiple SSB beams. std::array, NOF_OFDM_SYM_PER_SLOT_NORMAL_CP>, MAX_NOF_CORESETS_PER_BWP> cce_bitmap_per_coreset_per_first_pdcch_symb; - for (unsigned cs_idx = 0; cs_idx < MAX_NOF_CORESETS_PER_BWP; ++cs_idx) { - for (unsigned symb = 0; symb < NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; ++symb) { + for (unsigned cs_idx = 0; cs_idx != MAX_NOF_CORESETS_PER_BWP; ++cs_idx) { + for (unsigned symb = 0; symb != NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; ++symb) { cce_bitmap_per_coreset_per_first_pdcch_symb[cs_idx][symb].resize(maximum_nof_cces); cce_bitmap_per_coreset_per_first_pdcch_symb[cs_idx][symb].reset(); } } auto get_cce_monitored_sum = [&cce_bitmap_per_coreset_per_first_pdcch_symb]() { unsigned monitored_cces = 0; - for (unsigned cs_idx = 0; cs_idx < MAX_NOF_CORESETS_PER_BWP; ++cs_idx) { - for (unsigned symb = 0; symb < NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; ++symb) { + for (unsigned cs_idx = 0; cs_idx != MAX_NOF_CORESETS_PER_BWP; ++cs_idx) { + for (unsigned symb = 0; symb != NOF_OFDM_SYM_PER_SLOT_NORMAL_CP; ++symb) { monitored_cces += cce_bitmap_per_coreset_per_first_pdcch_symb[cs_idx][symb].count(); } } return monitored_cces; }; - for (unsigned ss1_idx = 0; ss1_idx < bwp.search_spaces.size(); ++ss1_idx) { + for (unsigned ss1_idx = 0, ss1_idx_end = bwp.search_spaces.size(); ss1_idx != ss1_idx_end; ++ss1_idx) { const search_space_info* ss1 = bwp.search_spaces[static_cast(ss1_idx)]; for (unsigned i = 0; i != NOF_AGGREGATION_LEVELS; ++i) { pdcch_candidate_list& ss_candidates = candidates[ss1->cfg->get_id()][slot_index][i]; @@ -533,7 +537,7 @@ static void generate_crnti_monitored_pdcch_candidates(bwp_info& bwp_cfg, rnti_t auto& ss_candidates = candidates[ss->cfg->get_id()]; ss_candidates.resize(max_slot_periodicity); - for (unsigned slot_count = 0; slot_count != ss_candidates.size(); ++slot_count) { + for (unsigned slot_count = 0, slot_count_end = ss_candidates.size(); slot_count != slot_count_end; ++slot_count) { const slot_point slot_mod{bwp_cfg.dl_common->generic_params.scs, slot_count}; // If the SearchSpace is not monitored in this slot, skip its candidate generation. @@ -548,15 +552,16 @@ static void generate_crnti_monitored_pdcch_candidates(bwp_info& bwp_cfg, rnti_t ss_candidates[slot_count][i] = pdcch_candidates_common_ss_get_lowest_cce(pdcch_candidates_common_ss_configuration{ aggr_lvl, ss->cfg->get_nof_candidates()[i], ss->coreset->get_nof_cces()}); - } else { - ss_candidates[slot_count][i] = pdcch_candidates_ue_ss_get_lowest_cce( - pdcch_candidates_ue_ss_configuration{aggr_lvl, - ss->cfg->get_nof_candidates()[i], - ss->coreset->get_nof_cces(), - ss->coreset->id, - crnti, - slot_mod.slot_index()}); + continue; } + + ss_candidates[slot_count][i] = + pdcch_candidates_ue_ss_get_lowest_cce(pdcch_candidates_ue_ss_configuration{aggr_lvl, + ss->cfg->get_nof_candidates()[i], + ss->coreset->get_nof_cces(), + ss->coreset->id, + crnti, + slot_mod.slot_index()}); } } } @@ -798,15 +803,15 @@ void ue_configuration::update(const cell_common_configuration_list& common_cells } // Check for cells that have been added/modified. - for (unsigned i = 0; i != cfg_req.cells->size(); ++i) { + for (unsigned i = 0, e = cfg_req.cells->size(); i != e; ++i) { const cell_config_dedicated& ded_cell = cfg_req.cells.value()[i]; const du_cell_index_t cell_index = ded_cell.serv_cell_cfg.cell_index; if (not du_cells.contains(cell_index)) { // New Cell. - du_cells.emplace(cell_index, - std::make_unique( - crnti, *common_cells[cell_index], ded_cell.serv_cell_cfg, cfg_req.cells->size() > 1)); + du_cells.emplace( + cell_index, + std::make_unique(crnti, *common_cells[cell_index], ded_cell.serv_cell_cfg, e > 1)); } else { // Reconfiguration of existing cell. du_cells[cell_index]->reconfigure(ded_cell.serv_cell_cfg); @@ -815,14 +820,14 @@ void ue_configuration::update(const cell_common_configuration_list& common_cells // Update UE cell to DU cell indexing ue_cell_to_du_cell_index.resize(cfg_req.cells.value().size()); - for (unsigned i = 0; i != cfg_req.cells->size(); ++i) { + for (unsigned i = 0, e = cfg_req.cells->size(); i != e; ++i) { ue_cell_to_du_cell_index[i] = cfg_req.cells.value()[i].serv_cell_cfg.cell_index; } } // Update RRM policies for the UE. if (cfg_req.res_alloc_cfg.has_value()) { - for (unsigned i = 0; i != nof_cells(); ++i) { + for (unsigned i = 0, e = nof_cells(); i != e; ++i) { du_cells[ue_cell_to_du_cell_index[i]]->set_rrm_config(*cfg_req.res_alloc_cfg); } } diff --git a/lib/scheduler/logging/scheduler_event_logger.cpp b/lib/scheduler/logging/scheduler_event_logger.cpp index f4943a8e95..ad2e670c92 100644 --- a/lib/scheduler/logging/scheduler_event_logger.cpp +++ b/lib/scheduler/logging/scheduler_event_logger.cpp @@ -25,25 +25,27 @@ using namespace srsran; -void scheduler_event_logger::log_impl() +scheduler_event_logger::scheduler_event_logger(du_cell_index_t cell_index_, pci_t pci_) : + cell_index(cell_index_), + pci(pci_), + logger(srslog::fetch_basic_logger("SCHED")), + mode(logger.debug.enabled() ? mode_t::debug : (logger.info.enabled() ? mode_t::info : mode_t::none)) { if (mode == debug) { - logger.debug("Processed slot events:{}", to_c_str(fmtbuf)); - fmtbuf.clear(); + fmt::format_to(fmtbuf, "\n- Cell creation: idx={} pci={}", cell_index, pci); } else if (mode == info) { - logger.info("Processed slot events:{}", to_c_str(fmtbuf)); - fmtbuf.clear(); + fmt::format_to(fmtbuf, "{}Cell creation idx={} pci={}", separator(), cell_index, pci); } } -void scheduler_event_logger::enqueue_impl(const cell_creation_event& cell_ev) +void scheduler_event_logger::log_impl() { - cell_pcis[cell_ev.cell_index] = cell_ev.pci; - if (mode == debug) { - fmt::format_to(fmtbuf, "\n- Cell creation: idx={} pci={}", cell_ev.cell_index, cell_ev.pci); + logger.debug("Processed slot events pci={}:{}", pci, to_c_str(fmtbuf)); + fmtbuf.clear(); } else if (mode == info) { - fmt::format_to(fmtbuf, "{}Cell creation idx={} pci={}", separator(), cell_ev.cell_index, cell_ev.pci); + logger.info("Processed slot events pci={}:{}", pci, to_c_str(fmtbuf)); + fmtbuf.clear(); } } @@ -51,7 +53,7 @@ void scheduler_event_logger::enqueue_impl(const prach_event& rach_ev) { if (mode == debug) { fmt::format_to(fmtbuf, - "\n- PRACH: slot={}, pci={} preamble={} ra-rnti={} temp_crnti={} ta_cmd={}", + "\n- PRACH: slot={} pci={} preamble={} ra-rnti={} temp_crnti={} ta_cmd={}", rach_ev.slot_rx, cell_pcis[rach_ev.cell_index], rach_ev.preamble_id, diff --git a/lib/scheduler/logging/scheduler_event_logger.h b/lib/scheduler/logging/scheduler_event_logger.h index e8b9209bfe..baa35538f9 100644 --- a/lib/scheduler/logging/scheduler_event_logger.h +++ b/lib/scheduler/logging/scheduler_event_logger.h @@ -31,10 +31,6 @@ namespace srsran { class scheduler_event_logger { public: - struct cell_creation_event { - du_cell_index_t cell_index; - pci_t pci; - }; struct prach_event { slot_point slot_rx; du_cell_index_t cell_index; @@ -102,11 +98,7 @@ class scheduler_event_logger scheduler_slot_handler::error_outcome outcome; }; - scheduler_event_logger() : - logger(srslog::fetch_basic_logger("SCHED")), - mode(logger.debug.enabled() ? mode_t::debug : (logger.info.enabled() ? mode_t::info : mode_t::none)) - { - } + scheduler_event_logger(du_cell_index_t cell_index_, pci_t pci_); void log() { @@ -134,8 +126,6 @@ class scheduler_event_logger void log_impl(); - void enqueue_impl(const cell_creation_event& cell_ev); - void enqueue_impl(const prach_event& rach_ev); void enqueue_impl(const rach_indication_message& rach_ind); @@ -155,6 +145,8 @@ class scheduler_event_logger void enqueue_impl(const dl_buffer_state_indication_message& bs); void enqueue_impl(const phr_event& phr_ev); + du_cell_index_t cell_index; + pci_t pci; srslog::basic_logger& logger; mode_t mode = none; diff --git a/lib/scheduler/logging/scheduler_metric_handler.cpp b/lib/scheduler/logging/scheduler_metric_handler.cpp index 618da76622..cd0ba57f6f 100644 --- a/lib/scheduler/logging/scheduler_metric_handler.cpp +++ b/lib/scheduler/logging/scheduler_metric_handler.cpp @@ -69,7 +69,7 @@ void scheduler_metrics_handler::handle_crc_indication(const ul_crc_pdu_indicatio u.data.sum_ul_tb_bytes += tbs.value(); } if (crc_pdu.time_advance_offset.has_value()) { - u.last_ta = crc_pdu.time_advance_offset->to_seconds(); + u.last_ta = crc_pdu.time_advance_offset; } } } @@ -119,33 +119,29 @@ void scheduler_metrics_handler::handle_uci_pdu_indication(const uci_indication:: if (ues.contains(pdu.ue_index)) { auto& u = ues[pdu.ue_index]; - if (variant_holds_alternative(pdu.pdu)) { - auto& f1 = variant_get(pdu.pdu); - - if (f1.ul_sinr_dB.has_value()) { - handle_pucch_sinr(u, f1.ul_sinr_dB.value()); + if (const auto* f1 = std::get_if(&pdu.pdu)) { + if (f1->ul_sinr_dB.has_value()) { + handle_pucch_sinr(u, f1->ul_sinr_dB.value()); } - if (f1.time_advance_offset.has_value()) { - u.last_ta = f1.time_advance_offset.value().to_seconds(); + if (f1->time_advance_offset.has_value()) { + u.last_ta = f1->time_advance_offset; } - } else if (variant_holds_alternative(pdu.pdu)) { - auto& f2 = variant_get(pdu.pdu); - - if (f2.ul_sinr_dB.has_value()) { - handle_pucch_sinr(u, f2.ul_sinr_dB.value()); + } else if (const auto* f2 = std::get_if(&pdu.pdu)) { + if (f2->ul_sinr_dB.has_value()) { + handle_pucch_sinr(u, f2->ul_sinr_dB.value()); } - if (f2.csi.has_value()) { - handle_csi_report(u, f2.csi.value()); + if (f2->csi.has_value()) { + handle_csi_report(u, f2->csi.value()); } - if (f2.time_advance_offset.has_value()) { - u.last_ta = f2.time_advance_offset.value().to_seconds(); + if (f2->time_advance_offset.has_value()) { + u.last_ta = f2->time_advance_offset; } } else { // PUSCH case. - auto& pusch = variant_get(pdu.pdu); + const auto& pusch = std::get(pdu.pdu); if (pusch.csi.has_value()) { handle_csi_report(u, pusch.csi.value()); @@ -214,7 +210,7 @@ void scheduler_metrics_handler::handle_slot_result(const sched_result& slot_resu continue; } ue_metric_context& u = ues[it->second]; - for (auto& cw : dl_grant.pdsch_cfg.codewords) { + for (const auto& cw : dl_grant.pdsch_cfg.codewords) { u.data.dl_mcs += cw.mcs_index.to_uint(); u.data.nof_dl_cws++; } @@ -294,8 +290,8 @@ scheduler_metrics_handler::ue_metric_context::compute_report(std::chrono::millis for (const unsigned value : last_dl_bs) { ret.dl_bs += value; } - if (last_ta >= 0) { - ret.last_ta = std::chrono::microseconds{static_cast(last_ta * 1e6)}; + if (last_ta.has_value()) { + ret.last_ta = last_ta; } ret.last_phr = last_phr; diff --git a/lib/scheduler/logging/scheduler_metrics_handler.h b/lib/scheduler/logging/scheduler_metrics_handler.h index f1c15eaff3..6853806811 100644 --- a/lib/scheduler/logging/scheduler_metrics_handler.h +++ b/lib/scheduler/logging/scheduler_metrics_handler.h @@ -38,44 +38,6 @@ class scheduler_metrics_handler final : public harq_timeout_handler, public sche using msecs = std::chrono::milliseconds; using usecs = std::chrono::microseconds; -public: - /// \brief Creates a scheduler UE metrics handler. In case the metrics_report_period is zero, no metrics are reported. - explicit scheduler_metrics_handler(msecs metrics_report_period, scheduler_ue_metrics_notifier& notifier); - - /// \brief Register creation of a UE. - void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci, unsigned num_prbs) override; - - /// \brief Register removal of a UE. - void handle_ue_deletion(du_ue_index_t ue_index) override; - - /// \brief Register CRC indication. - void handle_crc_indication(const ul_crc_pdu_indication& crc_pdu, units::bytes tbs); - - /// \brief Register HARQ-ACK UCI indication. - void handle_dl_harq_ack(du_ue_index_t ue_index, bool ack, units::bytes tbs); - - /// \brief Register HARQ timeout. - void handle_harq_timeout(du_ue_index_t ue_index, bool is_dl) override; - - /// \brief Handle UCI PDU indication. - void handle_uci_pdu_indication(const uci_indication::uci_pdu& pdu); - - /// \brief Handle UL BSR indication. - void handle_ul_bsr_indication(const ul_bsr_indication_message& bsr); - - /// \brief Handle UL PHR indication. - void handle_ul_phr_indication(const ul_phr_indication_message& phr_ind); - - /// \brief Handle DL Buffer Status indication. - void handle_dl_buffer_state_indication(const dl_buffer_state_indication_message& dl_bs); - - /// \brief Handle results stored in the scheduler result and push new entry. - void push_result(slot_point sl_tx, const sched_result& slot_result); - - /// \brief Checks whether the metrics reporting is active. - bool connected() const { return report_period != std::chrono::nanoseconds{0}; } - -private: struct ue_metric_context { /// \brief In this struct we store all the metadata that is reset at every report. struct non_persistent_data { @@ -98,6 +60,11 @@ class scheduler_metrics_handler final : public harq_timeout_handler, public sche unsigned dl_prbs_used = 0; unsigned ul_prbs_used = 0; }; + + // This user provided constructor is added here to fix a Clang compilation error related to the use of nested types + // with std::optional. + ue_metric_context() {} + pci_t pci; unsigned nof_prbs; du_ue_index_t ue_index; @@ -106,33 +73,66 @@ class scheduler_metrics_handler final : public harq_timeout_handler, public sche uint8_t last_ri = 1; unsigned last_bsr = 0; std::optional last_phr; - double last_ta = -1; + std::optional last_ta; std::array last_dl_bs{0}; non_persistent_data data; scheduler_ue_metrics compute_report(std::chrono::milliseconds metric_report_period); - - void reset(); + void reset(); }; - void handle_pucch_sinr(ue_metric_context& u, float sinr); - void handle_csi_report(ue_metric_context& u, const csi_report_data& csi); - - void report_metrics(); - void handle_slot_result(const sched_result& slot_result); - scheduler_ue_metrics_notifier& notifier; const std::chrono::milliseconds report_period; - - // derived. + /// Derived value. unsigned report_period_slots = 0; - /// \brief This type is used so that multiple threads can access different positions concurrently. slotted_id_table ues; std::unordered_map rnti_to_ue_index_lookup; - - // \brief Counter of number of slots elapsed since the last report. + /// \brief Counter of number of slots elapsed since the last report. unsigned slot_counter = 0; + +public: + /// \brief Creates a scheduler UE metrics handler. In case the metrics_report_period is zero, no metrics are reported. + explicit scheduler_metrics_handler(msecs metrics_report_period, scheduler_ue_metrics_notifier& notifier); + + /// \brief Register creation of a UE. + void handle_ue_creation(du_ue_index_t ue_index, rnti_t rnti, pci_t pcell_pci, unsigned num_prbs) override; + + /// \brief Register removal of a UE. + void handle_ue_deletion(du_ue_index_t ue_index) override; + + /// \brief Register CRC indication. + void handle_crc_indication(const ul_crc_pdu_indication& crc_pdu, units::bytes tbs); + + /// \brief Register HARQ-ACK UCI indication. + void handle_dl_harq_ack(du_ue_index_t ue_index, bool ack, units::bytes tbs); + + /// \brief Register HARQ timeout. + void handle_harq_timeout(du_ue_index_t ue_index, bool is_dl) override; + + /// \brief Handle UCI PDU indication. + void handle_uci_pdu_indication(const uci_indication::uci_pdu& pdu); + + /// \brief Handle UL BSR indication. + void handle_ul_bsr_indication(const ul_bsr_indication_message& bsr); + + /// \brief Handle UL PHR indication. + void handle_ul_phr_indication(const ul_phr_indication_message& phr_ind); + + /// \brief Handle DL Buffer Status indication. + void handle_dl_buffer_state_indication(const dl_buffer_state_indication_message& dl_bs); + + /// \brief Handle results stored in the scheduler result and push new entry. + void push_result(slot_point sl_tx, const sched_result& slot_result); + + /// \brief Checks whether the metrics reporting is active. + bool connected() const { return report_period != std::chrono::nanoseconds{0}; } + +private: + void handle_pucch_sinr(ue_metric_context& u, float sinr); + void handle_csi_report(ue_metric_context& u, const csi_report_data& csi); + void report_metrics(); + void handle_slot_result(const sched_result& slot_result); }; } // namespace srsran diff --git a/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp b/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp index dad883a749..7255af2e11 100644 --- a/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp +++ b/lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.cpp @@ -36,6 +36,7 @@ pdcch_resource_allocator_impl::pdcch_resource_allocator_impl(const cell_configur const coreset_configuration& cs_cfg = (ss.get_coreset_id() == to_coreset_id(0)) ? (*cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0) : cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.common_coreset.value(); + for (unsigned lidx = 0; lidx != NOF_AGGREGATION_LEVELS; ++lidx) { const aggregation_level aggr_lvl = aggregation_index_to_level(lidx); const unsigned nof_candidates = ss.get_nof_candidates()[lidx]; @@ -44,7 +45,7 @@ pdcch_resource_allocator_impl::pdcch_resource_allocator_impl(const cell_configur aggr_lvl_candidates.candidates = pdcch_candidates_common_ss_get_lowest_cce( pdcch_candidates_common_ss_configuration{aggr_lvl, nof_candidates, cs_cfg.get_nof_cces()}); aggr_lvl_candidates.candidate_crbs.resize(aggr_lvl_candidates.candidates.size()); - for (unsigned i = 0; i != aggr_lvl_candidates.candidates.size(); ++i) { + for (unsigned i = 0, e = aggr_lvl_candidates.candidates.size(); i != e; ++i) { aggr_lvl_candidates.candidate_crbs[i] = pdcch_helper::cce_to_prb_mapping( cell_cfg.dl_cfg_common.init_dl_bwp.generic_params, cs_cfg, cell_cfg.pci, aggr_lvl, i); @@ -56,7 +57,7 @@ pdcch_resource_allocator_impl::pdcch_resource_allocator_impl(const cell_configur } } - for (unsigned i = 0; i < SLOT_ALLOCATOR_RING_SIZE; ++i) { + for (unsigned i = 0; i != SLOT_ALLOCATOR_RING_SIZE; ++i) { slot_records[i] = std::make_unique(); } } @@ -83,12 +84,9 @@ pdcch_dl_information* pdcch_resource_allocator_impl::alloc_dl_pdcch_common(cell_ const bwp_configuration& bwp_cfg = cell_cfg.dl_cfg_common.init_dl_bwp.generic_params; const search_space_configuration& ss_cfg = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.search_spaces[(size_t)ss_id]; - const coreset_configuration* cs_cfg = nullptr; - if (ss_cfg.get_coreset_id() == to_coreset_id(0)) { - cs_cfg = &(*cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0); - } else { - cs_cfg = &cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.common_coreset.value(); - } + const coreset_configuration* cs_cfg = (ss_cfg.get_coreset_id() == to_coreset_id(0)) + ? &(*cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0) + : &cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.common_coreset.value(); return alloc_dl_pdcch_helper(slot_alloc, rnti, @@ -184,7 +182,7 @@ pdcch_ul_information* pdcch_resource_allocator_impl::alloc_ul_pdcch_helper(cell_ pdcch.ctx.context.ss_id = ss_cfg.get_id(); pdcch.ctx.context.dci_format = (ss_cfg.is_common_search_space() || - (variant_get(ss_cfg.get_monitored_dci_formats()) == + (std::get(ss_cfg.get_monitored_dci_formats()) == search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0)) ? "0_0" : "0_1"; @@ -233,7 +231,7 @@ pdcch_dl_information* pdcch_resource_allocator_impl::alloc_dl_pdcch_helper(cell_ pdcch.ctx.context.ss_id = ss_cfg.get_id(); pdcch.ctx.context.dci_format = (ss_cfg.is_common_search_space() || - (variant_get(ss_cfg.get_monitored_dci_formats()) == + (std::get(ss_cfg.get_monitored_dci_formats()) == search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0)) ? "1_0" : "1_1"; diff --git a/lib/scheduler/policy/scheduler_time_rr.cpp b/lib/scheduler/policy/scheduler_time_rr.cpp index a199471128..f5d6ecb2aa 100644 --- a/lib/scheduler/policy/scheduler_time_rr.cpp +++ b/lib/scheduler/policy/scheduler_time_rr.cpp @@ -23,7 +23,6 @@ #include "scheduler_time_rr.h" #include "../support/pdsch/pdsch_resource_allocation.h" #include "../support/pusch/pusch_td_resource_indices.h" -#include "../ue_scheduling/ue_pdsch_param_candidate_searcher.h" using namespace srsran; @@ -126,6 +125,110 @@ static unsigned compute_max_nof_rbs_per_ue_per_slot(const ue_repository& return (bwp_crb_limits.length() / nof_ues_to_be_scheduled_per_slot); } +/// \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_vector dl_harq_candidates; + + const ue_cell& ue_cc = ue_ref.get_cell(cell_index); + if (ue_cc.is_in_fallback_mode()) { + return dl_harq_candidates; + } + if (is_retx) { + // 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()) { + dl_harq_candidates.push_back(&h); + } + } + std::sort(dl_harq_candidates.begin(), + dl_harq_candidates.end(), + [](const dl_harq_process* lhs, const dl_harq_process* rhs) { return lhs->slot_ack() < rhs->slot_ack(); }); + } else if (ue_cc.is_active()) { + // If there are no pending new Tx bytes or UE in fallback, return. + if (not ue_ref.has_pending_dl_newtx_bytes()) { + return dl_harq_candidates; + } + + // Find empty HARQ. If any, add to the list. + const dl_harq_process* h = ue_cc.harqs.find_empty_dl_harq(); + if (h != nullptr) { + dl_harq_candidates.push_back(h); + } else { + // No empty HARQs are available. Log this occurrence. + if (ue_cc.harqs.find_dl_harq_waiting_ack() == nullptr) { + // A HARQ is already being retransmitted, or all HARQs are waiting for a grant for a retransmission. + logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: No available HARQs for new transmissions.", + ue_cc.ue_index, + ue_cc.rnti()); + } else { + // All HARQs are waiting for their respective HARQ-ACK. This may be a symptom of a long RTT for the PDSCH + // and HARQ-ACK. + logger.warning( + "ue={} rnti={} PDSCH allocation skipped. Cause: All the HARQs are allocated and waiting for their " + "respective HARQ-ACK. Check if any HARQ-ACK went missing in the lower layers or is arriving too late to " + "the scheduler.", + ue_cc.ue_index, + ue_cc.rnti()); + } + } + } + return dl_harq_candidates; +} + +/// \brief Fetches list of UL HARQ candidates to schedule. +static static_vector +get_ue_ul_harq_candidates(const ue& ue_ref, ue_cell_index_t cell_index, bool is_retx, srslog::basic_logger& logger) +{ + static_vector ul_harq_candidates; + + const ue_cell& ue_cc = ue_ref.get_cell(cell_index); + if (ue_cc.is_in_fallback_mode()) { + return ul_harq_candidates; + } + if (is_retx) { + // Create list of UL HARQ processes with pending retx, sorted from oldest to newest. + for (unsigned i = 0; i != ue_cc.harqs.nof_ul_harqs(); ++i) { + const ul_harq_process& h = ue_cc.harqs.ul_harq(i); + if (h.has_pending_retx()) { + ul_harq_candidates.push_back(&h); + } + } + std::sort(ul_harq_candidates.begin(), + ul_harq_candidates.end(), + [](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)) { + return ul_harq_candidates; + } + + // Find empty HARQ. If any, add to the list. + const ul_harq_process* h = ue_cc.harqs.find_empty_ul_harq(); + if (h != nullptr) { + ul_harq_candidates.push_back(h); + } else { + // No empty HARQs are available. Log this occurrence. + if (ue_cc.harqs.find_ul_harq_waiting_ack() == nullptr) { + // A HARQ is already being retransmitted, or all HARQs are waiting for a grant for a retransmission. + logger.debug("ue={} rnti={} PUSCH allocation skipped. Cause: No available HARQs for new transmissions.", + ue_cc.ue_index, + ue_cc.rnti()); + } else { + // All HARQs are waiting for their respective CRC. This may be a symptom of a slow PUSCH processing chain. + logger.warning("ue={} rnti={} PUSCH allocation skipped. Cause: All the UE HARQs are busy waiting for " + "their respective CRC result. Check if any CRC PDU went missing in the lower layers or is " + "arriving too late to the scheduler.", + ue_cc.ue_index, + ue_cc.rnti()); + } + } + } + return ul_harq_candidates; +} + /// \brief Algorithm to select next UE to allocate in a time-domain RR fashion /// \param[in] ue_db map of "slot_ue" /// \param[in] next_ue_index UE index with the highest priority to be allocated. @@ -185,8 +288,7 @@ static alloc_outcome alloc_dl_ue(const ue& u, // Prioritize PCell over SCells. for (unsigned i = 0; i != u.nof_cells(); ++i) { - const ue_cell& ue_cc = u.get_cell(to_ue_cell_index(i)); - const slot_point pdcch_slot = res_grid.get_pdcch_slot(ue_cc.cell_index); + const ue_cell& ue_cc = u.get_cell(to_ue_cell_index(i)); if (ue_cc.is_in_fallback_mode()) { // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. @@ -199,87 +301,24 @@ static alloc_outcome alloc_dl_ue(const ue& u, return alloc_outcome::skip_ue; } - // Create PDSCH param candidate search object. - ue_pdsch_param_candidate_searcher candidates{u, to_ue_cell_index(i), is_retx, pdcch_slot, logger}; - if (candidates.dl_harqs().empty()) { + // Get DL HARQ candidates. + const auto harq_candidates = get_ue_dl_harq_candidates(u, to_ue_cell_index(i), is_retx, 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; } // Iterate through allocation parameter candidates. - for (const ue_pdsch_param_candidate_searcher::candidate& param_candidate : candidates) { - const pdsch_time_domain_resource_allocation& pdsch = param_candidate.pdsch_td_res(); - const search_space_info& ss = param_candidate.ss(); - const dl_harq_process& h = param_candidate.harq(); - const dci_dl_rnti_config_type dci_type = param_candidate.dci_dl_rnti_cfg_type(); - const cell_slot_resource_grid& grid = res_grid.get_pdsch_grid(ue_cc.cell_index, pdsch.k0); - // Apply RB allocation limits. - const unsigned start_rb = std::max(ue_expert_cfg.pdsch_crb_limits.start(), ss.dl_crb_lims.start()); - const unsigned end_rb = std::min(ue_expert_cfg.pdsch_crb_limits.stop(), ss.dl_crb_lims.stop()); - if (start_rb >= end_rb) { - logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: Invalid RB allocation range [{}, {})", - ue_cc.ue_index, - ue_cc.rnti(), - start_rb, - end_rb); - continue; + 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; } - const crb_interval dl_crb_lims = {start_rb, end_rb}; - const crb_bitmap used_crbs = grid.used_crbs(ss.bwp->dl_common->generic_params.scs, dl_crb_lims, pdsch.symbols); - if (used_crbs.all()) { - logger.debug( - "ue={} rnti={} PDSCH allocation skipped. Cause: No more RBs available", ue_cc.ue_index, ue_cc.rnti()); - return alloc_outcome::skip_slot; - } - grant_prbs_mcs mcs_prbs = is_retx ? grant_prbs_mcs{h.last_alloc_params().tb.front().value().mcs, - h.last_alloc_params().rbs.type1().length()} - : ue_cc.required_dl_prbs(pdsch, u.pending_dl_newtx_bytes(), dci_type); - // Limit the grant PRBs. - if (not is_retx and dl_new_tx_max_nof_rbs_per_ue_per_slot.has_value()) { - mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, dl_new_tx_max_nof_rbs_per_ue_per_slot.value()); - // [Implementation-defined] - // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs for - // which dl_new_tx_max_nof_rbs_per_ue_per_slot was computed. One way is by checking if the emtpy interval is - // less than 2 times the required RBs. If so, allocate all remaining RBs. NOTE: This approach won't hold good in - // case of low traffic scenario. - const unsigned twice_grant_crbs_length = - rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); - if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { - mcs_prbs.n_prbs = twice_grant_crbs_length; - } - } - - if (mcs_prbs.n_prbs == 0) { - logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: UE's CQI=0 ", ue_cc.ue_index, ue_cc.rnti()); - return alloc_outcome::skip_ue; - } - - // [Implementation-defined] In case of partial slots and nof. PRBs allocated equals to 1 probability of KO is - // high due to code not being able to cope with interference. So the solution is to increase the PRB allocation - // to greater than 1 PRB. - const auto& cell_cfg = res_grid.get_cell_cfg_common(ue_cc.cell_index); - if (not cell_cfg.is_fully_dl_enabled(pdcch_slot + pdsch.k0) and mcs_prbs.n_prbs == 1) { - mcs_prbs.n_prbs = 2; - } - - const crb_interval ue_grant_crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); - bool are_crbs_valid = not ue_grant_crbs.empty(); // Cannot be empty. - if (is_retx) { - // In case of Retx, the #CRBs need to stay the same. - are_crbs_valid = ue_grant_crbs.length() == h.last_alloc_params().rbs.type1().length(); - } - if (are_crbs_valid) { - const alloc_outcome result = pdsch_alloc.allocate_dl_grant(ue_pdsch_grant{&u, - ue_cc.cell_index, - h.id, - ss.cfg->get_id(), - param_candidate.pdsch_td_res_index(), - ue_grant_crbs, - mcs_prbs.mcs}); - // If the allocation failed due to invalid parameters, we continue iteration. - if (result != alloc_outcome::invalid_params) { - return result; - } + const alloc_outcome result = pdsch_alloc.allocate_dl_grant(grant); + // If the allocation failed due to invalid parameters, we continue iteration. + if (result != alloc_outcome::invalid_params) { + return result; } } } @@ -287,15 +326,13 @@ static alloc_outcome alloc_dl_ue(const ue& u, } /// Allocate UE PUSCH grant. -static alloc_outcome alloc_ul_ue(const ue& u, - const ue_resource_grid_view& res_grid, - ue_pusch_allocator& pusch_alloc, - bool is_retx, - bool schedule_sr_only, - srslog::basic_logger& logger, - unsigned pusch_td_res_idx, - const scheduler_ue_expert_config& ue_expert_cfg, - std::optional ul_new_tx_max_nof_rbs_per_ue_per_slot = {}) +static alloc_outcome alloc_ul_ue(const ue& u, + const ue_resource_grid_view& res_grid, + ue_pusch_allocator& pusch_alloc, + bool is_retx, + bool schedule_sr_only, + srslog::basic_logger& logger, + std::optional ul_new_tx_max_nof_rbs_per_ue_per_slot = {}) { unsigned pending_newtx_bytes = 0; if (not is_retx) { @@ -316,156 +353,24 @@ static alloc_outcome alloc_ul_ue(const ue& u, return alloc_outcome::skip_ue; } - const slot_point pdcch_slot = res_grid.get_pdcch_slot(ue_cc.cell_index); - - const cell_configuration& cell_cfg_common = res_grid.get_cell_cfg_common(ue_cc.cell_index); - const ul_harq_process* h = is_retx ? ue_cc.harqs.find_pending_ul_retx() : ue_cc.harqs.find_empty_ul_harq(); - if (h == nullptr) { - // No HARQs available. - if (u.get_cell(to_ue_cell_index(i)).is_active() and not is_retx) { - if (res_grid.has_ue_ul_pdcch(ue_cc.cell_index, u.crnti) or ue_cc.harqs.find_ul_harq_waiting_ack() == nullptr) { - // A HARQ is already being retransmitted, or all HARQs are waiting for a grant for a retransmission. - logger.debug("ue={} rnti={} PUSCH allocation skipped. Cause: No available HARQs for new transmissions.", - ue_cc.ue_index, - ue_cc.rnti()); - } else { - // All HARQs are waiting for their respective CRC. This may be a symptom of a slow PUSCH processing chain. - logger.warning("ue={} rnti={} PUSCH allocation skipped. Cause: All the UE HARQs are busy waiting for " - "their respective CRC result. Check if any CRC PDU went missing in the lower layers or is " - "arriving too late to the scheduler.", - ue_cc.ue_index, - ue_cc.rnti()); - } - } + // Get UL HARQ candidates. + const auto harq_candidates = get_ue_ul_harq_candidates(u, to_ue_cell_index(i), is_retx, logger); + if (harq_candidates.empty()) { + // The conditions for a new PUSCH allocation for this UE were not met (e.g. lack of available HARQs). continue; } - std::optional preferred_dci_rnti_type; - if (is_retx) { - preferred_dci_rnti_type = h->last_tx_params().dci_cfg_type; - } - static_vector search_spaces = - ue_cc.get_active_ul_search_spaces(pdcch_slot, preferred_dci_rnti_type); - for (const search_space_info* ss : search_spaces) { - if (ss->cfg->is_search_space0()) { - continue; - } - - const pusch_time_domain_resource_allocation& pusch_td = ss->pusch_time_domain_list[pusch_td_res_idx]; - - if (res_grid.has_ue_ul_grant(ue_cc.cell_index, ue_cc.rnti(), pusch_td.k2 + cell_cfg_common.ntn_cs_koffset)) { - // Only one PUSCH per UE per slot. - return alloc_outcome::skip_ue; - } - - const slot_point pusch_slot = pdcch_slot + pusch_td.k2 + cell_cfg_common.ntn_cs_koffset; - if (not cell_cfg_common.is_ul_enabled(pusch_slot)) { - // Try next PUSCH time domain resource value. - return alloc_outcome::invalid_params; - } - const unsigned start_ul_symbols = - NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg_common.get_nof_ul_symbol_per_slot(pusch_slot); - // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for - // the first transmission. - const bool sym_length_match_prev_grant_for_retx = - is_retx ? pusch_td.symbols.length() == h->last_tx_params().nof_symbols : true; - if (pusch_td.symbols.start() < start_ul_symbols or - pusch_td.symbols.stop() > (start_ul_symbols + cell_cfg_common.get_nof_ul_symbol_per_slot(pusch_slot)) or - !sym_length_match_prev_grant_for_retx) { - // Try next PUSCH time domain resource value. - return alloc_outcome::invalid_params; - } - - const cell_slot_resource_grid& grid = - res_grid.get_pusch_grid(ue_cc.cell_index, pusch_td.k2 + cell_cfg_common.ntn_cs_koffset); - // Apply RB allocation limits. - const unsigned start_rb = std::max(ue_expert_cfg.pusch_crb_limits.start(), ss->ul_crb_lims.start()); - const unsigned end_rb = std::min(ue_expert_cfg.pusch_crb_limits.stop(), ss->ul_crb_lims.stop()); - if (start_rb >= end_rb) { - logger.debug("ue={} rnti={} PUSCH allocation skipped. Cause: Invalid RB allocation range [{}, {})", - ue_cc.ue_index, - ue_cc.rnti(), - start_rb, - end_rb); - continue; - } - const crb_interval ul_crb_lims = {start_rb, end_rb}; - const prb_bitmap used_crbs = - grid.used_crbs(ss->bwp->ul_common->generic_params.scs, ul_crb_lims, pusch_td.symbols); - if (used_crbs.all()) { - logger.debug( - "ue={} rnti={} PUSCH allocation skipped. Cause: No more RBs available", ue_cc.ue_index, ue_cc.rnti()); - return alloc_outcome::skip_slot; - } - - // Compute the MCS and the number of PRBs, depending on the pending bytes to transmit. - grant_prbs_mcs mcs_prbs = is_retx - ? grant_prbs_mcs{h->last_tx_params().mcs, h->last_tx_params().rbs.type1().length()} - : ue_cc.required_ul_prbs(pusch_td, - pending_newtx_bytes, - ss->get_ul_dci_format() == srsran::dci_ul_format::f0_0 - ? srsran::dci_ul_rnti_config_type::c_rnti_f0_0 - : srsran::dci_ul_rnti_config_type::c_rnti_f0_1); - // Limit the grant PRBs. - if (not is_retx and not schedule_sr_only and ul_new_tx_max_nof_rbs_per_ue_per_slot.has_value()) { - mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, ul_new_tx_max_nof_rbs_per_ue_per_slot.value()); - // [Implementation-defined] - // Check whether it's the last UE to be scheduled in this slot i.e. if the emtpy interval is less than 2 times - // the required RBs. If so, allocate all remaining RBs. - const unsigned twice_grant_crbs_length = - rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); - if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { - mcs_prbs.n_prbs = twice_grant_crbs_length; - } - } - // NOTE: this should never happen, but it's safe not to proceed if we get n_prbs == 0. - if (mcs_prbs.n_prbs == 0) { - logger.debug("ue={} rnti={} PUSCH allocation skipped. Cause: MCS and PRBs computation resulted in no PRBs " - "allocated to this UE", - ue_cc.ue_index, - ue_cc.rnti()); - return alloc_outcome::skip_ue; - } - - // Due to the pre-allocated UCI bits, MCS 0 and PRB 1 would not leave any space for the payload on the TBS, as - // all the space would be taken by the UCI bits. As a result of this, the effective code rate would be 0 and the - // allocation would fail and be postponed to the next slot. - // [Implementation-defined] In our tests, we have seen that MCS 5 with 1 PRB can lead (depending on the - // configuration) to a non-valid MCS-PRB allocation; therefore, we set 6 as minimum value for 1 PRB. - // TODO: Remove this part and handle the problem with a loop that is general for any configuration. - const sch_mcs_index min_mcs_for_1_prb = static_cast(6U); - const unsigned min_allocable_prbs = 1U; - if (mcs_prbs.mcs < min_mcs_for_1_prb and mcs_prbs.n_prbs <= min_allocable_prbs) { - ++mcs_prbs.n_prbs; - } - - const crb_interval ue_grant_crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); - // There must be at least one available CRB. - bool are_crbs_valid = not ue_grant_crbs.empty(); - // For low MCS, we need to allocate more than min_allocable_prbs PRBs; else, the overhead due to UCI-on-PUSCH will - // make the effective code-rate exceed 0.95. - // TODO: Remove this part and handle the problem with a loop that is general for any configuration. - if (ue_grant_crbs.length() <= min_allocable_prbs and mcs_prbs.mcs < min_mcs_for_1_prb) { - logger.debug("ue={} rnti={} PUSCH allocation skipped. Cause: the scheduler couldn't allocate the min. " - "number of PRBs={} for MCS={}", - ue_cc.ue_index, - ue_cc.rnti(), - mcs_prbs.n_prbs, - mcs_prbs.mcs.to_uint()); - return alloc_outcome::skip_ue; - } - - if (is_retx) { - // In case of Retx, the #CRBs need to stay the same. - are_crbs_valid = ue_grant_crbs.length() == h->last_tx_params().rbs.type1().length(); + // Iterate through allocation parameter candidates. + 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.max_nof_rbs = ul_new_tx_max_nof_rbs_per_ue_per_slot; } - if (are_crbs_valid) { - const alloc_outcome result = pusch_alloc.allocate_ul_grant(ue_pusch_grant{ - &u, ue_cc.cell_index, h->id, ue_grant_crbs, pusch_td_res_idx, ss->cfg->get_id(), mcs_prbs.mcs}); - // If the allocation failed due to invalid parameters, we continue the iteration. - if (result != alloc_outcome::invalid_params) { - return result; - } + const alloc_outcome result = pusch_alloc.allocate_ul_grant(grant); + // If the allocation failed due to invalid parameters, we continue iteration. + if (result != alloc_outcome::invalid_params) { + return result; } } } @@ -520,48 +425,25 @@ void scheduler_time_rr::ul_sched(ue_pusch_allocator& pusch_alloc, return; } - // Fetch applicable PUSCH Time Domain resource index list. - // NOTE: All UEs use the same PUSCH Time Domain Resource configuration. - const ue& ref_u = **ues.begin(); - const ue_cell_configuration& ue_cfg = ref_u.get_pcell().cfg(); - - static_vector pusch_td_res_index_list = - get_pusch_td_resource_indices(ue_cfg, res_grid.get_pdcch_slot(ref_u.get_pcell().cell_index)); - // First schedule UL data re-transmissions. - for (unsigned pusch_td_res_idx : pusch_td_res_index_list) { - auto data_retx_ue_function = [this, &res_grid, &pusch_alloc, pusch_td_res_idx](const ue& u) { - return alloc_ul_ue(u, res_grid, pusch_alloc, true, false, logger, pusch_td_res_idx, expert_cfg); - }; - next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, data_retx_ue_function); - } + 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. - for (unsigned pusch_td_res_idx : pusch_td_res_index_list) { - auto sr_ue_function = [this, &res_grid, &pusch_alloc, pusch_td_res_idx](const ue& u) { - return alloc_ul_ue(u, res_grid, pusch_alloc, false, true, logger, pusch_td_res_idx, expert_cfg); - }; - next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, sr_ue_function); - } + auto sr_ue_function = [this, &res_grid, &pusch_alloc](const ue& u) { + return alloc_ul_ue(u, res_grid, pusch_alloc, false, true, 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) { - for (unsigned pusch_td_res_idx : pusch_td_res_index_list) { - auto data_tx_ue_function = - [this, &res_grid, &pusch_alloc, ul_new_tx_max_nof_rbs_per_ue_per_slot, pusch_td_res_idx](const ue& u) { - return alloc_ul_ue(u, - res_grid, - pusch_alloc, - false, - false, - logger, - pusch_td_res_idx, - expert_cfg, - 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); - } + 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); + }; + next_ul_ue_index = round_robin_apply(ues, next_ul_ue_index, data_tx_ue_function); } } diff --git a/lib/scheduler/policy/ue_allocator.h b/lib/scheduler/policy/ue_allocator.h index 91ba367af1..16771f5216 100644 --- a/lib/scheduler/policy/ue_allocator.h +++ b/lib/scheduler/policy/ue_allocator.h @@ -36,10 +36,10 @@ struct ue_pdsch_grant { const ue* user; du_cell_index_t cell_index; harq_id_t h_id; - search_space_id ss_id; - unsigned time_res_index; - crb_interval crbs; - sch_mcs_index mcs; + /// Recommended nof. bytes to schedule. This field is not present/ignored in case of HARQ retransmission. + std::optional recommended_nof_bytes; + /// Maximum nof. RBs to allocate to the UE. This field is not present/ignored in case of HARQ retransmission. + std::optional max_nof_rbs; }; /// Information relative to a UE PUSCH grant. @@ -47,10 +47,10 @@ struct ue_pusch_grant { const ue* user; du_cell_index_t cell_index; harq_id_t h_id; - crb_interval crbs; - unsigned time_res_index; - search_space_id ss_id = to_search_space_id(1); - sch_mcs_index mcs; + /// Recommended nof. bytes to schedule. This field is not present/ignored in case of HARQ retransmission. + std::optional recommended_nof_bytes; + /// Maximum nof. RBs to allocate to the UE. This field is not present/ignored in case of HARQ retransmission. + std::optional max_nof_rbs; }; /// \brief Outcome of a UE grant allocation, and action for the scheduler policy to follow afterwards. diff --git a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp index 8d4e7266f0..3086088909 100644 --- a/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp +++ b/lib/scheduler/pucch_scheduling/pucch_allocator_impl.cpp @@ -387,11 +387,10 @@ void pucch_allocator_impl::pucch_allocate_csi_opportunity(cell_slot_resource_all "The CSI is the first dedicated PUCCH grant that is expected to be allocated."); if (existing_grants.format1_sr_grant != nullptr) { - return convert_to_format2_csi( - pucch_slot_alloc, *existing_grants.format1_sr_grant, crnti, ue_cell_cfg, csi_part1_nof_bits); + convert_to_format2_csi(pucch_slot_alloc, *existing_grants.format1_sr_grant, crnti, ue_cell_cfg, csi_part1_nof_bits); } - return allocate_new_csi_grant(pucch_slot_alloc, crnti, ue_cell_cfg, csi_part1_nof_bits); + allocate_new_csi_grant(pucch_slot_alloc, crnti, ue_cell_cfg, csi_part1_nof_bits); } pucch_uci_bits pucch_allocator_impl::remove_ue_uci_from_pucch(cell_slot_resource_allocator& slot_alloc, @@ -427,12 +426,10 @@ pucch_uci_bits pucch_allocator_impl::remove_ue_uci_from_pucch(cell_slot_resource return removed_uci_info; } // Proceed with Format 1. - else { - // Only remove HARQ-ACK grant, handle SR grant separately. - removed_uci_info.harq_ack_nof_bits = it->format_1.harq_ack_nof_bits; - pucchs.erase(it); - resource_manager.release_harq_f1_resource(slot_alloc.slot, crnti, pucch_cfg); - } + // Only remove HARQ-ACK grant, handle SR grant separately. + removed_uci_info.harq_ack_nof_bits = it->format_1.harq_ack_nof_bits; + pucchs.erase(it); + resource_manager.release_harq_f1_resource(slot_alloc.slot, crnti, pucch_cfg); } // Remove SR grant, if any. @@ -831,8 +828,8 @@ void pucch_allocator_impl::convert_to_format2_csi(cell_slot_resource_allocator& const unsigned candidate_uci_bits = sr_nof_bits_to_uint(sr_bits_to_report) + csi_part1_nof_bits; const unsigned max_payload = - get_pucch_format2_max_payload(variant_get(pucch_res->format_params).nof_prbs, - variant_get(pucch_res->format_params).nof_symbols, + get_pucch_format2_max_payload(std::get(pucch_res->format_params).nof_prbs, + std::get(pucch_res->format_params).nof_symbols, max_pucch_code_rate); // It's the config validator that should ensure this is verified. @@ -869,7 +866,7 @@ void pucch_allocator_impl::convert_to_format2_csi(cell_slot_resource_allocator& rnti, *pucch_res, ue_cell_cfg, - variant_get(pucch_res->format_params).nof_prbs, + std::get(pucch_res->format_params).nof_prbs, harq_bits_only_csi, sr_bits_to_report, csi_part1_nof_bits); @@ -907,8 +904,8 @@ std::optional pucch_allocator_impl::convert_to_format2_harq(cell_slot_ .format_2_common_param.value() .max_c_rate); const unsigned max_payload = - get_pucch_format2_max_payload(variant_get(format2_res.pucch_res->format_params).nof_prbs, - variant_get(format2_res.pucch_res->format_params).nof_symbols, + get_pucch_format2_max_payload(std::get(format2_res.pucch_res->format_params).nof_prbs, + std::get(format2_res.pucch_res->format_params).nof_symbols, max_pucch_code_rate); if (max_payload < candidate_uci_bits) { @@ -926,8 +923,8 @@ std::optional pucch_allocator_impl::convert_to_format2_harq(cell_slot_ // Compute the number of PRBs required for the uci bits computed above. const unsigned nof_prbs = get_pucch_format2_nof_prbs(candidate_uci_bits, - variant_get(format2_res.pucch_res->format_params).nof_prbs, - variant_get(format2_res.pucch_res->format_params).nof_symbols, + std::get(format2_res.pucch_res->format_params).nof_prbs, + std::get(format2_res.pucch_res->format_params).nof_symbols, max_pucch_code_rate); // Remove the previously allocated PUCCH format-1 resources. @@ -994,8 +991,8 @@ pucch_allocator_impl::change_format2_resource(cell_slot_resource_allocator& .format_2_common_param.value() .max_c_rate); const unsigned max_payload = - get_pucch_format2_max_payload(variant_get(format2_res.pucch_res->format_params).nof_prbs, - variant_get(format2_res.pucch_res->format_params).nof_symbols, + get_pucch_format2_max_payload(std::get(format2_res.pucch_res->format_params).nof_prbs, + std::get(format2_res.pucch_res->format_params).nof_symbols, max_pucch_code_rate); if (max_payload < candidate_uci_bits) { @@ -1014,8 +1011,8 @@ pucch_allocator_impl::change_format2_resource(cell_slot_resource_allocator& // Compute the number of PRBs required for the uci bits computed above. const unsigned nof_prbs = get_pucch_format2_nof_prbs(candidate_uci_bits, - variant_get(format2_res.pucch_res->format_params).nof_prbs, - variant_get(format2_res.pucch_res->format_params).nof_symbols, + std::get(format2_res.pucch_res->format_params).nof_prbs, + std::get(format2_res.pucch_res->format_params).nof_symbols, max_pucch_code_rate); // Remove the previously allocated PUCCH format-2 resource. @@ -1150,8 +1147,8 @@ void pucch_allocator_impl::allocate_new_csi_grant(cell_slot_resource_allocator& .format_2_common_param.value() .max_c_rate); const unsigned max_payload = - get_pucch_format2_max_payload(variant_get(csi_f2_res->format_params).nof_prbs, - variant_get(csi_f2_res->format_params).nof_symbols, + get_pucch_format2_max_payload(std::get(csi_f2_res->format_params).nof_prbs, + std::get(csi_f2_res->format_params).nof_symbols, max_pucch_code_rate); // When this function is called, it means that there are no SR grants to be multiplexed with CSI; thus, the CSI bits @@ -1172,7 +1169,7 @@ void pucch_allocator_impl::allocate_new_csi_grant(cell_slot_resource_allocator& crnti, *csi_f2_res, ue_cell_cfg, - variant_get(csi_f2_res->format_params).nof_prbs, + std::get(csi_f2_res->format_params).nof_prbs, harq_ack_bits_only_csi, sr_bits_only_csi, csi_part1_bits); @@ -1216,7 +1213,7 @@ std::optional pucch_allocator_impl::add_harq_bits_to_harq_f2_grant(puc .format_2_common_param.value() .max_c_rate); - const auto& f2_params = variant_get(pucch_f2_harq_cfg.pucch_res->format_params); + const auto& f2_params = std::get(pucch_f2_harq_cfg.pucch_res->format_params); const unsigned max_nof_prbs = f2_params.nof_prbs; const unsigned nof_symbols = f2_params.nof_symbols; const unsigned max_payload = get_pucch_format2_max_payload(max_nof_prbs, nof_symbols, max_pucch_code_rate); @@ -1261,7 +1258,7 @@ void pucch_allocator_impl::fill_pucch_ded_format1_grant(pucch_info& pu // Set PRBs and symbols, first. // The number of PRBs is not explicitly stated in the TS, but it can be inferred it's 1. - const auto& res_f1 = variant_get(pucch_ded_res_cfg.format_params); + const auto& res_f1 = std::get(pucch_ded_res_cfg.format_params); pucch_grant.resources.prbs.set(pucch_ded_res_cfg.starting_prb, pucch_ded_res_cfg.starting_prb + PUCCH_FORMAT_1_NOF_PRBS); pucch_grant.resources.symbols.set(res_f1.starting_sym_idx, res_f1.starting_sym_idx + res_f1.nof_symbols); @@ -1299,7 +1296,7 @@ void pucch_allocator_impl::fill_pucch_format2_grant(pucch_info& // Set PRBs and symbols, first.º // The number of PRBs is not explicitly stated in the TS, but it can be inferred it's 1. pucch_grant.resources.prbs.set(pucch_ded_res_cfg.starting_prb, pucch_ded_res_cfg.starting_prb + nof_prbs); - const auto& res_f2 = variant_get(pucch_ded_res_cfg.format_params); + const auto& res_f2 = std::get(pucch_ded_res_cfg.format_params); pucch_grant.resources.symbols.set(res_f2.starting_sym_idx, res_f2.starting_sym_idx + res_f2.nof_symbols); if (pucch_ded_res_cfg.second_hop_prb.has_value()) { pucch_grant.resources.second_hop_prbs.set(pucch_ded_res_cfg.second_hop_prb.value(), diff --git a/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp b/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp index 34493e9a40..66891399e3 100644 --- a/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp +++ b/lib/scheduler/pucch_scheduling/pucch_resource_manager.cpp @@ -38,7 +38,7 @@ static int get_pucch_res_idx_for_csi(const ue_cell_configuration& ue_cell_cfg) const bwp_id_t bwp_id = srsran::MIN_BWP_ID; const auto& csi_report_cfg = ue_cell_cfg.cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; auto& csi_pucch_res_list = - variant_get(csi_report_cfg.report_cfg_type) + std::get(csi_report_cfg.report_cfg_type) .pucch_csi_res_list; const auto& it = std::find_if(csi_pucch_res_list.begin(), diff --git a/lib/scheduler/scheduler_impl.cpp b/lib/scheduler/scheduler_impl.cpp index 3cad6a319d..b0581634d2 100644 --- a/lib/scheduler/scheduler_impl.cpp +++ b/lib/scheduler/scheduler_impl.cpp @@ -43,20 +43,17 @@ bool scheduler_impl::handle_cell_configuration_request(const sched_cell_configur return false; } - // Update logger with new cell index. - sched_ev_logger.enqueue(scheduler_event_logger::cell_creation_event{msg.cell_index, cell_cfg->pci}); - // Check if it is a new DU Cell Group. if (not groups.contains(msg.cell_group_index)) { // If it is a new group, create a new instance. groups.emplace(msg.cell_group_index, - std::make_unique(expert_params.ue, config_notifier, metrics, sched_ev_logger)); + std::make_unique(expert_params.ue, config_notifier, metrics)); } // Create a new cell scheduler instance. - cells.emplace(msg.cell_index, - std::make_unique( - expert_params, msg, *cell_cfg, *groups[msg.cell_group_index], sched_ev_logger, metrics)); + cells.emplace( + msg.cell_index, + std::make_unique(expert_params, msg, *cell_cfg, *groups[msg.cell_group_index], metrics)); return true; } diff --git a/lib/scheduler/scheduler_impl.h b/lib/scheduler/scheduler_impl.h index f7ddd825e8..dd57cbf3ce 100644 --- a/lib/scheduler/scheduler_impl.h +++ b/lib/scheduler/scheduler_impl.h @@ -70,8 +70,7 @@ class scheduler_impl final : public mac_scheduler const scheduler_expert_config expert_params; sched_configuration_notifier& config_notifier; - srslog::basic_logger& logger; - scheduler_event_logger sched_ev_logger; + srslog::basic_logger& logger; // Slot metrics sink. scheduler_metrics_handler metrics; diff --git a/lib/scheduler/support/csi_report_helpers.cpp b/lib/scheduler/support/csi_report_helpers.cpp index ca37be3428..2b72fefb31 100644 --- a/lib/scheduler/support/csi_report_helpers.cpp +++ b/lib/scheduler/support/csi_report_helpers.cpp @@ -28,14 +28,12 @@ bool srsran::csi_helper::is_csi_reporting_slot(const serving_cell_config& ue_cfg // We assume we only use the first CSI report configuration. const unsigned csi_report_cfg_idx = 0; const auto& csi_report_cfg = ue_cfg.csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; + const auto& csi_report_cfg_type = + std::get(csi_report_cfg.report_cfg_type); // > Scheduler CSI grants. - unsigned csi_offset = - variant_get(csi_report_cfg.report_cfg_type) - .report_slot_offset; - unsigned csi_period = csi_report_periodicity_to_uint( - variant_get(csi_report_cfg.report_cfg_type) - .report_slot_period); + unsigned csi_offset = csi_report_cfg_type.report_slot_offset; + unsigned csi_period = csi_report_periodicity_to_uint(csi_report_cfg_type.report_slot_period); if ((sl_tx - csi_offset).to_uint() % csi_period == 0) { return true; diff --git a/lib/scheduler/support/csi_rs_helper.cpp b/lib/scheduler/support/csi_rs_helper.cpp index 4e3a3ce318..a0e706eacc 100644 --- a/lib/scheduler/support/csi_rs_helper.cpp +++ b/lib/scheduler/support/csi_rs_helper.cpp @@ -32,7 +32,6 @@ bool srsran::csi_helper::is_csi_rs_slot(const cell_configuration& cell_cfg, slot return false; } - // for (unsigned i = 0; i != cell_cfg.zp_csi_rs_list.size(); ++i) for (const auto& zp_csi : cell_cfg.zp_csi_rs_list) { if (zp_csi.offset.has_value() and zp_csi.period.has_value() and (sl_tx - *zp_csi.offset).to_uint() % (unsigned)*zp_csi.period == 0) { diff --git a/lib/scheduler/support/mcs_tbs_calculator.cpp b/lib/scheduler/support/mcs_tbs_calculator.cpp index 4cdb050bc4..a7ea836f12 100644 --- a/lib/scheduler/support/mcs_tbs_calculator.cpp +++ b/lib/scheduler/support/mcs_tbs_calculator.cpp @@ -23,7 +23,6 @@ #include "mcs_tbs_calculator.h" #include "dmrs_helpers.h" #include "sch_pdu_builder.h" -#include "srsran/adt/variant.h" #include "srsran/ran/pdsch/dlsch_info.h" #include "srsran/ran/pusch/pusch_mcs.h" #include "srsran/ran/pusch/ulsch_info.h" @@ -32,7 +31,7 @@ using namespace srsran; -constexpr unsigned NOF_BITS_PER_BYTE = 8U; +static constexpr unsigned NOF_BITS_PER_BYTE = 8U; // Helper that generates the ulsch_configuration object necessary to compute the Effective Code Rate. static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_cfg, @@ -79,31 +78,31 @@ static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_c // Use \c betaOffsetACK-Index1 for up to 2 bits HARQ-ACK reporting. else if (pusch_cfg.nof_harq_ack_bits < 3) { ulsch_info.beta_offset_harq_ack = - beta_harq_ack_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_harq_ack_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_ack_idx_1.value()); } // Use \c betaOffsetACK-Index1 for up to 11 bits HARQ-ACK reporting. else if (pusch_cfg.nof_harq_ack_bits < 12) { ulsch_info.beta_offset_harq_ack = - beta_harq_ack_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_harq_ack_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_ack_idx_2.value()); } // Use \c betaOffsetACK-Index1 for more than 11 bits HARQ-ACK reporting. else { ulsch_info.beta_offset_harq_ack = - beta_harq_ack_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_harq_ack_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_ack_idx_3.value()); } @@ -116,21 +115,21 @@ static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_c // Use \c betaOffsetCSI-Part1-Index1 for up to 11 bits CSI Part 1 reporting. else if (pusch_cfg.nof_csi_part1_bits < 12) { ulsch_info.beta_offset_csi_part1 = - beta_csi_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_csi_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_csi_p1_idx_1.value()); } // Use \c betaOffsetCSI-Part1-Index2 for more than 11 bits CSI Part 1 reporting. else { ulsch_info.beta_offset_csi_part1 = - beta_csi_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_csi_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_csi_p1_idx_2.value()); } @@ -143,21 +142,21 @@ static ulsch_configuration build_ulsch_info(const pusch_config_params& pusch_c // Use \c betaOffsetCSI-Part2-Index1 for up to 11 bits CSI Part 2 reporting. else if (pusch_cfg.max_nof_csi_part2_bits < 12) { ulsch_info.beta_offset_csi_part2 = - beta_csi_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_csi_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_csi_p2_idx_1.value()); } // Use \c betaOffsetCSI-Part2-Index2 for more than 11 bits CSI Part 2 reporting. else { ulsch_info.beta_offset_csi_part2 = - beta_csi_to_float(variant_get(ue_cell_cfg->cfg_dedicated() - .ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()) + beta_csi_to_float(std::get(ue_cell_cfg->cfg_dedicated() + .ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()) .beta_offset_csi_p2_idx_2.value()); } diff --git a/lib/scheduler/support/pdcch/search_space_helper.h b/lib/scheduler/support/pdcch/search_space_helper.h index 2a7dff4b41..a9de4e6724 100644 --- a/lib/scheduler/support/pdcch/search_space_helper.h +++ b/lib/scheduler/support/pdcch/search_space_helper.h @@ -33,7 +33,7 @@ inline bool search_space_supports_dci_dl_format(const search_space_configuration { if (ss_cfg.is_common_search_space()) { const auto& cmn_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); + std::get(ss_cfg.get_monitored_dci_formats()); switch (dci_fmt) { case dci_dl_format::f1_0: return cmn_dci_fmt.f0_0_and_f1_0; @@ -44,17 +44,17 @@ inline bool search_space_supports_dci_dl_format(const search_space_configuration default: report_fatal_error("DCI format {} not supported for common SearchSpace", dci_fmt); } - } else { - const auto& ue_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); - switch (dci_fmt) { - case dci_dl_format::f1_0: - return ue_dci_fmt == search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0; - case dci_dl_format::f1_1: - return ue_dci_fmt == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1; - default: - report_fatal_error("DCI format {} not supported for UE-dedicated SearchSpace", dci_fmt); - } + } + + const auto& ue_dci_fmt = + std::get(ss_cfg.get_monitored_dci_formats()); + switch (dci_fmt) { + case dci_dl_format::f1_0: + return ue_dci_fmt == search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0; + case dci_dl_format::f1_1: + return ue_dci_fmt == search_space_configuration::ue_specific_dci_format::f0_1_and_1_1; + default: + report_fatal_error("DCI format {} not supported for UE-dedicated SearchSpace", dci_fmt); } return false; } @@ -65,16 +65,17 @@ inline bool search_space_supports_dci_dl_format(const search_space_configuration inline dci_dl_format get_dl_dci_format(const search_space_configuration& ss_cfg) { if (ss_cfg.is_common_search_space()) { - const auto common_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); + const auto& common_dci_fmt = + std::get(ss_cfg.get_monitored_dci_formats()); if (common_dci_fmt.f0_0_and_f1_0) { return dci_dl_format::f1_0; } // TODO: Handle DCI Formats 2_0, 2_1, 2_2, 2_3 under Common SearchSpace. report_fatal_error("Unsupported DL DCI format"); } - const auto uss_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); + + const auto& uss_dci_fmt = + std::get(ss_cfg.get_monitored_dci_formats()); switch (uss_dci_fmt) { case search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0: return dci_dl_format::f1_0; @@ -90,16 +91,17 @@ inline dci_dl_format get_dl_dci_format(const search_space_configuration& ss_cfg) inline dci_ul_format get_ul_dci_format(const search_space_configuration& ss_cfg) { if (ss_cfg.is_common_search_space()) { - const auto common_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); + const auto& common_dci_fmt = + std::get(ss_cfg.get_monitored_dci_formats()); if (common_dci_fmt.f0_0_and_f1_0) { return dci_ul_format::f0_0; } // TODO: Handle DCI Formats 2_0, 2_1, 2_2, 2_3 under Common SearchSpace. report_fatal_error("Unsupported UL DCI format"); } - const auto uss_dci_fmt = - variant_get(ss_cfg.get_monitored_dci_formats()); + + const auto& uss_dci_fmt = + std::get(ss_cfg.get_monitored_dci_formats()); switch (uss_dci_fmt) { case search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0: return dci_ul_format::f0_0; @@ -129,4 +131,4 @@ inline bool is_pdcch_monitoring_active(slot_point sl, const search_space_configu } } // namespace pdcch_helper -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/lib/scheduler/support/pdsch/pdsch_resource_allocation.h b/lib/scheduler/support/pdsch/pdsch_resource_allocation.h index d5d31e5ebb..5f02ff2a0a 100644 --- a/lib/scheduler/support/pdsch/pdsch_resource_allocation.h +++ b/lib/scheduler/support/pdsch/pdsch_resource_allocation.h @@ -75,7 +75,7 @@ inline crb_interval get_ra_crb_limits_common(const bwp_downlink_common& init_dl_ : init_dl_bwp.pdcch_common.common_coreset.value(); srsran_assert( ss_cfg.is_common_search_space() and - variant_get(ss_cfg.get_monitored_dci_formats()).f0_0_and_f1_0, + std::get(ss_cfg.get_monitored_dci_formats()).f0_0_and_f1_0, "Invalid SearchSpace type"); return get_ra_crb_limits(dci_dl_format::f1_0, init_dl_bwp, init_dl_bwp, ss_cfg, cs_cfg); diff --git a/lib/scheduler/support/pusch/pusch_td_resource_indices.cpp b/lib/scheduler/support/pusch/pusch_td_resource_indices.cpp index 27567255a4..18ff4491e6 100644 --- a/lib/scheduler/support/pusch/pusch_td_resource_indices.cpp +++ b/lib/scheduler/support/pusch/pusch_td_resource_indices.cpp @@ -69,25 +69,16 @@ compute_pusch_td_resource_indices(span -srsran::get_pusch_td_resource_indices(const ue_cell_configuration& ue_cfg, slot_point pdcch_slot) +srsran::get_pusch_td_resource_indices(const cell_configuration& cell_cfg, + slot_point pdcch_slot, + const search_space_info* ss_info) { - const cell_configuration& cell_cfg = ue_cfg.cell_cfg_common; - const search_space_info* ss_info = - ue_cfg.find_search_space(ue_cfg.cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces.back().get_id()); - if (ss_info == nullptr) { - return {}; + unsigned min_k1 = cell_cfg.expert_cfg.ue.min_k1; + span pusch_time_domain_list = + cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().pusch_td_alloc_list; + if (ss_info != nullptr) { + min_k1 = *std::min(ss_info->get_k1_candidates().begin(), ss_info->get_k1_candidates().end()); + pusch_time_domain_list = ss_info->pusch_time_domain_list; } - const unsigned min_k1 = *std::min(ss_info->get_k1_candidates().begin(), ss_info->get_k1_candidates().end()); - return compute_pusch_td_resource_indices( - cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().pusch_td_alloc_list, cell_cfg, pdcch_slot, min_k1); -} - -static_vector -srsran::get_pusch_td_res_indices_common(const cell_configuration& cell_cfg, slot_point pdcch_slot) -{ - return compute_pusch_td_resource_indices( - cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common.value().pusch_td_alloc_list, - cell_cfg, - pdcch_slot, - cell_cfg.expert_cfg.ue.min_k1); + return compute_pusch_td_resource_indices(pusch_time_domain_list, cell_cfg, pdcch_slot, min_k1); } diff --git a/lib/scheduler/support/pusch/pusch_td_resource_indices.h b/lib/scheduler/support/pusch/pusch_td_resource_indices.h index fd44469c83..bcbf3300cf 100644 --- a/lib/scheduler/support/pusch/pusch_td_resource_indices.h +++ b/lib/scheduler/support/pusch/pusch_td_resource_indices.h @@ -22,6 +22,7 @@ #pragma once +#include "../../config/ue_configuration.h" #include "srsran/adt/static_vector.h" #include "srsran/ran/pusch/pusch_constants.h" #include "srsran/ran/slot_point.h" @@ -32,20 +33,16 @@ class ue_repository; class cell_configuration; class ue_cell_configuration; -/// \brief Fetches a list of PUSCH Time Domain resource indexes based on cell, UE configuration and nof. symbols in -/// PUSCH slot. -/// \param[in] ue_cfg User's dedicated configuration. -/// \param[in] pdcch_slot Slot at which the PDCCH is supposed to be scheduled. -/// \return List of PUSCH Time Domain resource indexes. -static_vector -get_pusch_td_resource_indices(const ue_cell_configuration& ue_cfg, slot_point pdcch_slot); - /// \brief Fetches a list of PUSCH Time Domain resource indexes based on cell, UE configuration and nof. symbols in /// PUSCH slot. /// \param[in] cell_cfg Cell configuration. /// \param[in] pdcch_slot Slot at which the PDCCH is supposed to be scheduled. +/// \param[in] ss_info SearchSpace information. /// \return List of PUSCH Time Domain resource indexes. +/// \remark If \c ss_info is nullptr, then minimum k1 is taken from \c cell_cfg. static_vector -get_pusch_td_res_indices_common(const cell_configuration& cell_cfg, slot_point pdcch_slot); +get_pusch_td_resource_indices(const cell_configuration& cell_cfg, + slot_point pdcch_slot, + const search_space_info* ss_info = nullptr); } // namespace srsran diff --git a/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp b/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp index 7e003fb17b..7eee443dee 100644 --- a/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp +++ b/lib/scheduler/uci_scheduling/uci_allocator_impl.cpp @@ -44,7 +44,7 @@ uci_allocator_impl::~uci_allocator_impl() = default; static void update_uci_on_pusch_harq_offsets(uci_info::harq_info& uci_harq, const uci_on_pusch& uci_cfg) { // We assume the configuration contains the values for beta_offsets. - const auto& beta_offsets = variant_get(uci_cfg.beta_offsets_cfg.value()); + const auto& beta_offsets = std::get(uci_cfg.beta_offsets_cfg.value()); // The values of \c beta_offsets are set according to Section 9.3, TS38.213. const uint16_t harq_ack_nof_bits = uci_harq.harq_ack_nof_bits; @@ -71,7 +71,7 @@ static void add_csi_to_uci_on_pusch(uci_info::csi_info& uci_csi, const ue_cell_c const auto& uci_cfg = ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.value(); // We assume the configuration contains the values for beta_offsets. - const auto& beta_offsets = variant_get(uci_cfg.beta_offsets_cfg.value()); + const auto& beta_offsets = std::get(uci_cfg.beta_offsets_cfg.value()); // The values of \c beta_offsets are set according to Section 9.3, TS 38.213. if (uci_csi.csi_part1_nof_bits <= 11) { @@ -92,7 +92,7 @@ static void add_csi_to_uci_on_pusch(uci_info::csi_info& uci_csi, const ue_cell_c const auto& uci_cfg = ue_cell_cfg.cfg_dedicated().ul_config.value().init_ul_bwp.pusch_cfg.value().uci_cfg.value(); // We assume the configuration contains the values for beta_offsets. - const auto& beta_offsets = variant_get(uci_cfg.beta_offsets_cfg.value()); + const auto& beta_offsets = std::get(uci_cfg.beta_offsets_cfg.value()); // The values of \c beta_offsets are set according to Section 9.3, TS 38.213. if (uci_csi.csi_part1_nof_bits <= 11) { @@ -209,8 +209,9 @@ std::optional uci_allocator_impl::alloc_uci_harq_ue(cell_resourc // Step 1: Check for validity of the UCI slot and other restrictions. // Check whether the UCI slot to be scheduled is >= last UCI HARQ ACK allocated slot for the UE. - // NOTE: The reason for having this logic is due to fact that COTS UE sends an invalid ACK bits if newly scheduled - // PDSCH has its UCI HARQ ACK slot before the (in time) UCI HARQ ACK slot of the previously scheduled PDSCH. + // See TS 38.214, clause 5.1, "In a given scheduled cell, the UE is not expected to receive a first PDSCH in slot i, + // with the corresponding HARQ-ACK assigned to be transmitted in slot j, and a second PDSCH starting later than the + // first PDSCH with its corresponding HARQ-ACK assigned to be transmitted in a slot before slot j". if (k1_candidate < min_pdsch_to_ack_slot_distance) { continue; } diff --git a/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp b/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp index 365ae926bb..d9354fccb5 100644 --- a/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp +++ b/lib/scheduler/uci_scheduling/uci_scheduler_impl.cpp @@ -142,7 +142,7 @@ void uci_scheduler_impl::add_ue(const ue_cell_configuration& ue_cfg) const unsigned csi_report_cfg_idx = 0; const auto& csi_report_cfg = ue_cfg.cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; const auto& period_pucch = - variant_get(csi_report_cfg.report_cfg_type); + std::get(csi_report_cfg.report_cfg_type); unsigned period_slots = csi_report_periodicity_to_uint(period_pucch.report_slot_period); add_resource(ue_cfg.crnti, period_pucch.report_slot_offset, period_slots, false); @@ -193,7 +193,7 @@ void uci_scheduler_impl::rem_ue(const ue_cell_configuration& ue_cfg) const unsigned csi_report_cfg_idx = 0; const auto& csi_report_cfg = ue_cfg.cfg_dedicated().csi_meas_cfg.value().csi_report_cfg_list[csi_report_cfg_idx]; const auto& period_pucch = - variant_get(csi_report_cfg.report_cfg_type); + std::get(csi_report_cfg.report_cfg_type); unsigned period_slots = csi_report_periodicity_to_uint(period_pucch.report_slot_period); rem_resource(ue_cfg.crnti, period_pucch.report_slot_offset, period_slots, false); diff --git a/lib/scheduler/ue_scheduling/dl_logical_channel_manager.cpp b/lib/scheduler/ue_scheduling/dl_logical_channel_manager.cpp index 9337da5713..d43bb450b2 100644 --- a/lib/scheduler/ue_scheduling/dl_logical_channel_manager.cpp +++ b/lib/scheduler/ue_scheduling/dl_logical_channel_manager.cpp @@ -84,11 +84,13 @@ unsigned dl_logical_channel_manager::allocate_mac_sdu(dl_msg_lc_info& subpdu, lc } // Account for available space and MAC subheader to decide the number of bytes to allocate. - unsigned alloc_bytes = std::min(rem_bytes, lch_bytes); + unsigned alloc_bytes = std::min(rem_bytes, get_mac_sdu_required_bytes(lch_bytes)); - // If it is last PDU of the TBS, allocate all leftover bytes. + // Allocate all leftover bytes in following cases: + // - If it is last PDU of the TBS. + // - [Implementation-defined] If \c leftover_bytes is < 5 bytes, as it results in small SDU size. unsigned leftover_bytes = rem_bytes - alloc_bytes; - if (leftover_bytes > 0 and (leftover_bytes <= MAX_MAC_SDU_SUBHEADER_SIZE or pending_bytes() == 0)) { + if (leftover_bytes > 0 and ((leftover_bytes <= MAX_MAC_SDU_SUBHEADER_SIZE + 1) or pending_bytes() == 0)) { alloc_bytes += leftover_bytes; } if (alloc_bytes == MAC_SDU_SUBHEADER_LENGTH_THRES + MIN_MAC_SDU_SUBHEADER_SIZE) { @@ -190,7 +192,7 @@ unsigned srsran::allocate_mac_ces(dl_msg_tb_info& tb_info, dl_logical_channel_ma unsigned dl_logical_channel_manager::allocate_ue_con_res_id_mac_ce(dl_msg_lc_info& subpdu, unsigned rem_bytes) { - if (not pending_con_res_id) { + if (not is_con_res_id_pending()) { return 0; } diff --git a/lib/scheduler/ue_scheduling/dl_logical_channel_manager.h b/lib/scheduler/ue_scheduling/dl_logical_channel_manager.h index 6370ee72c0..775c041c0d 100644 --- a/lib/scheduler/ue_scheduling/dl_logical_channel_manager.h +++ b/lib/scheduler/ue_scheduling/dl_logical_channel_manager.h @@ -38,7 +38,7 @@ class dl_logical_channel_manager /// LCID of the MAC CE. lcid_dl_sch_t ce_lcid; /// Holds payload of CE except UE Contention Resolution Identity. - variant ce_payload; + std::variant ce_payload; }; dl_logical_channel_manager(); @@ -75,11 +75,7 @@ class dl_logical_channel_manager bool has_pending_bytes(lcid_t lcid) const { return pending_bytes(lcid) > 0; } /// \brief Checks whether a ConRes CE is pending for transmission. - /// \remark ConRes CE is only sent when there is pending data for SRB0 or SRB1. - bool is_con_res_id_pending() const - { - return (pending_con_res_id and (has_pending_bytes(LCID_SRB0) or has_pending_bytes(LCID_SRB1))); - } + bool is_con_res_id_pending() const { return pending_con_res_id; } /// \brief Checks whether UE has pending CEs to be scheduled (ConRes excluded). bool has_pending_ces() const { return not pending_ces.empty(); } diff --git a/lib/scheduler/ue_scheduling/ue.cpp b/lib/scheduler/ue_scheduling/ue.cpp index 5c9604f6e3..c60f61a4a9 100644 --- a/lib/scheduler/ue_scheduling/ue.cpp +++ b/lib/scheduler/ue_scheduling/ue.cpp @@ -125,17 +125,6 @@ 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_srb0_or_srb1_newtx_bytes(bool is_srb0) const -{ - unsigned pending_bytes = dl_lc_ch_mgr.pending_bytes(is_srb0 ? LCID_SRB0 : LCID_SRB1); - - if (pending_bytes != 0) { - // In case SRB0 has data, only allocate SRB0 and CEs. - return pending_bytes + dl_lc_ch_mgr.pending_ue_con_res_id_ce_bytes(); - } - return pending_bytes; -} - unsigned ue::pending_ul_newtx_bytes() const { constexpr static unsigned SR_GRANT_BYTES = 512; @@ -176,11 +165,16 @@ unsigned ue::build_dl_transport_block_info(dl_msg_tb_info& tb_info, unsigned tb_ return total_subpdu_bytes; } -unsigned ue::build_dl_fallback_transport_block_info(dl_msg_tb_info& tb_info, unsigned tb_size_bytes, bool is_srb0) +unsigned ue::build_dl_fallback_transport_block_info(dl_msg_tb_info& tb_info, unsigned tb_size_bytes) { unsigned total_subpdu_bytes = 0; total_subpdu_bytes += allocate_ue_con_res_id_mac_ce(tb_info, dl_lc_ch_mgr, tb_size_bytes); - total_subpdu_bytes += - allocate_mac_sdus(tb_info, dl_lc_ch_mgr, tb_size_bytes - total_subpdu_bytes, is_srb0 ? LCID_SRB0 : LCID_SRB1); + // Since SRB0 PDU cannot be segmented, skip SRB0 if remaining TB size is not enough to fit entire PDU. + if (dl_lc_ch_mgr.has_pending_bytes(LCID_SRB0) and + ((tb_size_bytes - total_subpdu_bytes) >= dl_lc_ch_mgr.pending_bytes(LCID_SRB0))) { + total_subpdu_bytes += allocate_mac_sdus(tb_info, dl_lc_ch_mgr, tb_size_bytes - total_subpdu_bytes, LCID_SRB0); + return total_subpdu_bytes; + } + total_subpdu_bytes += allocate_mac_sdus(tb_info, dl_lc_ch_mgr, tb_size_bytes - total_subpdu_bytes, LCID_SRB1); return total_subpdu_bytes; } diff --git a/lib/scheduler/ue_scheduling/ue.h b/lib/scheduler/ue_scheduling/ue.h index c7802cc7ff..a791d8fdf4 100644 --- a/lib/scheduler/ue_scheduling/ue.h +++ b/lib/scheduler/ue_scheduling/ue.h @@ -134,6 +134,9 @@ class ue /// \brief Whether MAC ConRes CE is pending. bool is_conres_ce_pending() const { return dl_lc_ch_mgr.is_con_res_id_pending(); } + /// \brief Returns the UE pending ConRes CE bytes to be scheduled, if any. + unsigned pending_conres_ce_bytes() const { return dl_lc_ch_mgr.pending_ue_con_res_id_ce_bytes(); } + /// \brief Returns the UE pending CEs' bytes to be scheduled, if any. unsigned pending_ce_bytes() const { return dl_lc_ch_mgr.pending_ce_bytes(); } @@ -144,12 +147,6 @@ 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 that are not already allocated in a DL HARQ for SRB0 or SRB1. The - /// value is used to derive the required transport block size for an DL grant. - /// param[in] is_srb0 tells whether to computes the number of DL pending bytes for SRB0, if true; for SRB1 otherwise. - /// \return The number of DL pending bytes. - unsigned pending_dl_srb0_or_srb1_newtx_bytes(bool is_srb0) 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; @@ -166,7 +163,7 @@ class ue /// SRB0 or for SRB1 in fallback mode. /// It includes the UE Contention Resolution Identity CE if it is pending. /// \return Returns the number of bytes reserved in the TB for subPDUs (other than padding). - unsigned build_dl_fallback_transport_block_info(dl_msg_tb_info& tb_info, unsigned tb_size_bytes, bool is_srb0); + unsigned build_dl_fallback_transport_block_info(dl_msg_tb_info& tb_info, unsigned tb_size_bytes); private: /// Expert config parameters used for UE scheduler. diff --git a/lib/scheduler/ue_scheduling/ue_cell.h b/lib/scheduler/ue_scheduling/ue_cell.h index e33c292473..0cf8e256a3 100644 --- a/lib/scheduler/ue_scheduling/ue_cell.h +++ b/lib/scheduler/ue_scheduling/ue_cell.h @@ -63,6 +63,11 @@ class ue_cell harq_entity harqs; + // Slot at which PDSCH was allocated in the past for this UE in this cell. + slot_point last_pdsch_allocated_slot; + // Slot at which PUSCH was allocated in the past for this UE in this cell. + slot_point last_pusch_allocated_slot; + rnti_t rnti() const { return crnti_; } bwp_id_t active_bwp_id() const { return to_bwp_id(0); } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp index 45c904cd2b..afd601a216 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.cpp @@ -25,6 +25,8 @@ #include "../support/dci_builder.h" #include "../support/mcs_calculator.h" #include "../support/sched_result_helpers.h" +#include "ue_pdsch_alloc_param_candidate_searcher.h" +#include "ue_pusch_alloc_param_candidate_searcher.h" #include "srsran/ran/pdcch/coreset.h" #include "srsran/scheduler/scheduler_dci.h" #include "srsran/support/error_handling.h" @@ -46,10 +48,27 @@ void ue_cell_grid_allocator::add_cell(du_cell_index_t cell_index, cells.emplace(cell_index, cell_t{cell_index, &pdcch_sched, &uci_alloc, &cell_alloc}); } -void ue_cell_grid_allocator::slot_indication() +void ue_cell_grid_allocator::slot_indication(slot_point sl) { dl_attempts_count = 0; ul_attempts_count = 0; + // Clear slots which are in the past relative to current slot indication. + auto* pdsch_slot = slots_with_no_pdsch_space.begin(); + while (pdsch_slot != slots_with_no_pdsch_space.end()) { + if (*pdsch_slot < sl) { + pdsch_slot = slots_with_no_pdsch_space.erase(pdsch_slot); + continue; + } + ++pdsch_slot; + } + auto* pusch_slot = slots_with_no_pusch_space.begin(); + while (pusch_slot != slots_with_no_pusch_space.end()) { + if (*pusch_slot < sl) { + pusch_slot = slots_with_no_pusch_space.erase(pusch_slot); + continue; + } + ++pusch_slot; + } } alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& grant) @@ -78,372 +97,403 @@ alloc_outcome ue_cell_grid_allocator::allocate_dl_grant(const ue_pdsch_grant& gr const cell_configuration& cell_cfg = ue_cell_cfg.cell_cfg_common; const bwp_downlink_common& bwp_dl_cmn = *ue_cell_cfg.bwp(ue_cc->active_bwp_id()).dl_common; dl_harq_process& h_dl = ue_cc->harqs.dl_harq(grant.h_id); + const bool is_retx = not h_dl.empty(); + + // Fetch PDCCH resource grid allocator. + cell_slot_resource_allocator& pdcch_alloc = get_res_alloc(grant.cell_index)[0]; + if (not cell_cfg.is_dl_enabled(pdcch_alloc.slot)) { + logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: DL is not active in the PDCCH slot={}", + u.ue_index, + u.crnti, + pdcch_alloc.slot); + return alloc_outcome::skip_slot; + } - if (not ue_cc->is_active() and h_dl.empty()) { + if (not ue_cc->is_active() and not is_retx) { // newTxs are not allowed for inactive UEs. - logger.warning("PDSCH allocation failed. Cause: The ue={} carrier with cell_index={} is inactive. New DL Tx " - "transmissions are not allowed.", + logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: Carrier with cell_index={} is inactive. New DL Tx " + "transmissions are not allowed", u.ue_index, + u.crnti, grant.cell_index); return alloc_outcome::skip_ue; } - // Find a SearchSpace candidate. - const search_space_info* ss_info = ue_cell_cfg.find_search_space(grant.ss_id); - if (ss_info == nullptr) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: No valid SearchSpace found.", u.ue_index, u.crnti); - return alloc_outcome::invalid_params; + if (ue_cc->is_in_fallback_mode()) { + // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. + return alloc_outcome::skip_ue; } - if (ss_info->bwp->bwp_id != ue_cc->active_bwp_id()) { - logger.warning( - "ue={} rnti={}: Failed to allocate PDSCH. Cause: SearchSpace not valid for active BWP.", u.ue_index, u.crnti); + + if (not is_retx and not grant.recommended_nof_bytes.has_value()) { + logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: Recommended nof. bytes to schedule is not given " + "for new Tx with h_id={}", + u.ue_index, + u.crnti, + grant.h_id); return alloc_outcome::invalid_params; } - const search_space_configuration& ss_cfg = *ss_info->cfg; - const coreset_configuration& cs_cfg = *ss_info->coreset; - const aggregation_level aggr_lvl = - ue_cc->get_aggregation_level(ue_cc->link_adaptation_controller().get_effective_cqi(), *ss_info, true); + // [Implementation-defined] + // If there is large gap between two PDSCHs scheduled for a UE, \c last_pdsch_allocated_slot could be having an old + // slot value and the condition pdsch_alloc.slot (e.g. 47.0) <= ue_cc->last_pdsch_allocated_slot (e.g. 989.0) maybe be + // true for long time and UE may not get scheduled. + // This scenario can be prevented by resetting \c last_pdsch_allocated_slot when its behind current PDCCH slot by + // SCHEDULER_MAX_K0 number of slots. + if (ue_cc->last_pdsch_allocated_slot.valid() and + std::abs(pdcch_alloc.slot - ue_cc->last_pdsch_allocated_slot) > SCHEDULER_MAX_K0) { + ue_cc->last_pdsch_allocated_slot.clear(); + } - if (ue_cc->is_in_fallback_mode()) { - // Skip allocation for UEs in fallback mode, as it is handled by the SRB fallback scheduler. + // Create PDSCH param candidate search object. + ue_pdsch_alloc_param_candidate_searcher candidates{u, grant.cell_index, h_dl, pdcch_alloc.slot}; + if (candidates.is_empty()) { + // The conditions for a new PDSCH allocation for this UE were not met (e.g. lack of available SearchSpaces). return alloc_outcome::skip_ue; } - dci_dl_rnti_config_type dci_type; - if (not h_dl.empty()) { - dci_type = h_dl.last_alloc_params().dci_cfg_type; - } else { - dci_type = ss_info->get_dl_dci_format() == dci_dl_format::f1_0 ? dci_dl_rnti_config_type::c_rnti_f1_0 - : dci_dl_rnti_config_type::c_rnti_f1_1; - } + // Iterate through allocation parameter candidates. + for (const ue_pdsch_alloc_param_candidate_searcher::candidate& param_candidate : candidates) { + const pdsch_time_domain_resource_allocation& pdsch_td_cfg = param_candidate.pdsch_td_res(); + const search_space_info& ss_info = param_candidate.ss(); + const dci_dl_rnti_config_type dci_type = param_candidate.dci_dl_rnti_cfg_type(); + const search_space_configuration& ss_cfg = *ss_info.cfg; - // In case of retx, ensure the RI does not change. - const unsigned nof_dl_layers = - not h_dl.empty() ? h_dl.last_alloc_params().nof_layers : ue_cc->channel_state_manager().get_nof_dl_layers(); + // Fetch PDSCH resource grid allocator. + cell_slot_resource_allocator& pdsch_alloc = get_res_alloc(grant.cell_index)[pdsch_td_cfg.k0]; - const span pdsch_list = ss_info->pdsch_time_domain_list; - const pdsch_time_domain_resource_allocation& pdsch_td_cfg = pdsch_list[grant.time_res_index]; + // Verify only one PDSCH exists for an RNTI. + // See TS 38.214, clause 5.1, "For any HARQ process ID(s) in a given scheduled cell, the UE is not + // expected to receive a PDSCH that overlaps in time with another PDSCH". + if (ue_cc->last_pdsch_allocated_slot.valid() and pdsch_alloc.slot <= ue_cc->last_pdsch_allocated_slot) { + // Try next candidate. + continue; + } - // Fetch PDCCH and PDSCH resource grid allocators. - cell_slot_resource_allocator& pdcch_alloc = get_res_alloc(grant.cell_index)[0]; - cell_slot_resource_allocator& pdsch_alloc = get_res_alloc(grant.cell_index)[pdsch_td_cfg.k0]; - - if (pdsch_alloc.result.dl.bc.sibs.size() + pdsch_alloc.result.dl.paging_grants.size() + - pdsch_alloc.result.dl.rar_grants.size() + pdsch_alloc.result.dl.ue_grants.size() >= - expert_cfg.max_pdschs_per_slot) { - logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: Max number of PDSCHs per slot {} was reached.", - u.ue_index, - u.crnti, - expert_cfg.max_pdschs_per_slot); - return alloc_outcome::skip_slot; - } - if (not cell_cfg.is_dl_enabled(pdcch_alloc.slot)) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH in slot={}. Cause: DL is not active in the PDCCH slot", - u.ue_index, - u.crnti, - pdsch_alloc.slot); - return alloc_outcome::skip_slot; - } - if (not cell_cfg.is_dl_enabled(pdsch_alloc.slot)) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH in slot={}. Cause: DL is not active in the PDSCH slot", - u.ue_index, - u.crnti, - pdsch_alloc.slot); - return alloc_outcome::invalid_params; - } - // Check whether PDSCH time domain resource does not overlap with CORESET. - if (pdsch_td_cfg.symbols.start() < ss_cfg.get_first_symbol_index() + cs_cfg.duration) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH in slot={}. Cause: PDSCH time domain resource={} is " - "starting before the " - "CORESET symbols", + // Check if there is space in PDSCH resource grid. + const bool is_pdsch_full = + std::find(slots_with_no_pdsch_space.begin(), slots_with_no_pdsch_space.end(), pdsch_alloc.slot) != + slots_with_no_pdsch_space.end(); + if (is_pdsch_full) { + // Try next candidate. + continue; + } + + if (pdsch_alloc.result.dl.bc.sibs.size() + pdsch_alloc.result.dl.paging_grants.size() + + pdsch_alloc.result.dl.rar_grants.size() + pdsch_alloc.result.dl.ue_grants.size() >= + expert_cfg.max_pdschs_per_slot) { + logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: Max number of PDSCHs per slot {} was reached.", u.ue_index, u.crnti, - pdsch_alloc.slot, - grant.time_res_index); - return alloc_outcome::invalid_params; - } - // Check whether PDSCH time domain resource fits in DL symbols of the slot. - if (pdsch_td_cfg.symbols.stop() > cell_cfg.get_nof_dl_symbol_per_slot(pdsch_alloc.slot)) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH in slot={}. Cause: Nof. DL symbols in PDSCH slot shorter " - "than PDSCH time " - "domain resource={}", + expert_cfg.max_pdschs_per_slot); + return alloc_outcome::skip_slot; + } + + // Verify there is space in PDSCH and PDCCH result lists for new allocations. + if (pdsch_alloc.result.dl.ue_grants.full() or pdcch_alloc.result.dl.dl_pdcchs.full()) { + logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space available in scheduler output list", u.ue_index, - u.crnti, - pdsch_alloc.slot, - grant.time_res_index); - return alloc_outcome::invalid_params; - } - // Verify there is space in PDSCH and PDCCH result lists for new allocations. - if (pdsch_alloc.result.dl.ue_grants.full() or pdcch_alloc.result.dl.dl_pdcchs.full()) { - logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space available in scheduler output list", - u.ue_index, - u.crnti); - return alloc_outcome::skip_slot; - } + u.crnti); + return alloc_outcome::skip_slot; + } - // In case of retx, ensure the number of PRBs for the grant did not change. - if (not h_dl.empty() and grant.crbs.length() != h_dl.last_alloc_params().rbs.type1().length()) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: Number of CRBs has to remain constant during " - "retxs (Harq-id={}, nof_prbs expected={} != actual={})", + if (not cell_cfg.is_dl_enabled(pdsch_alloc.slot)) { + logger.debug("ue={} rnti={}: Failed to allocate PDSCH in slot={}. Cause: DL is not active in the PDSCH slot", u.ue_index, u.crnti, - h_dl.id, - h_dl.last_alloc_params().rbs.type1().length(), - grant.crbs.length()); - return alloc_outcome::invalid_params; - } - - // Verify only one PDSCH exists for a RNTI. - // See TS 38.214, release 15.8.0, clause 5.1. For any HARQ process ID(s) in a given scheduled cell, the UE is not - // expected to receive a PDSCH that overlaps in time with another PDSCH. - for (const dl_msg_alloc& pdsch : pdsch_alloc.result.dl.ue_grants) { - if (pdsch.pdsch_cfg.rnti == u.crnti) { - return alloc_outcome::skip_ue; + pdsch_alloc.slot); + return alloc_outcome::skip_slot; } - } - - // Allocate PDCCH position. - pdcch_dl_information* pdcch = - get_pdcch_sched(grant.cell_index).alloc_dl_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); - if (pdcch == nullptr) { - logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); - // TODO: For now, given that only one searchSpace is used, we skip the UE. In the future, we might still try - // different searchSpaces. - return alloc_outcome::skip_ue; - } - // Allocate UCI. UCI destination (i.e., PUCCH or PUSCH) depends on whether there exist a PUSCH grant for the UE. - unsigned k1 = 0; - span k1_list = ss_info->get_k1_candidates(); - std::optional uci = - get_uci_alloc(grant.cell_index) - .alloc_uci_harq_ue( - get_res_alloc(grant.cell_index), u.crnti, u.get_pcell().cfg(), pdsch_td_cfg.k0, k1_list, nullptr); - if (uci.has_value()) { - k1 = uci.value().k1; - pdcch->ctx.context.harq_feedback_timing = k1; - } else { - logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space in PUCCH.", u.ue_index, u.crnti); - get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - // TODO: For now, given that only one searchSpace is used, we skip the UE. In the future, we might still try - // different searchSpaces. - return alloc_outcome::skip_ue; - } + // Apply RB allocation limits. + const unsigned start_rb = std::max(expert_cfg.pdsch_crb_limits.start(), ss_info.dl_crb_lims.start()); + const unsigned end_rb = std::min(expert_cfg.pdsch_crb_limits.stop(), ss_info.dl_crb_lims.stop()); + if (start_rb >= end_rb) { + // Invalid RB allocation range. + return alloc_outcome::skip_slot; + } - // Fetch UL resource allocator. - cell_slot_resource_allocator& ul_alloc = get_res_alloc(grant.cell_index)[pdsch_td_cfg.k0 + k1]; - - // [Implementation-defined] Check whether max. PUCCHs per slot or max. UL grants per slot is reached if PDSCH - // allocation for current UE succeeds. If so, allocate remaining RBs to the current UE only if it's a new Tx. - // NOTE: At this point UCI is already allocated hence '>' is used rather than '>='. - crb_interval adjusted_crbs{grant.crbs}; - if (h_dl.empty() and ((ul_alloc.result.ul.pucchs.size() > (expert_cfg.max_pucchs_per_slot - 1)) or - ((ul_alloc.result.ul.pucchs.size() + ul_alloc.result.ul.puschs.size()) > - (expert_cfg.max_ul_grants_per_slot - 1)))) { - const crb_interval dl_crb_lims = {std::max(expert_cfg.pdsch_crb_limits.start(), ss_info->dl_crb_lims.start()), - std::min(expert_cfg.pdsch_crb_limits.stop(), ss_info->dl_crb_lims.stop())}; + const crb_interval dl_crb_lims = {start_rb, end_rb}; const crb_bitmap used_crbs = pdsch_alloc.dl_res_grid.used_crbs(bwp_dl_cmn.generic_params.scs, dl_crb_lims, pdsch_td_cfg.symbols); - adjusted_crbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0); - } + if (used_crbs.all()) { + slots_with_no_pdsch_space.push_back(pdsch_alloc.slot); + return alloc_outcome::skip_slot; + } + + grant_prbs_mcs mcs_prbs = + is_retx ? grant_prbs_mcs{h_dl.last_alloc_params().tb.front().value().mcs, + h_dl.last_alloc_params().rbs.type1().length()} + : ue_cc->required_dl_prbs(pdsch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); + // Try to limit the grant PRBs. + if (not is_retx) { + // Limit nof. RBs to allocate to maximum RBs provided in grant. + if (grant.max_nof_rbs.has_value()) { + mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); + } + // [Implementation-defined] In case of partial slots and nof. PRBs allocated equals to 1 probability of KO is + // high due to code not being able to cope with interference. So the solution is to increase the PRB allocation + // to greater than 1 PRB. + if (not cell_cfg.is_fully_dl_enabled(pdsch_alloc.slot) and mcs_prbs.n_prbs == 1) { + mcs_prbs.n_prbs = 2; + } + // [Implementation-defined] + // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs per slot + // and not X+1 nof. UEs. One way is by checking if the emtpy interval is less than 2 times the required RBs. If + // so, allocate all remaining RBs. NOTE: This approach won't hold good in case of low traffic scenario. + const unsigned twice_grant_crbs_length = + rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); + if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { + mcs_prbs.n_prbs = twice_grant_crbs_length; + } + } + + if (mcs_prbs.n_prbs == 0) { + logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: UE's CQI=0 ", u.ue_index, u.crnti); + return alloc_outcome::skip_ue; + } - // Verify CRBs fit in the chosen BWP. - if (not ss_info->dl_crb_lims.contains(adjusted_crbs)) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: CRBs={} are outside the valid limits={}.", + crb_interval crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); + if (crbs.empty()) { + logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: Cause: No more RBs available at slot={}", u.ue_index, u.crnti, - adjusted_crbs, - ss_info->dl_crb_lims); - get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::invalid_params; - } + pdsch_alloc.slot); + return alloc_outcome::skip_slot; + } - // Verify there is no RB collision. - if (pdsch_alloc.dl_res_grid.collides(bwp_dl_cmn.generic_params.scs, pdsch_td_cfg.symbols, adjusted_crbs)) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space available in scheduler RB resource grid.", - u.ue_index, - u.crnti); - get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::invalid_params; - } + // In case of Retx, the #CRBs need to stay the same. + if (is_retx and crbs.length() != h_dl.last_alloc_params().rbs.type1().length()) { + logger.debug( + "ue={} rnti={}: Failed to allocate PDSCH. Cause: No more RBs available at slot={} for h_id={} retransmission", + u.ue_index, + u.crnti, + pdsch_alloc.slot, + h_dl.id); + return alloc_outcome::skip_ue; + } - pdsch_config_params pdsch_cfg; - switch (dci_type) { - case dci_dl_rnti_config_type::c_rnti_f1_0: - pdsch_cfg = get_pdsch_config_f1_0_c_rnti(cell_cfg, &ue_cell_cfg, pdsch_list[grant.time_res_index]); - break; - case dci_dl_rnti_config_type::c_rnti_f1_1: - pdsch_cfg = get_pdsch_config_f1_1_c_rnti(ue_cell_cfg, pdsch_list[grant.time_res_index], nof_dl_layers); - break; - default: - report_fatal_error("Unsupported PDCCH DCI UL format"); - } + const aggregation_level aggr_lvl = + ue_cc->get_aggregation_level(ue_cc->link_adaptation_controller().get_effective_cqi(), ss_info, true); + // In case of retx, ensure the RI does not change. + const unsigned nof_dl_layers = + is_retx ? h_dl.last_alloc_params().nof_layers : ue_cc->channel_state_manager().get_nof_dl_layers(); + + // Allocate PDCCH position. + pdcch_dl_information* pdcch = get_pdcch_sched(grant.cell_index) + .alloc_dl_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); + if (pdcch == nullptr) { + logger.info("ue={} rnti={}: Failed to allocate PDSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); + return alloc_outcome::skip_ue; + } - // Reduce estimated MCS by 1 whenever CSI-RS is sent over a particular slot to account for the overhead of CSI-RS REs. - sch_mcs_index adjusted_mcs{grant.mcs}; - if (not pdsch_alloc.result.dl.csi_rs.empty()) { - // [Implementation-defined] The max MCS values below are set empirically and should avoid the effective code rate to - // exceed 0.95 due to the overhead of CSI-RS REs. - adjusted_mcs = adjusted_mcs == 0 ? adjusted_mcs : adjusted_mcs - 1; - uint8_t max_mcs_with_csi_rs = 28; - if (pdsch_cfg.mcs_table == pdsch_mcs_table::qam64) { - max_mcs_with_csi_rs = 26U; - } else if (pdsch_cfg.mcs_table == pdsch_mcs_table::qam256) { - max_mcs_with_csi_rs = 24U; - } - adjusted_mcs = static_cast(std::min(adjusted_mcs.to_uint(), max_mcs_with_csi_rs)); - } + // Allocate UCI. UCI destination (i.e., PUCCH or PUSCH) depends on whether there exist a PUSCH grant for the UE. + unsigned k1 = 0; + span k1_list = ss_info.get_k1_candidates(); + std::optional uci = + get_uci_alloc(grant.cell_index) + .alloc_uci_harq_ue( + get_res_alloc(grant.cell_index), u.crnti, u.get_pcell().cfg(), pdsch_td_cfg.k0, k1_list, nullptr); + if (uci.has_value()) { + k1 = uci.value().k1; + pdcch->ctx.context.harq_feedback_timing = k1; + } else { + logger.debug("ue={} rnti={}: Failed to allocate PDSCH. Cause: UCI allocation failed.", u.ue_index, u.crnti); + get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); + return alloc_outcome::skip_ue; + } - std::optional mcs_tbs_info; - // If it's a new Tx, compute the MCS and TBS. - if (h_dl.empty()) { - // As \c txDirectCurrentLocation, in \c SCS-SpecificCarrier, TS 38.331, "If this field (\c txDirectCurrentLocation) - // is absent for downlink within ServingCellConfigCommon and ServingCellConfigCommonSIB, the UE assumes the default - // value of 3300 (i.e. "Outside the carrier")". - bool contains_dc = false; - if (cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.has_value()) { - contains_dc = dc_offset_helper::is_contained( - cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.value(), - adjusted_crbs); - } - - mcs_tbs_info = compute_dl_mcs_tbs(pdsch_cfg, adjusted_mcs, adjusted_crbs.length(), contains_dc); - } else { - // It is a retx. - mcs_tbs_info.emplace( - sch_mcs_tbs{.mcs = h_dl.last_alloc_params().tb[0]->mcs, .tbs = h_dl.last_alloc_params().tb[0]->tbs_bytes}); - } + // Fetch UL resource allocator. + cell_slot_resource_allocator& ul_alloc = get_res_alloc(grant.cell_index)[pdsch_td_cfg.k0 + k1]; - // If there is not MCS-TBS info, it means no MCS exists such that the effective code rate is <= 0.95. - if (not mcs_tbs_info.has_value()) { - logger.warning( - "ue={} rnti={}: Failed to allocate PDSCH. Cause: no MCS such that code rate <= 0.95.", u.ue_index, u.crnti); - get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::invalid_params; - } + // [Implementation-defined] Check whether max. PUCCHs per slot or max. UL grants per slot is reached if PDSCH + // allocation for current UE succeeds. If so, allocate remaining RBs to the current UE only if it's a new Tx. + // NOTE: At this point UCI is already allocated hence '>' is used rather than '>='. + if (not is_retx and ((ul_alloc.result.ul.pucchs.size() > (expert_cfg.max_pucchs_per_slot - 1)) or + ((ul_alloc.result.ul.pucchs.size() + ul_alloc.result.ul.puschs.size()) > + (expert_cfg.max_ul_grants_per_slot - 1)))) { + crbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0); + } - // In case of retx, ensure the TBS does not change. - if (not h_dl.empty() and mcs_tbs_info->tbs != h_dl.last_alloc_params().tb[0]->tbs_bytes) { - logger.warning("ue={} rnti={}: Failed to allocate PDSCH. Cause: TBS has to remain constant during retxs " - "(Harq-id={}, tbs expected={} != actual={})", - u.ue_index, - u.crnti, - h_dl.id, - h_dl.last_alloc_params().tb[0]->tbs_bytes, - mcs_tbs_info->tbs); - return alloc_outcome::invalid_params; - } + // Verify there is no RB collision. + if (pdsch_alloc.dl_res_grid.collides(bwp_dl_cmn.generic_params.scs, pdsch_td_cfg.symbols, crbs)) { + logger.warning( + "ue={} rnti={}: Failed to allocate PDSCH. Cause: No space available in scheduler RB resource grid.", + u.ue_index, + u.crnti); + get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); + // TODO: Remove UCI allocated? + return alloc_outcome::invalid_params; + } - // Mark resources as occupied in the ResourceGrid. - pdsch_alloc.dl_res_grid.fill(grant_info{bwp_dl_cmn.generic_params.scs, pdsch_td_cfg.symbols, adjusted_crbs}); - - // Allocate UE DL HARQ. - bool is_new_data = h_dl.empty(); - if (is_new_data) { - // It is a new tx. - h_dl.new_tx(pdsch_alloc.slot, - k1, - expert_cfg.max_nof_harq_retxs, - uci.value().harq_bit_idx, - ue_cc->channel_state_manager().get_wideband_cqi(), - nof_dl_layers); - } else { - // It is a retx. - h_dl.new_retx(pdsch_alloc.slot, k1, uci.value().harq_bit_idx); - } + pdsch_config_params pdsch_cfg; + switch (dci_type) { + case dci_dl_rnti_config_type::c_rnti_f1_0: + pdsch_cfg = get_pdsch_config_f1_0_c_rnti(cell_cfg, &ue_cell_cfg, pdsch_td_cfg); + break; + case dci_dl_rnti_config_type::c_rnti_f1_1: + pdsch_cfg = get_pdsch_config_f1_1_c_rnti(ue_cell_cfg, pdsch_td_cfg, nof_dl_layers); + break; + default: + report_fatal_error("Unsupported PDCCH DCI UL format"); + } - // Fill DL PDCCH DCI PDU. - // Number of possible Downlink Assignment Indexes {0, ..., 3} as per TS38.213 Section 9.1.3. - static constexpr unsigned DAI_MOD = 4U; - uint8_t rv = ue_cc->get_pdsch_rv(h_dl); - // For allocation on PUSCH, we use a PUCCH resource indicator set to 0, as it will get ignored by the UE. - const unsigned pucch_res_indicator = - uci.value().pucch_res_indicator.has_value() ? uci.value().pucch_res_indicator.value() : 0U; - switch (dci_type) { - case dci_dl_rnti_config_type::c_rnti_f1_0: - build_dci_f1_0_c_rnti(pdcch->dci, - ue_cell_cfg.search_space(grant.ss_id), - cell_cfg.dl_cfg_common.init_dl_bwp, - adjusted_crbs, - grant.time_res_index, - k1, - pucch_res_indicator, - uci.value().harq_bit_idx % DAI_MOD, - mcs_tbs_info.value().mcs, - rv, - h_dl); - break; - case dci_dl_rnti_config_type::c_rnti_f1_1: - build_dci_f1_1_c_rnti(pdcch->dci, - ue_cell_cfg, - grant.ss_id, - crb_to_prb(ss_info->dl_crb_lims, adjusted_crbs), - grant.time_res_index, - k1, - pucch_res_indicator, - uci.value().harq_bit_idx % DAI_MOD, - mcs_tbs_info.value().mcs, - rv, - h_dl, - nof_dl_layers); - break; - default: - report_fatal_error("Unsupported RNTI type for PDSCH allocation"); - } + // Reduce estimated MCS by 1 whenever CSI-RS is sent over a particular slot to account for the overhead of CSI-RS + // REs. + sch_mcs_index adjusted_mcs{mcs_prbs.mcs}; + if (not is_retx and not pdsch_alloc.result.dl.csi_rs.empty()) { + // [Implementation-defined] The max MCS values below are set empirically and should avoid the effective code rate + // to exceed 0.95 due to the overhead of CSI-RS REs. + adjusted_mcs = adjusted_mcs == 0 ? adjusted_mcs : adjusted_mcs - 1; + uint8_t max_mcs_with_csi_rs = 28; + if (pdsch_cfg.mcs_table == pdsch_mcs_table::qam64) { + max_mcs_with_csi_rs = 26U; + } else if (pdsch_cfg.mcs_table == pdsch_mcs_table::qam256) { + max_mcs_with_csi_rs = 24U; + } + adjusted_mcs = static_cast(std::min(adjusted_mcs.to_uint(), max_mcs_with_csi_rs)); + } - // Fill PDSCH PDU. - dl_msg_alloc& msg = pdsch_alloc.result.dl.ue_grants.emplace_back(); - msg.context.ue_index = u.ue_index; - msg.context.k1 = k1; - msg.context.ss_id = ss_cfg.get_id(); - msg.context.nof_retxs = h_dl.tb(0).nof_retxs; - if (is_new_data and ue_cc->link_adaptation_controller().is_dl_olla_enabled()) { - msg.context.olla_offset = ue_cc->link_adaptation_controller().dl_cqi_offset(); - } - switch (pdcch->dci.type) { - case dci_dl_rnti_config_type::c_rnti_f1_0: - build_pdsch_f1_0_c_rnti(msg.pdsch_cfg, - pdsch_cfg, - mcs_tbs_info.value().tbs, - u.crnti, - cell_cfg, - ue_cell_cfg.search_space(grant.ss_id), - pdcch->dci.c_rnti_f1_0, - adjusted_crbs, - is_new_data); - break; - case dci_dl_rnti_config_type::c_rnti_f1_1: - build_pdsch_f1_1_c_rnti(msg.pdsch_cfg, - pdsch_cfg, - mcs_tbs_info.value(), - u.crnti, + std::optional mcs_tbs_info; + // If it's a new Tx, compute the MCS and TBS. + if (not is_retx) { + // As \c txDirectCurrentLocation, in \c SCS-SpecificCarrier, TS 38.331, "If this field (\c + // txDirectCurrentLocation) is absent for downlink within ServingCellConfigCommon and ServingCellConfigCommonSIB, + // the UE assumes the default value of 3300 (i.e. "Outside the carrier")". + bool contains_dc = false; + if (cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.has_value()) { + contains_dc = dc_offset_helper::is_contained( + cell_cfg.dl_cfg_common.freq_info_dl.scs_carrier_list.back().tx_direct_current_location.value(), crbs); + } + + mcs_tbs_info = compute_dl_mcs_tbs(pdsch_cfg, adjusted_mcs, crbs.length(), contains_dc); + } else { + // It is a retx. + mcs_tbs_info.emplace( + sch_mcs_tbs{.mcs = h_dl.last_alloc_params().tb[0]->mcs, .tbs = h_dl.last_alloc_params().tb[0]->tbs_bytes}); + } + + // If there is not MCS-TBS info, it means no MCS exists such that the effective code rate is <= 0.95. + if (not mcs_tbs_info.has_value()) { + logger.warning( + "ue={} rnti={}: Failed to allocate PDSCH. Cause: no MCS such that code rate <= 0.95.", u.ue_index, u.crnti); + get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); + return alloc_outcome::skip_ue; + } + + // Mark resources as occupied in the ResourceGrid. + pdsch_alloc.dl_res_grid.fill(grant_info{bwp_dl_cmn.generic_params.scs, pdsch_td_cfg.symbols, crbs}); + + // Allocate UE DL HARQ. + bool is_new_data = not is_retx; + if (is_new_data) { + // It is a new tx. + h_dl.new_tx(pdsch_alloc.slot, + k1, + expert_cfg.max_nof_harq_retxs, + uci.value().harq_bit_idx, + ue_cc->channel_state_manager().get_wideband_cqi(), + nof_dl_layers); + } else { + // It is a retx. + h_dl.new_retx(pdsch_alloc.slot, k1, uci.value().harq_bit_idx); + } + + // Fill DL PDCCH DCI PDU. + // Number of possible Downlink Assignment Indexes {0, ..., 3} as per TS38.213 Section 9.1.3. + static constexpr unsigned DAI_MOD = 4U; + uint8_t rv = ue_cc->get_pdsch_rv(h_dl); + // For allocation on PUSCH, we use a PUCCH resource indicator set to 0, as it will get ignored by the UE. + const unsigned pucch_res_indicator = + uci.value().pucch_res_indicator.has_value() ? uci.value().pucch_res_indicator.value() : 0U; + switch (dci_type) { + case dci_dl_rnti_config_type::c_rnti_f1_0: + build_dci_f1_0_c_rnti(pdcch->dci, + ss_info, + cell_cfg.dl_cfg_common.init_dl_bwp, + crbs, + param_candidate.pdsch_td_res_index(), + k1, + pucch_res_indicator, + uci.value().harq_bit_idx % DAI_MOD, + mcs_tbs_info.value().mcs, + rv, + h_dl); + break; + case dci_dl_rnti_config_type::c_rnti_f1_1: + build_dci_f1_1_c_rnti(pdcch->dci, ue_cell_cfg, - grant.ss_id, - pdcch->dci.c_rnti_f1_1, - adjusted_crbs, + ss_cfg.get_id(), + crb_to_prb(ss_info.dl_crb_lims, crbs), + param_candidate.pdsch_td_res_index(), + k1, + pucch_res_indicator, + uci.value().harq_bit_idx % DAI_MOD, + mcs_tbs_info.value().mcs, + rv, h_dl, - ue_cc->channel_state_manager()); - break; - default: - report_fatal_error("Unsupported PDCCH DL DCI format"); - } + nof_dl_layers); + break; + default: + report_fatal_error("Unsupported RNTI type for PDSCH allocation"); + } - // Save set PDCCH and PDSCH PDU parameters in HARQ process. - dl_harq_sched_context pdsch_sched_ctx; - pdsch_sched_ctx.dci_cfg_type = pdcch->dci.type; - if (is_new_data) { - pdsch_sched_ctx.olla_mcs = - ue_cc->link_adaptation_controller().calculate_dl_mcs(msg.pdsch_cfg.codewords[0].mcs_table); - } - h_dl.save_alloc_params(pdsch_sched_ctx, msg.pdsch_cfg); + // Fill PDSCH PDU. + dl_msg_alloc& msg = pdsch_alloc.result.dl.ue_grants.emplace_back(); + msg.context.ue_index = u.ue_index; + msg.context.k1 = k1; + msg.context.ss_id = ss_cfg.get_id(); + msg.context.nof_retxs = h_dl.tb(0).nof_retxs; + if (is_new_data and ue_cc->link_adaptation_controller().is_dl_olla_enabled()) { + msg.context.olla_offset = ue_cc->link_adaptation_controller().dl_cqi_offset(); + } + switch (pdcch->dci.type) { + case dci_dl_rnti_config_type::c_rnti_f1_0: + build_pdsch_f1_0_c_rnti(msg.pdsch_cfg, + pdsch_cfg, + mcs_tbs_info.value().tbs, + u.crnti, + cell_cfg, + ss_info, + pdcch->dci.c_rnti_f1_0, + crbs, + is_new_data); + break; + case dci_dl_rnti_config_type::c_rnti_f1_1: + build_pdsch_f1_1_c_rnti(msg.pdsch_cfg, + pdsch_cfg, + mcs_tbs_info.value(), + u.crnti, + ue_cell_cfg, + ss_cfg.get_id(), + pdcch->dci.c_rnti_f1_1, + crbs, + h_dl, + ue_cc->channel_state_manager()); + break; + default: + report_fatal_error("Unsupported PDCCH DL DCI format"); + } + + // Save set PDCCH and PDSCH PDU parameters in HARQ process. + dl_harq_sched_context pdsch_sched_ctx; + pdsch_sched_ctx.dci_cfg_type = pdcch->dci.type; + if (is_new_data) { + pdsch_sched_ctx.olla_mcs = + ue_cc->link_adaptation_controller().calculate_dl_mcs(msg.pdsch_cfg.codewords[0].mcs_table); + 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); + 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); + } + + return alloc_outcome::success; } - return alloc_outcome::success; + // No candidates for PDSCH allocation. + return alloc_outcome::invalid_params; } alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& grant) @@ -477,388 +527,479 @@ alloc_outcome ue_cell_grid_allocator::allocate_ul_grant(const ue_pusch_grant& gr const ue_cell_configuration& ue_cell_cfg = ue_cc->cfg(); const cell_configuration& cell_cfg = ue_cell_cfg.cell_cfg_common; ul_harq_process& h_ul = ue_cc->harqs.ul_harq(grant.h_id); + const bool is_retx = not h_ul.empty(); + + // Fetch PDCCH resource grid allocators. + cell_slot_resource_allocator& pdcch_alloc = get_res_alloc(grant.cell_index)[pdcch_delay_in_slots]; + if (not cell_cfg.is_dl_enabled(pdcch_alloc.slot)) { + logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: DL is not active in the PDCCH slot={}", + u.ue_index, + u.crnti, + pdcch_alloc.slot); + return alloc_outcome::skip_slot; + } + + // Verify there is space in PDCCH result lists for new allocations. + if (pdcch_alloc.result.dl.ul_pdcchs.full()) { + logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: Maximum number of PDCCH grants per " + "slot {} reached", + u.ue_index, + u.crnti, + pdcch_alloc.result.dl.ul_pdcchs.capacity()); + return alloc_outcome::skip_slot; + } - if (not ue_cc->is_active() and h_ul.empty()) { + if (not ue_cc->is_active() and not is_retx) { // newTxs are not allowed for inactive UEs. - logger.warning("PUSCH allocation failed. Cause: The ue={} carrier with cell_index={} is inactive", + logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: The ue={} carrier with cell_index={} is inactive", u.ue_index, + u.crnti, grant.cell_index); return alloc_outcome::skip_ue; } - // Find a SearchSpace candidate. - const search_space_info* ss_info = ue_cell_cfg.find_search_space(grant.ss_id); - if (ss_info == nullptr) { - logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: No valid SearchSpace found.", u.ue_index, u.crnti); - return alloc_outcome::invalid_params; - } - if (ss_info->bwp->bwp_id != ue_cc->active_bwp_id()) { - logger.warning( - "ue={} rnti={}: Failed to allocate PUSCH. Cause: Chosen SearchSpace {} does not belong to the active BWP {}", - u.ue_index, - u.crnti, - grant.ss_id, - ue_cc->active_bwp_id()); + if (not is_retx and not grant.recommended_nof_bytes.has_value()) { + logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: Recommended nof. bytes to schedule is not given " + "for new Tx with h_id={}", + u.ue_index, + u.crnti, + grant.h_id); return alloc_outcome::invalid_params; } - const search_space_configuration& ss_cfg = *ss_info->cfg; - const bwp_uplink_common& bwp_ul_cmn = *ss_info->bwp->ul_common; - const subcarrier_spacing scs = bwp_ul_cmn.generic_params.scs; - const pusch_time_domain_resource_allocation& pusch_td_cfg = ss_info->pusch_time_domain_list[grant.time_res_index]; - const dci_ul_rnti_config_type ss_supported_crnti_dci_type = ss_info->get_ul_dci_format() == dci_ul_format::f0_0 - ? dci_ul_rnti_config_type::c_rnti_f0_0 - : dci_ul_rnti_config_type::c_rnti_f0_1; - const dci_ul_rnti_config_type dci_type = - h_ul.empty() ? ss_supported_crnti_dci_type : h_ul.last_tx_params().dci_cfg_type; - - const aggregation_level aggr_lvl = - ue_cc->get_aggregation_level(ue_cc->link_adaptation_controller().get_effective_cqi(), *ss_info, false); - - // See 3GPP TS 38.213, clause 10.1, - // A UE monitors PDCCH candidates in one or more of the following search spaces sets - // - a Type1-PDCCH CSS set configured by ra-SearchSpace in PDCCH-ConfigCommon for a DCI format with - // CRC scrambled by a RA-RNTI, a MsgB-RNTI, or a TC-RNTI on the primary cell. - if (dci_type == dci_ul_rnti_config_type::tc_rnti_f0_0 and - grant.ss_id != cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.ra_search_space_id) { - logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: SearchSpace not valid for re-transmission.", - u.ue_index, - u.crnti); - return alloc_outcome::invalid_params; + + // [Implementation-defined] + // If there is large gap between two PUSCHs scheduled for a UE, \c last_pusch_allocated_slot could be having an old + // slot value and the condition pusch_alloc.slot (e.g. 47.3) <= ue_cc->last_pusch_allocated_slot (e.g. 989.3) maybe be + // true for long time and UE may not get scheduled even after receiving maximum nof. SR indication configured to UE + // and eventually UE PRACHes. + // This scenario can be prevented by resetting \c last_pusch_allocated_slot when its behind current PDCCH slot by + // SCHEDULER_MAX_K2 number of slots. + if (ue_cc->last_pusch_allocated_slot.valid() and + std::abs(pdcch_alloc.slot - ue_cc->last_pusch_allocated_slot) > SCHEDULER_MAX_K2) { + ue_cc->last_pusch_allocated_slot.clear(); } - // In case of re-transmission DCI format must remain same and therefore its necessary to find the SS which support - // that DCI format. - if (dci_type != dci_ul_rnti_config_type::tc_rnti_f0_0 and dci_type != ss_supported_crnti_dci_type) { - logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: DCI format {} in HARQ retx is not supported in " - "SearchSpace {}.", - u.ue_index, - u.crnti, - dci_ul_rnti_config_format(h_ul.last_tx_params().dci_cfg_type), - grant.ss_id); - return alloc_outcome::invalid_params; + // Create PUSCH param candidate search object. + ue_pusch_alloc_param_candidate_searcher candidates{u, grant.cell_index, h_ul, pdcch_alloc.slot}; + if (candidates.is_empty()) { + // The conditions for a new PUSCH allocation for this UE were not met (e.g. lack of available SearchSpaces). + return alloc_outcome::skip_ue; } - // Fetch PDCCH and PDSCH resource grid allocators. - cell_slot_resource_allocator& pdcch_alloc = get_res_alloc(grant.cell_index)[pdcch_delay_in_slots]; - cell_slot_resource_allocator& pusch_alloc = - get_res_alloc(grant.cell_index)[pdcch_delay_in_slots + pusch_td_cfg.k2 + cell_cfg.ntn_cs_koffset]; + // Iterate through allocation parameter candidates. + for (const ue_pusch_alloc_param_candidate_searcher::candidate& param_candidate : candidates) { + const pusch_time_domain_resource_allocation& pusch_td_cfg = param_candidate.pusch_td_res(); + const search_space_info& ss_info = param_candidate.ss(); + const dci_ul_rnti_config_type dci_type = param_candidate.dci_ul_rnti_cfg_type(); + const search_space_configuration& ss_cfg = *ss_info.cfg; + const bwp_uplink_common& bwp_ul_cmn = *ss_info.bwp->ul_common; + const subcarrier_spacing scs = bwp_ul_cmn.generic_params.scs; + const unsigned final_k2 = pusch_td_cfg.k2 + cell_cfg.ntn_cs_koffset; + + // Fetch PUSCH resource grid allocators. + cell_slot_resource_allocator& pusch_alloc = get_res_alloc(grant.cell_index)[pdcch_delay_in_slots + final_k2]; + + // Verify only one PUSCH exists for an RNTI. + // See TS 38.214, clause 6.1, "For any HARQ process ID(s) in a given scheduled cell, the UE is not expected to + // transmit a PUSCH that overlaps in time with another PUSCH". + // "For any two HARQ process IDs in a given scheduled cell, if the UE is scheduled to start a first PUSCH + // transmission starting in symbol j by a PDCCH ending in symbol i, the UE is not expected to be scheduled to + // transmit a PUSCH starting earlier than the end of the first PUSCH by a PDCCH that ends later than symbol i". + if (ue_cc->last_pusch_allocated_slot.valid() and pusch_alloc.slot <= ue_cc->last_pusch_allocated_slot) { + // Try next candidate. + continue; + } - if (not cell_cfg.is_dl_enabled(pdcch_alloc.slot)) { - logger.warning("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: DL is not active in the PDCCH slot", + // Check if there is space in PUSCH resource grid. + const bool is_pusch_full = + std::find(slots_with_no_pusch_space.begin(), slots_with_no_pusch_space.end(), pusch_alloc.slot) != + slots_with_no_pusch_space.end(); + if (is_pusch_full) { + // Try next candidate. + continue; + } + + if (not cell_cfg.is_ul_enabled(pusch_alloc.slot)) { + logger.warning( + "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: UL is not active in the PUSCH slot (k2={})", + u.ue_index, + u.crnti, + pusch_alloc.slot, + final_k2); + // Try next candidate. + continue; + } + + // When checking the number of remaining grants for PUSCH, take into account that the PUCCH grants for this UE will + // be removed when multiplexing the UCI on PUSCH. + unsigned pusch_pdu_rem_space = get_space_left_for_pusch_pdus(pusch_alloc.result, u.crnti, expert_cfg); + if (pusch_pdu_rem_space == 0) { + if (pusch_alloc.result.ul.puschs.size() >= expert_cfg.max_puschs_per_slot) { + logger.info( + "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of PUSCHs per slot {} was reached.", + u.ue_index, + u.crnti, + pusch_alloc.slot, + expert_cfg.max_puschs_per_slot); + } else { + logger.info("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Max number of UL grants per slot {} " + "was reached.", + u.ue_index, + u.crnti, + pusch_alloc.slot, + expert_cfg.max_puschs_per_slot); + } + // Try next candidate. + continue; + } + + // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled using common + // PUCCH resources. + if (get_uci_alloc(grant.cell_index).has_uci_harq_on_common_pucch_res(u.crnti, pusch_alloc.slot)) { + logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: UE has PUCCH grant using common PUCCH " + "resources scheduled", u.ue_index, u.crnti, pusch_alloc.slot); - return alloc_outcome::skip_slot; - } - if (not cell_cfg.is_ul_enabled(pusch_alloc.slot)) { - logger.warning( - "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: UL is not active in the PUSCH slot (k2={})", - u.ue_index, - u.crnti, - pusch_alloc.slot, - pusch_td_cfg.k2); - return alloc_outcome::invalid_params; - } + // Try next candidate. + continue; + } - // When checking the number of remaining grants for PUSCH, take into account that the PUCCH grants for this UE will be - // removed when multiplexing the UCI on PUSCH. - unsigned pusch_pdu_rem_space = get_space_left_for_pusch_pdus(pusch_alloc.result, u.crnti, expert_cfg); - if (pusch_pdu_rem_space == 0) { - if (pusch_alloc.result.ul.puschs.size() >= expert_cfg.max_puschs_per_slot) { - logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: Max number of PUSCHs per slot {} was reached.", - u.ue_index, - u.crnti, - expert_cfg.max_puschs_per_slot); - } else { - logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: Max number of UL grants per slot {} was reached.", - u.ue_index, - u.crnti, - expert_cfg.max_puschs_per_slot); + const unsigned start_ul_symbols = + NOF_OFDM_SYM_PER_SLOT_NORMAL_CP - cell_cfg.get_nof_ul_symbol_per_slot(pusch_alloc.slot); + // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for + // the first transmission. + const bool sym_length_match_prev_grant_for_retx = + is_retx ? pusch_td_cfg.symbols.length() == h_ul.last_tx_params().nof_symbols : true; + if (pusch_td_cfg.symbols.start() < start_ul_symbols or + pusch_td_cfg.symbols.stop() > (start_ul_symbols + cell_cfg.get_nof_ul_symbol_per_slot(pusch_alloc.slot)) or + !sym_length_match_prev_grant_for_retx) { + // Try next candidate. + continue; } - return alloc_outcome::skip_slot; - } - // Verify there is space in PDCCH result lists for new allocations. - if (pdcch_alloc.result.dl.ul_pdcchs.full()) { - logger.warning( - "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Maximum number of PDCCH grants per slot {} reached", - u.ue_index, - u.crnti, - pusch_alloc.slot, - pdcch_alloc.result.dl.ul_pdcchs.capacity()); - return alloc_outcome::skip_slot; - } + // Apply RB allocation limits. + const unsigned start_rb = std::max(expert_cfg.pusch_crb_limits.start(), ss_info.ul_crb_lims.start()); + const unsigned end_rb = std::min(expert_cfg.pusch_crb_limits.stop(), ss_info.ul_crb_lims.stop()); + if (start_rb >= end_rb) { + logger.debug("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Invalid RB allocation range [{}, {})", + u.ue_index, + u.crnti, + pusch_alloc.slot, + start_rb, + end_rb); + // Try next candidate. + continue; + } - // Verify only one PUSCH exists for a RNTI. - for (const ul_sched_info& pusch : pusch_alloc.result.ul.puschs) { - if (pusch.pusch_cfg.rnti == u.crnti) { - return alloc_outcome::skip_ue; + const crb_interval ul_crb_lims = {start_rb, end_rb}; + const prb_bitmap used_crbs = pusch_alloc.ul_res_grid.used_crbs(scs, ul_crb_lims, pusch_td_cfg.symbols); + if (used_crbs.all()) { + slots_with_no_pusch_space.push_back(pusch_alloc.slot); + // Try next candidate. + continue; } - } - // [Implementation-defined] We skip allocation of PUSCH if there is already a PUCCH grant scheduled using common PUCCH - // resources. - if (get_uci_alloc(grant.cell_index).has_uci_harq_on_common_pucch_res(u.crnti, pusch_alloc.slot)) { - logger.debug("ue={} rnti={} Allocation of PUSCH in slot={} skipped. Cause: UE has PUCCH grant using common PUCCH " - "resources scheduled", - u.ue_index, - u.crnti, - pusch_alloc.slot); - return alloc_outcome::skip_ue; - } + // Compute the MCS and the number of PRBs, depending on the pending bytes to transmit. + grant_prbs_mcs mcs_prbs = + is_retx ? grant_prbs_mcs{h_ul.last_tx_params().mcs, h_ul.last_tx_params().rbs.type1().length()} + : ue_cc->required_ul_prbs(pusch_td_cfg, grant.recommended_nof_bytes.value(), dci_type); + // Try to limit the grant PRBs. + if (not is_retx) { + // Limit nof. RBs to allocate to maximum RBs provided in grant. + if (grant.max_nof_rbs.has_value()) { + mcs_prbs.n_prbs = std::min(mcs_prbs.n_prbs, grant.max_nof_rbs.value()); + } + // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If + // so, allocate remaining RBs to the current UE only if it's a new Tx. + if (pusch_pdu_rem_space == 1) { + mcs_prbs.n_prbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0).length(); + } + // Due to the pre-allocated UCI bits, MCS 0 and PRB 1 would not leave any space for the payload on the TBS, as + // all the space would be taken by the UCI bits. As a result of this, the effective code rate would be 0 and the + // allocation would fail and be postponed to the next slot. + // [Implementation-defined] In our tests, we have seen that MCS 5 with 1 PRB can lead (depending on the + // configuration) to a non-valid MCS-PRB allocation; therefore, we set 6 as minimum value for 1 PRB. + // TODO: Remove this part and handle the problem with a loop that is general for any configuration. + const sch_mcs_index min_mcs_for_1_prb = static_cast(6U); + const unsigned min_allocable_prbs = 1U; + if (mcs_prbs.mcs < min_mcs_for_1_prb and mcs_prbs.n_prbs <= min_allocable_prbs) { + ++mcs_prbs.n_prbs; + } + // [Implementation-defined] + // Check whether to allocate all remaining RBs or not. This is done to ensure we allocate only X nof. UEs per slot + // and not X+1 nof. UEs. One way is by checking if the emtpy interval is less than 2 times the required RBs. If + // so, allocate all remaining RBs. NOTE: This approach won't hold good in case of low traffic scenario. + const unsigned twice_grant_crbs_length = + rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs * 2, 0).length(); + if (twice_grant_crbs_length < (mcs_prbs.n_prbs * 2)) { + mcs_prbs.n_prbs = twice_grant_crbs_length; + } + } - // [Implementation-defined] Check whether max. UL grants per slot is reached if PUSCH for current UE succeeds. If so, - // allocate remaining RBs to the current UE only if it's a new Tx. - crb_interval adjusted_crbs{grant.crbs}; - if (h_ul.empty() and pusch_pdu_rem_space == 1) { - const crb_interval ul_crb_lims = {std::max(expert_cfg.pusch_crb_limits.start(), ss_info->ul_crb_lims.start()), - std::min(expert_cfg.pusch_crb_limits.stop(), ss_info->ul_crb_lims.stop())}; - const crb_bitmap used_crbs = - pusch_alloc.ul_res_grid.used_crbs(bwp_ul_cmn.generic_params.scs, ul_crb_lims, pusch_td_cfg.symbols); - adjusted_crbs = rb_helper::find_empty_interval_of_length(used_crbs, used_crbs.size(), 0); - } + // NOTE: This should never happen, but it's safe not to proceed if we get n_prbs == 0. + if (mcs_prbs.n_prbs == 0) { + logger.debug( + "ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: MCS and PRBs computation resulted in no PRBs " + "allocated to this UE", + u.ue_index, + u.crnti, + pusch_alloc.slot); + return alloc_outcome::skip_ue; + } - // Verify CRBs allocation. - if (not ss_info->ul_crb_lims.contains(adjusted_crbs)) { - logger.warning("rnti={} Failed to allocate PUSCH. Cause: CRBs {} allocated outside the BWP {}", + crb_interval crbs = rb_helper::find_empty_interval_of_length(used_crbs, mcs_prbs.n_prbs, 0); + if (crbs.empty()) { + logger.debug("ue={} rnti={}: Failed to allocate PUSCH. Cause: Cause: No more RBs available at slot={}", u.ue_index, u.crnti, - adjusted_crbs, - ss_info->ul_crb_lims); - return alloc_outcome::invalid_params; - } + pusch_alloc.slot); + // Try next candidate. + continue; + } - // In case of retx, ensure the number of PRBs for the grant did not change. - if (not h_ul.empty() and adjusted_crbs.length() != h_ul.last_tx_params().rbs.type1().length()) { - logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: Number of CRBs has to remain constant during retxs " - "(harq-id={}, " - "nof_prbs={}!={})", - u.ue_index, - u.crnti, - h_ul.id, - h_ul.last_tx_params().rbs.type1().length(), - adjusted_crbs.length()); - return alloc_outcome::invalid_params; - } + // In case of Retx, the #CRBs need to stay the same. + if (is_retx and crbs.length() != h_ul.last_tx_params().rbs.type1().length()) { + logger.debug( + "ue={} rnti={}: Failed to allocate PUSCH. Cause: No more RBs available at slot={} for h_id={} retransmission", + u.ue_index, + u.crnti, + pusch_alloc.slot, + h_ul.id); + // Try next candidate. + continue; + } - // Verify there is no RB collision. - if (pusch_alloc.ul_res_grid.collides(scs, pusch_td_cfg.symbols, adjusted_crbs)) { - logger.warning("ue={} rnti={}: Failed to allocate PUSCH. Cause: No space available in scheduler RB resource grid.", - u.ue_index, - u.crnti); - return alloc_outcome::invalid_params; - } + // Verify there is no RB collision. + if (pusch_alloc.ul_res_grid.collides(scs, pusch_td_cfg.symbols, crbs)) { + logger.warning("ue={} rnti={}: Failed to allocate PUSCH in slot={}. Cause: Allocation collides with existing " + "PUSCH in RB grid [{}, {})", + u.ue_index, + u.crnti, + pusch_alloc.slot, + crbs.start(), + crbs.stop()); + // Try next candidate. + continue; + } - // Allocate PDCCH position. - pdcch_ul_information* pdcch = - get_pdcch_sched(grant.cell_index).alloc_ul_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); - if (pdcch == nullptr) { - logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); - return alloc_outcome::skip_ue; - } + const aggregation_level aggr_lvl = + ue_cc->get_aggregation_level(ue_cc->link_adaptation_controller().get_effective_cqi(), ss_info, false); - const unsigned nof_harq_ack_bits = - get_uci_alloc(grant.cell_index).get_scheduled_pdsch_counter_in_ue_uci(pusch_alloc, u.crnti); - - const bool is_csi_report_slot = - csi_helper::is_csi_reporting_slot(u.get_pcell().cfg().cfg_dedicated(), pusch_alloc.slot); - - // Fetch PUSCH parameters based on type of transmission. - pusch_config_params pusch_cfg; - switch (dci_type) { - case dci_ul_rnti_config_type::tc_rnti_f0_0: - pusch_cfg = get_pusch_config_f0_0_tc_rnti(cell_cfg, pusch_td_cfg); - break; - case dci_ul_rnti_config_type::c_rnti_f0_0: - pusch_cfg = get_pusch_config_f0_0_c_rnti( - cell_cfg, &ue_cell_cfg, bwp_ul_cmn, pusch_td_cfg, nof_harq_ack_bits, is_csi_report_slot); - break; - case dci_ul_rnti_config_type::c_rnti_f0_1: - pusch_cfg = get_pusch_config_f0_1_c_rnti(ue_cell_cfg, - pusch_td_cfg, - ue_cc->channel_state_manager().get_nof_ul_layers(), - nof_harq_ack_bits, - is_csi_report_slot); - break; - default: - report_fatal_error("Unsupported PDCCH DCI UL format"); - } + // Allocate PDCCH position. + pdcch_ul_information* pdcch = get_pdcch_sched(grant.cell_index) + .alloc_ul_pdcch_ue(pdcch_alloc, u.crnti, ue_cell_cfg, ss_cfg.get_id(), aggr_lvl); + if (pdcch == nullptr) { + logger.info("ue={} rnti={}: Failed to allocate PUSCH. Cause: No space in PDCCH.", u.ue_index, u.crnti); + return alloc_outcome::skip_ue; + } - // Compute MCS and TBS for this transmission. - std::optional mcs_tbs_info; - // If it's a new Tx, compute the MCS and TBS from SNR, payload size, and available RBs. - if (h_ul.empty()) { - bool contains_dc = dc_offset_helper::is_contained( - cell_cfg.expert_cfg.ue.initial_ul_dc_offset, cell_cfg.nof_ul_prbs, adjusted_crbs); + const unsigned nof_harq_ack_bits = + get_uci_alloc(grant.cell_index).get_scheduled_pdsch_counter_in_ue_uci(pusch_alloc, u.crnti); + + const bool is_csi_report_slot = + csi_helper::is_csi_reporting_slot(u.get_pcell().cfg().cfg_dedicated(), pusch_alloc.slot); + + // Fetch PUSCH parameters based on type of transmission. + pusch_config_params pusch_cfg; + switch (dci_type) { + case dci_ul_rnti_config_type::tc_rnti_f0_0: + pusch_cfg = get_pusch_config_f0_0_tc_rnti(cell_cfg, pusch_td_cfg); + break; + case dci_ul_rnti_config_type::c_rnti_f0_0: + pusch_cfg = get_pusch_config_f0_0_c_rnti( + cell_cfg, &ue_cell_cfg, bwp_ul_cmn, pusch_td_cfg, nof_harq_ack_bits, is_csi_report_slot); + break; + case dci_ul_rnti_config_type::c_rnti_f0_1: + pusch_cfg = get_pusch_config_f0_1_c_rnti(ue_cell_cfg, + pusch_td_cfg, + ue_cc->channel_state_manager().get_nof_ul_layers(), + nof_harq_ack_bits, + is_csi_report_slot); + break; + default: + report_fatal_error("Unsupported PDCCH DCI UL format"); + } - mcs_tbs_info = compute_ul_mcs_tbs(pusch_cfg, &ue_cell_cfg, grant.mcs, adjusted_crbs.length(), contains_dc); - } - // If it's a reTx, fetch the MCS and TBS from the previous transmission. - else { - mcs_tbs_info.emplace(sch_mcs_tbs{.mcs = h_ul.last_tx_params().mcs, .tbs = h_ul.last_tx_params().tbs_bytes}); - } + // Compute MCS and TBS for this transmission. + std::optional mcs_tbs_info; + // If it's a new Tx, compute the MCS and TBS from SNR, payload size, and available RBs. + if (not is_retx) { + bool contains_dc = + dc_offset_helper::is_contained(cell_cfg.expert_cfg.ue.initial_ul_dc_offset, cell_cfg.nof_ul_prbs, crbs); - // If there is not MCS-TBS info, it means no MCS exists such that the effective code rate is <= 0.95. - if (not mcs_tbs_info.has_value()) { - logger.warning( - "ue={} rnti={}: Failed to allocate PUSCH. Cause: no MCS such that code rate <= 0.95 with this " - "configuration: mcs={} crbs={} symbols={} nof_oh={} tb-sc-field={} layers={} pi2bpsk={} " - "harq_bits={} csi1_bits={} csi2_bits={} mcs_table_idx={} dmrs_A_pos={} is_dmrs_type2={} dmrs_add_pos_idx={}", - u.ue_index, - u.crnti, - grant.mcs.to_uint(), - adjusted_crbs, - pusch_cfg.symbols, - pusch_cfg.nof_oh_prb, - pusch_cfg.tb_scaling_field, - pusch_cfg.nof_layers, - pusch_cfg.tp_pi2bpsk_present ? "yes" : "no", - pusch_cfg.nof_harq_ack_bits, - pusch_cfg.nof_csi_part1_bits, - pusch_cfg.max_nof_csi_part2_bits, - static_cast(pusch_cfg.mcs_table), - ue_cell_cfg.cell_cfg_common.dmrs_typeA_pos == dmrs_typeA_position::pos2 ? "pos2" : "pos3", - ue_cell_cfg.cfg_dedicated().ul_config->init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs.value().is_dmrs_type2 - ? "yes" - : "no", - static_cast(ue_cell_cfg.cfg_dedicated() - .ul_config->init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs.value() - .additional_positions)); - get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); - return alloc_outcome::invalid_params; - } + mcs_tbs_info = compute_ul_mcs_tbs(pusch_cfg, &ue_cell_cfg, mcs_prbs.mcs, crbs.length(), contains_dc); + } + // If it's a reTx, fetch the MCS and TBS from the previous transmission. + else { + mcs_tbs_info.emplace(sch_mcs_tbs{.mcs = h_ul.last_tx_params().mcs, .tbs = h_ul.last_tx_params().tbs_bytes}); + } - // Mark resources as occupied in the ResourceGrid. - pusch_alloc.ul_res_grid.fill(grant_info{scs, pusch_td_cfg.symbols, adjusted_crbs}); - - // Remove NTN offset when adding slot to HARQ process. - slot_point harq_slot = pusch_alloc.slot - ue_cell_cfg.cell_cfg_common.ntn_cs_koffset; - // Allocate UE UL HARQ. - bool is_new_data = h_ul.empty(); - if (is_new_data) { - // It is a new tx. - h_ul.new_tx(harq_slot, expert_cfg.max_nof_harq_retxs); - } else { - // It is a retx. - h_ul.new_retx(harq_slot); - } + // If there is not MCS-TBS info, it means no MCS exists such that the effective code rate is <= 0.95. + if (not mcs_tbs_info.has_value()) { + logger.warning( + "ue={} rnti={}: Failed to allocate PUSCH. Cause: no MCS such that code rate <= 0.95 with this " + "configuration: mcs={} crbs={} symbols={} nof_oh={} tb-sc-field={} layers={} pi2bpsk={} " + "harq_bits={} csi1_bits={} csi2_bits={} mcs_table_idx={} dmrs_A_pos={} is_dmrs_type2={} dmrs_add_pos_idx={}", + u.ue_index, + u.crnti, + mcs_tbs_info.value().mcs.to_uint(), + crbs, + pusch_cfg.symbols, + pusch_cfg.nof_oh_prb, + pusch_cfg.tb_scaling_field, + pusch_cfg.nof_layers, + pusch_cfg.tp_pi2bpsk_present ? "yes" : "no", + pusch_cfg.nof_harq_ack_bits, + pusch_cfg.nof_csi_part1_bits, + pusch_cfg.max_nof_csi_part2_bits, + static_cast(pusch_cfg.mcs_table), + ue_cell_cfg.cell_cfg_common.dmrs_typeA_pos == dmrs_typeA_position::pos2 ? "pos2" : "pos3", + ue_cell_cfg.cfg_dedicated().ul_config->init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs.value().is_dmrs_type2 + ? "yes" + : "no", + static_cast(ue_cell_cfg.cfg_dedicated() + .ul_config->init_ul_bwp.pusch_cfg->pusch_mapping_type_a_dmrs.value() + .additional_positions)); + get_pdcch_sched(grant.cell_index).cancel_last_pdcch(pdcch_alloc); + return alloc_outcome::invalid_params; + } + + // Mark resources as occupied in the ResourceGrid. + pusch_alloc.ul_res_grid.fill(grant_info{scs, pusch_td_cfg.symbols, crbs}); - // Compute total DAI. See TS 38.213, 9.1.3.2. - // Total DAI provides total number of transmissions at the end of each interval (slot in a cell). Values range from 1 - // to 4. - // If a UE is not provided PDSCH-CodeBlockGroupTransmission and the UE is scheduled for a PUSCH transmission by - // DCI format 0_1 with DAI field value V_T_DAI_UL = 4 and the UE has not received any PDCCH within the monitoring - // occasions for PDCCH with DCI format 1_0 or DCI format 1_1 for scheduling PDSCH receptions or SPS PDSCH - // release on any serving cell c and the UE does not have HARQ-ACK information in response to a SPS PDSCH - // reception to multiplex in the PUSCH, the UE does not multiplex HARQ-ACK information in the PUSCH transmission. - // NOTE: DAI is encoded as per left most column in Table 9.1.3-2 of TS 38.213. - unsigned dai = 3; - if (dci_type == dci_ul_rnti_config_type::c_rnti_f0_1) { - unsigned total_harq_ack_in_uci = nof_harq_ack_bits; - if (total_harq_ack_in_uci != 0) { - // See TS 38.213, Table 9.1.3-2. dai value below maps to the leftmost column in the table. - dai = ((total_harq_ack_in_uci - 1) % 4); + // Remove NTN offset when adding slot to HARQ process. + slot_point harq_slot = pusch_alloc.slot - ue_cell_cfg.cell_cfg_common.ntn_cs_koffset; + // Allocate UE UL HARQ. + bool is_new_data = not is_retx; + if (is_new_data) { + // It is a new tx. + h_ul.new_tx(harq_slot, expert_cfg.max_nof_harq_retxs); + } else { + // It is a retx. + h_ul.new_retx(harq_slot); } - } - // Fill UL PDCCH DCI. - uint8_t rv = ue_cc->get_pusch_rv(h_ul); - switch (dci_type) { - case dci_ul_rnti_config_type::tc_rnti_f0_0: - build_dci_f0_0_tc_rnti(pdcch->dci, - *ue_cell_cfg.bwp(to_bwp_id(0)).dl_common, - ue_cell_cfg.bwp(ue_cc->active_bwp_id()).ul_common->generic_params, - adjusted_crbs, - grant.time_res_index, - mcs_tbs_info.value().mcs, - rv, - h_ul); - break; - case dci_ul_rnti_config_type::c_rnti_f0_0: - build_dci_f0_0_c_rnti(pdcch->dci, - ue_cell_cfg.search_space(grant.ss_id), - cell_cfg.ul_cfg_common.init_ul_bwp, - adjusted_crbs, - grant.time_res_index, - mcs_tbs_info.value().mcs, - rv, - h_ul); - break; - case dci_ul_rnti_config_type::c_rnti_f0_1: - build_dci_f0_1_c_rnti(pdcch->dci, - ue_cell_cfg, - grant.ss_id, - adjusted_crbs, - grant.time_res_index, - mcs_tbs_info.value().mcs, - rv, - h_ul, - dai, - ue_cc->channel_state_manager().get_nof_ul_layers()); - break; - default: - report_fatal_error("Unsupported PDCCH UL DCI format"); - } + // Compute total DAI. See TS 38.213, 9.1.3.2. + // Total DAI provides total number of transmissions at the end of each interval (slot in a cell). Values range from + // 1 to 4. If a UE is not provided PDSCH-CodeBlockGroupTransmission and the UE is scheduled for a PUSCH transmission + // by DCI format 0_1 with DAI field value V_T_DAI_UL = 4 and the UE has not received any PDCCH within the monitoring + // occasions for PDCCH with DCI format 1_0 or DCI format 1_1 for scheduling PDSCH receptions or SPS PDSCH + // release on any serving cell c and the UE does not have HARQ-ACK information in response to a SPS PDSCH + // reception to multiplex in the PUSCH, the UE does not multiplex HARQ-ACK information in the PUSCH transmission. + // NOTE: DAI is encoded as per left most column in Table 9.1.3-2 of TS 38.213. + unsigned dai = 3; + if (dci_type == dci_ul_rnti_config_type::c_rnti_f0_1) { + unsigned total_harq_ack_in_uci = nof_harq_ack_bits; + if (total_harq_ack_in_uci != 0) { + // See TS 38.213, Table 9.1.3-2. dai value below maps to the leftmost column in the table. + dai = ((total_harq_ack_in_uci - 1) % 4); + } + } - // Fill PUSCH. - ul_sched_info& msg = pusch_alloc.result.ul.puschs.emplace_back(); - msg.context.ue_index = u.ue_index; - msg.context.ss_id = ss_cfg.get_id(); - msg.context.k2 = pusch_td_cfg.k2; - msg.context.nof_retxs = h_ul.tb().nof_retxs; - if (is_new_data and ue_cc->link_adaptation_controller().is_ul_olla_enabled()) { - msg.context.olla_offset = ue_cc->link_adaptation_controller().ul_snr_offset_db(); - } - switch (pdcch->dci.type) { - case dci_ul_rnti_config_type::tc_rnti_f0_0: - build_pusch_f0_0_tc_rnti(msg.pusch_cfg, - pusch_cfg, - mcs_tbs_info.value().tbs, - u.crnti, - cell_cfg, - pdcch->dci.tc_rnti_f0_0, - adjusted_crbs, - is_new_data); - break; - case dci_ul_rnti_config_type::c_rnti_f0_0: - build_pusch_f0_0_c_rnti(msg.pusch_cfg, - u.crnti, - pusch_cfg, - mcs_tbs_info.value().tbs, - cell_cfg, - bwp_ul_cmn, - pdcch->dci.c_rnti_f0_0, - adjusted_crbs, - is_new_data); - break; - case dci_ul_rnti_config_type::c_rnti_f0_1: - build_pusch_f0_1_c_rnti(msg.pusch_cfg, - u.crnti, - pusch_cfg, - mcs_tbs_info.value(), + // Fill UL PDCCH DCI. + uint8_t rv = ue_cc->get_pusch_rv(h_ul); + switch (dci_type) { + case dci_ul_rnti_config_type::tc_rnti_f0_0: + build_dci_f0_0_tc_rnti(pdcch->dci, + *ue_cell_cfg.bwp(to_bwp_id(0)).dl_common, + ue_cell_cfg.bwp(ue_cc->active_bwp_id()).ul_common->generic_params, + crbs, + param_candidate.pusch_td_res_index(), + mcs_tbs_info.value().mcs, + rv, + h_ul); + break; + case dci_ul_rnti_config_type::c_rnti_f0_0: + build_dci_f0_0_c_rnti(pdcch->dci, + ss_info, + cell_cfg.ul_cfg_common.init_ul_bwp, + crbs, + param_candidate.pusch_td_res_index(), + mcs_tbs_info.value().mcs, + rv, + h_ul); + break; + case dci_ul_rnti_config_type::c_rnti_f0_1: + build_dci_f0_1_c_rnti(pdcch->dci, ue_cell_cfg, ss_cfg.get_id(), - pdcch->dci.c_rnti_f0_1, - adjusted_crbs, - h_ul); - break; - default: - report_fatal_error("Unsupported PDCCH UL DCI format"); - } + crbs, + param_candidate.pusch_td_res_index(), + mcs_tbs_info.value().mcs, + rv, + h_ul, + dai, + ue_cc->channel_state_manager().get_nof_ul_layers()); + break; + default: + report_fatal_error("Unsupported PDCCH UL DCI format"); + } - // Check if there is any UCI grant allocated on the PUCCH that can be moved to the PUSCH. - get_uci_alloc(grant.cell_index).multiplex_uci_on_pusch(msg, pusch_alloc, ue_cell_cfg, u.crnti); + // Fill PUSCH. + ul_sched_info& msg = pusch_alloc.result.ul.puschs.emplace_back(); + msg.context.ue_index = u.ue_index; + msg.context.ss_id = ss_cfg.get_id(); + msg.context.k2 = final_k2; + msg.context.nof_retxs = h_ul.tb().nof_retxs; + if (is_new_data and ue_cc->link_adaptation_controller().is_ul_olla_enabled()) { + msg.context.olla_offset = ue_cc->link_adaptation_controller().ul_snr_offset_db(); + } + switch (pdcch->dci.type) { + case dci_ul_rnti_config_type::tc_rnti_f0_0: + build_pusch_f0_0_tc_rnti(msg.pusch_cfg, + pusch_cfg, + mcs_tbs_info.value().tbs, + u.crnti, + cell_cfg, + pdcch->dci.tc_rnti_f0_0, + crbs, + is_new_data); + break; + case dci_ul_rnti_config_type::c_rnti_f0_0: + build_pusch_f0_0_c_rnti(msg.pusch_cfg, + u.crnti, + pusch_cfg, + mcs_tbs_info.value().tbs, + cell_cfg, + bwp_ul_cmn, + pdcch->dci.c_rnti_f0_0, + crbs, + is_new_data); + break; + case dci_ul_rnti_config_type::c_rnti_f0_1: + build_pusch_f0_1_c_rnti(msg.pusch_cfg, + u.crnti, + pusch_cfg, + mcs_tbs_info.value(), + ue_cell_cfg, + ss_cfg.get_id(), + pdcch->dci.c_rnti_f0_1, + crbs, + h_ul); + break; + default: + report_fatal_error("Unsupported PDCCH UL DCI format"); + } - // Save set PDCCH and PUSCH PDU parameters in HARQ process. - ul_harq_sched_context pusch_sched_ctx; - pusch_sched_ctx.dci_cfg_type = pdcch->dci.type; - if (is_new_data) { - pusch_sched_ctx.olla_mcs = ue_cc->link_adaptation_controller().calculate_ul_mcs(msg.pusch_cfg.mcs_table); - } - h_ul.save_alloc_params(pusch_sched_ctx, msg.pusch_cfg); + // Check if there is any UCI grant allocated on the PUCCH that can be moved to the PUSCH. + get_uci_alloc(grant.cell_index).multiplex_uci_on_pusch(msg, pusch_alloc, ue_cell_cfg, u.crnti); + + // Save set PDCCH and PUSCH PDU parameters in HARQ process. + ul_harq_sched_context pusch_sched_ctx; + pusch_sched_ctx.dci_cfg_type = pdcch->dci.type; + if (is_new_data) { + pusch_sched_ctx.olla_mcs = ue_cc->link_adaptation_controller().calculate_ul_mcs(msg.pusch_cfg.mcs_table); + ue_cc->last_pusch_allocated_slot = pusch_alloc.slot; + } + h_ul.save_alloc_params(pusch_sched_ctx, msg.pusch_cfg); + + // In case there is a SR pending. Reset it. + u.reset_sr_indication(); - // In case there is a SR pending. Reset it. - u.reset_sr_indication(); + return alloc_outcome::success; + } - return alloc_outcome::success; + // No candidates for PUSCH allocation. + return alloc_outcome::invalid_params; } diff --git a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h index 2006b0e6e4..1128a59189 100644 --- a/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h +++ b/lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h @@ -48,7 +48,7 @@ class ue_cell_grid_allocator : public ue_pdsch_allocator, public ue_pusch_alloca size_t nof_cells() const { return cells.size(); } - void slot_indication(); + void slot_indication(slot_point sl); alloc_outcome allocate_dl_grant(const ue_pdsch_grant& grant) override; @@ -81,6 +81,11 @@ class ue_cell_grid_allocator : public ue_pdsch_allocator, public ue_pusch_alloca slotted_array cells; + // List of slots at which there is no PDSCH space for further allocations. + static_vector slots_with_no_pdsch_space; + // List of slots at which there is no PUSCH space for further allocations. + static_vector slots_with_no_pusch_space; + // Number of allocation attempts for DL and UL in the given slot. unsigned dl_attempts_count = 0, ul_attempts_count = 0; }; diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.cpp b/lib/scheduler/ue_scheduling/ue_event_manager.cpp index a10b3c630b..eeb993f697 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.cpp +++ b/lib/scheduler/ue_scheduling/ue_event_manager.cpp @@ -97,7 +97,7 @@ class ue_event_manager::ue_dl_buffer_occupancy_manager final : public scheduler_ } // Log event. - parent.ev_logger.enqueue(dl_bo); + parent.du_cells[u.get_pcell().cell_index].ev_logger->enqueue(dl_bo); // Report event. parent.metrics_handler.handle_dl_buffer_state_indication(dl_bo); @@ -115,15 +115,12 @@ class ue_event_manager::ue_dl_buffer_occupancy_manager final : public scheduler_ // Initial capacity for the common and cell event lists, in order to avoid std::vector reallocations. We use the max // nof UEs as a conservative estimate of the expected number of events per slot. -static const size_t INITIAL_COMMON_EVENT_LIST_SIZE = MAX_NOF_DU_UES; -static const size_t INITIAL_CELL_EVENT_LIST_SIZE = MAX_NOF_DU_UES; +static constexpr size_t INITIAL_COMMON_EVENT_LIST_SIZE = MAX_NOF_DU_UES; +static constexpr size_t INITIAL_CELL_EVENT_LIST_SIZE = MAX_NOF_DU_UES; -ue_event_manager::ue_event_manager(ue_repository& ue_db_, - scheduler_metrics_handler& metrics_handler_, - scheduler_event_logger& ev_logger_) : +ue_event_manager::ue_event_manager(ue_repository& ue_db_, scheduler_metrics_handler& metrics_handler_) : ue_db(ue_db_), metrics_handler(metrics_handler_), - ev_logger(ev_logger_), logger(srslog::fetch_basic_logger("SCHED")), dl_bo_mng(std::make_unique(*this)) { @@ -159,12 +156,12 @@ void ue_event_manager::handle_ue_creation(ue_config_update_event ev) // Update UCI scheduler with new UE UCI resources. const auto& added_ue = ue_db[ueidx]; - for (unsigned i = 0; i != added_ue.nof_cells(); ++i) { + for (unsigned i = 0, e = added_ue.nof_cells(); i != e; ++i) { du_cells[pcell_index].uci_sched->add_ue(added_ue.get_cell(to_ue_cell_index(i)).cfg()); } // Log Event. - ev_logger.enqueue(scheduler_event_logger::ue_creation_event{ueidx, rnti, pcell_index}); + du_cells[pcell_index].ev_logger->enqueue(scheduler_event_logger::ue_creation_event{ueidx, rnti, pcell_index}); }); } @@ -181,7 +178,7 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) auto& u = ue_db[ue_idx]; // Update UE UCI resources in UCI scheduler. - for (unsigned i = 0; i != u.nof_cells(); ++i) { + for (unsigned i = 0, e = u.nof_cells(); i != e; ++i) { auto& ue_cc = u.get_cell(to_ue_cell_index(i)); if (not ev.next_config().contains(ue_cc.cell_index)) { // UE carrier is being removed. @@ -191,9 +188,9 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) du_cells[ue_cc.cell_index].uci_sched->reconf_ue(ev.next_config().ue_cell_cfg(ue_cc.cell_index), ue_cc.cfg()); } } - for (unsigned i = 0; i != ev.next_config().nof_cells(); ++i) { - auto& new_ue_cc_cfg = ev.next_config().ue_cell_cfg(to_ue_cell_index(i)); - auto* ue_cc = u.find_cell(new_ue_cc_cfg.cell_cfg_common.cell_index); + for (unsigned i = 0, e = ev.next_config().nof_cells(); i != e; ++i) { + const auto& new_ue_cc_cfg = ev.next_config().ue_cell_cfg(to_ue_cell_index(i)); + auto* ue_cc = u.find_cell(new_ue_cc_cfg.cell_cfg_common.cell_index); if (ue_cc == nullptr) { // New UE carrier is being added. du_cells[new_ue_cc_cfg.cell_cfg_common.cell_index].uci_sched->add_ue(new_ue_cc_cfg); @@ -204,7 +201,7 @@ void ue_event_manager::handle_ue_reconfiguration(ue_config_update_event ev) ue_db[ue_idx].handle_reconfiguration_request(ue_reconf_command{ev.next_config()}); // Log event. - ev_logger.enqueue(scheduler_event_logger::ue_reconf_event{ue_idx, ue_db[ue_idx].crnti}); + du_cells[u.get_pcell().cell_index].ev_logger->enqueue(scheduler_event_logger::ue_reconf_event{ue_idx, u.crnti}); }); } @@ -217,11 +214,12 @@ void ue_event_manager::handle_ue_deletion(ue_config_delete_event ev) logger.warning("Received request to delete ue={} that does not exist", ue_idx); return; } - const auto& u = ue_db[ue_idx]; - const rnti_t rnti = u.crnti; + const auto& u = ue_db[ue_idx]; + const rnti_t rnti = u.crnti; + du_cell_index_t pcell_idx = u.get_pcell().cell_index; // Update UCI scheduling by removing existing UE UCI resources. - for (unsigned i = 0; i != u.nof_cells(); ++i) { + for (unsigned i = 0, e = u.nof_cells(); i != e; ++i) { du_cells[u.get_cell(to_ue_cell_index(i)).cell_index].uci_sched->rem_ue(u.get_pcell().cfg()); } @@ -229,7 +227,7 @@ void ue_event_manager::handle_ue_deletion(ue_config_delete_event ev) ue_db.schedule_ue_rem(std::move(ev)); // Log UE removal event. - ev_logger.enqueue(sched_ue_delete_message{ue_idx, rnti}); + du_cells[pcell_idx].ev_logger->enqueue(sched_ue_delete_message{ue_idx, rnti}); }); } @@ -243,7 +241,8 @@ void ue_event_manager::handle_ue_config_applied(du_ue_index_t ue_idx) ue& u = ue_db[ue_idx]; // Log UE config applied event. - ev_logger.enqueue(scheduler_event_logger::ue_cfg_applied_event{ue_idx, u.crnti}); + du_cells[u.get_pcell().cell_index].ev_logger->enqueue( + scheduler_event_logger::ue_cfg_applied_event{ue_idx, u.crnti}); // Remove UE from fallback mode. u.get_pcell().set_fallback_state(false); @@ -259,24 +258,26 @@ void ue_event_manager::handle_ul_bsr_indication(const ul_bsr_indication_message& log_invalid_ue_index(bsr_ind.ue_index, "BSR"); return; } - auto& u = ue_db[bsr_ind.ue_index]; + auto& u = ue_db[bsr_ind.ue_index]; + du_cell_index_t pcell_idx = u.get_pcell().cell_index; + // Handle event. u.handle_bsr_indication(bsr_ind); if (u.get_pcell().is_in_fallback_mode()) { // Signal SRB fallback scheduler with the new SRB0/SRB1 buffer state. - du_cells[u.get_pcell().cell_index].fallback_sched->handle_ul_bsr_indication(bsr_ind.ue_index, bsr_ind); + du_cells[pcell_idx].fallback_sched->handle_ul_bsr_indication(bsr_ind.ue_index, bsr_ind); } // Log event. - if (ev_logger.enabled()) { + if (du_cells[pcell_idx].ev_logger->enabled()) { scheduler_event_logger::bsr_event event{}; event.ue_index = bsr_ind.ue_index; event.rnti = bsr_ind.crnti; event.type = bsr_ind.type; event.reported_lcgs = bsr_ind.reported_lcgs; event.tot_ul_pending_bytes = units::bytes{u.pending_ul_newtx_bytes()}; - ev_logger.enqueue(event); + du_cells[pcell_idx].ev_logger->enqueue(event); } // Notify metrics handler. @@ -301,7 +302,7 @@ void ue_event_manager::handle_ul_phr_indication(const ul_phr_indication_message& event.cell_index = cell_phr.serv_cell_id; event.ph = cell_phr.ph; event.p_cmax = cell_phr.p_cmax; - ev_logger.enqueue(event); + du_cells[cell_phr.serv_cell_id].ev_logger->enqueue(event); // Notify metrics handler. metrics_handler.handle_ul_phr_indication(phr_ind); @@ -315,7 +316,7 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) { srsran_assert(cell_exists(crc_ind.cell_index), "Invalid cell index"); - for (unsigned i = 0; i != crc_ind.crcs.size(); ++i) { + for (unsigned i = 0, e = crc_ind.crcs.size(); i != e; ++i) { cell_specific_events[crc_ind.cell_index].emplace( crc_ind.crcs[i].ue_index, [this, sl_rx = crc_ind.sl_rx, crc = crc_ind.crcs[i]](ue_cell& ue_cc) { @@ -331,7 +332,7 @@ void ue_event_manager::handle_crc_indication(const ul_crc_indication& crc_ind) } // Log event. - ev_logger.enqueue(scheduler_event_logger::crc_event{ + du_cells[ue_cc.cell_index].ev_logger->enqueue(scheduler_event_logger::crc_event{ crc.ue_index, crc.rnti, ue_cc.cell_index, sl_rx, crc.harq_id, crc.tb_crc_success, crc.ul_sinr_dB}); // Notify metrics handler. @@ -347,7 +348,7 @@ void ue_event_manager::handle_harq_ind(ue_cell& ue span harq_bits, std::optional pucch_snr) { - for (unsigned harq_idx = 0; harq_idx != harq_bits.size(); ++harq_idx) { + for (unsigned harq_idx = 0, harq_end_idx = harq_bits.size(); harq_idx != harq_end_idx; ++harq_idx) { // Update UE HARQ state with received HARQ-ACK. std::optional result = ue_cc.handle_dl_ack_info(uci_sl, harq_bits[harq_idx], harq_idx, pucch_snr); @@ -356,7 +357,7 @@ void ue_event_manager::handle_harq_ind(ue_cell& ue const units::bytes tbs{result->tb.tbs_bytes}; // Log Event. - ev_logger.enqueue(scheduler_event_logger::harq_ack_event{ + du_cells[ue_cc.cell_index].ev_logger->enqueue(scheduler_event_logger::harq_ack_event{ ue_cc.ue_index, ue_cc.rnti(), ue_cc.cell_index, uci_sl, result->h_id, harq_bits[harq_idx], tbs}); if (result->update == dl_harq_process::status_update::acked or result->update == dl_harq_process::status_update::nacked) { @@ -374,87 +375,85 @@ void ue_event_manager::handle_csi(ue_cell& ue_cc, const csi_report_data& csi_rep ue_cc.handle_csi_report(csi_rep); // Log event. - ev_logger.enqueue(scheduler_event_logger::csi_report_event{ue_cc.ue_index, ue_cc.rnti(), csi_rep}); + du_cells[ue_cc.cell_index].ev_logger->enqueue( + scheduler_event_logger::csi_report_event{ue_cc.ue_index, ue_cc.rnti(), csi_rep}); } void ue_event_manager::handle_uci_indication(const uci_indication& ind) { srsran_sanity_check(cell_exists(ind.cell_index), "Invalid cell index"); - for (unsigned i = 0; i != ind.ucis.size(); ++i) { + for (unsigned i = 0, e = ind.ucis.size(); i != e; ++i) { const uci_indication::uci_pdu& uci = ind.ucis[i]; cell_specific_events[ind.cell_index].emplace( uci.ue_index, [this, uci_sl = ind.slot_rx, uci_pdu = uci](ue_cell& ue_cc) { - if (variant_holds_alternative(uci_pdu.pdu)) { - const auto& pdu = variant_get(uci_pdu.pdu); - + if (const auto* pucch_f0f1 = std::get_if(&uci_pdu.pdu)) { // Process DL HARQ ACKs. - if (not pdu.harqs.empty()) { - handle_harq_ind(ue_cc, uci_sl, pdu.harqs, pdu.ul_sinr_dB); + if (not pucch_f0f1->harqs.empty()) { + handle_harq_ind(ue_cc, uci_sl, pucch_f0f1->harqs, pucch_f0f1->ul_sinr_dB); } // Process SRs. - if (pdu.sr_detected) { + if (pucch_f0f1->sr_detected) { // Handle SR indication. ue_db[ue_cc.ue_index].handle_sr_indication(); du_cells[ue_cc.cell_index].fallback_sched->handle_sr_indication(ue_cc.ue_index); // Log SR event. - ev_logger.enqueue(scheduler_event_logger::sr_event{ue_cc.ue_index, ue_cc.rnti()}); + du_cells[ue_cc.cell_index].ev_logger->enqueue( + scheduler_event_logger::sr_event{ue_cc.ue_index, ue_cc.rnti()}); } - const bool is_uci_valid = not pdu.harqs.empty() or pdu.sr_detected; + const bool is_uci_valid = not pucch_f0f1->harqs.empty() or pucch_f0f1->sr_detected; // Process Timing Advance Offset. - if (is_uci_valid and pdu.time_advance_offset.has_value() and pdu.ul_sinr_dB.has_value()) { + if (is_uci_valid and pucch_f0f1->time_advance_offset.has_value() and pucch_f0f1->ul_sinr_dB.has_value()) { ue_db[ue_cc.ue_index].handle_ul_n_ta_update_indication( - ue_cc.cell_index, pdu.ul_sinr_dB.value(), pdu.time_advance_offset.value()); + ue_cc.cell_index, pucch_f0f1->ul_sinr_dB.value(), pucch_f0f1->time_advance_offset.value()); } - - } else if (variant_holds_alternative(uci_pdu.pdu)) { - const auto& pdu = variant_get(uci_pdu.pdu); - + } else if (const auto* pusch_pdu = std::get_if(&uci_pdu.pdu)) { // Process DL HARQ ACKs. - if (not pdu.harqs.empty()) { - handle_harq_ind(ue_cc, uci_sl, pdu.harqs, std::nullopt); + if (not pusch_pdu->harqs.empty()) { + handle_harq_ind(ue_cc, uci_sl, pusch_pdu->harqs, std::nullopt); } // Process CSI. - if (pdu.csi.has_value()) { - handle_csi(ue_cc, *pdu.csi); + if (pusch_pdu->csi.has_value()) { + handle_csi(ue_cc, *pusch_pdu->csi); } - - } else if (variant_holds_alternative(uci_pdu.pdu)) { - const auto& pdu = variant_get(uci_pdu.pdu); - + } else if (const auto* pucch_f2f3f4 = + std::get_if(&uci_pdu.pdu)) { // Process DL HARQ ACKs. - if (not pdu.harqs.empty()) { - handle_harq_ind(ue_cc, uci_sl, pdu.harqs, pdu.ul_sinr_dB); + if (not pucch_f2f3f4->harqs.empty()) { + handle_harq_ind(ue_cc, uci_sl, pucch_f2f3f4->harqs, pucch_f2f3f4->ul_sinr_dB); } // Process SRs. const size_t sr_bit_position_with_1_sr_bit = 0; - if (not pdu.sr_info.empty() and pdu.sr_info.test(sr_bit_position_with_1_sr_bit)) { + if (not pucch_f2f3f4->sr_info.empty() and pucch_f2f3f4->sr_info.test(sr_bit_position_with_1_sr_bit)) { // Handle SR indication. ue_db[ue_cc.ue_index].handle_sr_indication(); // Log SR event. - ev_logger.enqueue(scheduler_event_logger::sr_event{ue_cc.ue_index, ue_cc.rnti()}); + du_cells[ue_cc.cell_index].ev_logger->enqueue( + scheduler_event_logger::sr_event{ue_cc.ue_index, ue_cc.rnti()}); } // Process CSI. - if (pdu.csi.has_value()) { - handle_csi(ue_cc, *pdu.csi); + if (pucch_f2f3f4->csi.has_value()) { + handle_csi(ue_cc, *pucch_f2f3f4->csi); } - const bool is_uci_valid = not pdu.harqs.empty() or - (not pdu.sr_info.empty() and pdu.sr_info.test(sr_bit_position_with_1_sr_bit)) or - pdu.csi.has_value(); + const bool is_uci_valid = + not pucch_f2f3f4->harqs.empty() or + (not pucch_f2f3f4->sr_info.empty() and pucch_f2f3f4->sr_info.test(sr_bit_position_with_1_sr_bit)) or + pucch_f2f3f4->csi.has_value(); // Process Timing Advance Offset. - if (is_uci_valid and pdu.time_advance_offset.has_value() and pdu.ul_sinr_dB.has_value()) { + if (is_uci_valid and pucch_f2f3f4->time_advance_offset.has_value() and + pucch_f2f3f4->ul_sinr_dB.has_value()) { ue_db[ue_cc.ue_index].handle_ul_n_ta_update_indication( - ue_cc.cell_index, pdu.ul_sinr_dB.value(), pdu.time_advance_offset.value()); + ue_cc.cell_index, pucch_f2f3f4->ul_sinr_dB.value(), pucch_f2f3f4->time_advance_offset.value()); } } @@ -476,10 +475,16 @@ void ue_event_manager::handle_dl_mac_ce_indication(const dl_mac_ce_indication& c log_invalid_ue_index(ce.ue_index, "DL MAC CE"); return; } - ue_db[ce.ue_index].handle_dl_mac_ce_indication(ce); + auto& u = ue_db[ce.ue_index]; + u.handle_dl_mac_ce_indication(ce); + + // Notify SRB fallback scheduler upon receiving ConRes CE indication. + if (ce.ce_lcid == lcid_dl_sch_t::UE_CON_RES_ID) { + du_cells[ue_db[ce.ue_index].get_pcell().cell_index].fallback_sched->handle_conres_indication(ce.ue_index); + } // Log event. - ev_logger.enqueue(ce); + du_cells[u.get_pcell().cell_index].ev_logger->enqueue(ce); }); } @@ -598,7 +603,7 @@ void ue_event_manager::handle_error_indication(slot_point } // Log event. - ev_logger.enqueue(scheduler_event_logger::error_indication_event{sl_tx, event}); + du_cells[cell_index].ev_logger->enqueue(scheduler_event_logger::error_indication_event{sl_tx, event}); }); } @@ -675,7 +680,8 @@ void ue_event_manager::run(slot_point sl, du_cell_index_t cell_index) void ue_event_manager::add_cell(cell_resource_allocator& cell_res_grid, ue_fallback_scheduler& fallback_sched, - uci_scheduler_impl& uci_sched) + uci_scheduler_impl& uci_sched, + scheduler_event_logger& ev_logger) { const du_cell_index_t cell_index = cell_res_grid.cell_index(); srsran_assert(not cell_exists(cell_index), "Overwriting cell configurations not supported"); @@ -684,6 +690,7 @@ void ue_event_manager::add_cell(cell_resource_allocator& cell_res_grid, du_cells[cell_index].res_grid = &cell_res_grid; du_cells[cell_index].fallback_sched = &fallback_sched; du_cells[cell_index].uci_sched = &uci_sched; + du_cells[cell_index].ev_logger = &ev_logger; } bool ue_event_manager::cell_exists(du_cell_index_t cell_index) const diff --git a/lib/scheduler/ue_scheduling/ue_event_manager.h b/lib/scheduler/ue_scheduling/ue_event_manager.h index 1386cae43b..3e271c235a 100644 --- a/lib/scheduler/ue_scheduling/ue_event_manager.h +++ b/lib/scheduler/ue_scheduling/ue_event_manager.h @@ -45,12 +45,13 @@ class ue_event_manager final : public sched_ue_configuration_handler, public scheduler_dl_buffer_state_indication_handler { public: - ue_event_manager(ue_repository& ue_db, scheduler_metrics_handler& metrics_handler, scheduler_event_logger& ev_logger); + ue_event_manager(ue_repository& ue_db, scheduler_metrics_handler& metrics_handler); ~ue_event_manager(); void add_cell(cell_resource_allocator& cell_res_grid, ue_fallback_scheduler& fallback_sched, - uci_scheduler_impl& uci_sched); + uci_scheduler_impl& uci_sched, + scheduler_event_logger& ev_logger); /// UE Add/Mod/Remove interface. void handle_ue_creation(ue_config_update_event ev) override; @@ -118,7 +119,6 @@ class ue_event_manager final : public sched_ue_configuration_handler, ue_repository& ue_db; scheduler_metrics_handler& metrics_handler; - scheduler_event_logger& ev_logger; srslog::basic_logger& logger; /// List of added and configured cells. @@ -132,6 +132,8 @@ class ue_event_manager final : public sched_ue_configuration_handler, // Reference to the CSI and SR UCI scheduler. uci_scheduler_impl* uci_sched = nullptr; + + scheduler_event_logger* ev_logger = nullptr; }; std::array du_cells{}; diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp index a8b5246cd4..e53261365f 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.cpp @@ -91,6 +91,11 @@ void ue_fallback_scheduler::run_slot(cell_resource_allocator& res_alloc) return; } + // Schedule ConRes CE. + if (not schedule_dl_conres_new_tx(res_alloc)) { + return; + } + // Schedule SRB0 messages before SRB1, as we prioritize SRB0 over SRB1. if (not schedule_dl_new_tx_srb0(res_alloc)) { return; @@ -105,15 +110,25 @@ void ue_fallback_scheduler::handle_dl_buffer_state_indication_srb(du_ue_index_t slot_point sl, unsigned srb_buffer_bytes) { - auto ue_it = - std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_index, is_srb0](const srb_ue& ue) { - return ue.ue_index == ue_index and ue.is_srb0 == is_srb0; + if (not ues.contains(ue_index)) { + logger.error("ue_index={} not found in the scheduler", ue_index); + return; + } + + auto ue_it = std::find_if( + pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_index, is_srb0](const fallback_ue& ue) { + return ue.ue_index == ue_index and (not ue.is_srb0.has_value() or ue.is_srb0.value() == is_srb0); }); - srsran_sanity_check(ues.contains(ue_index), "UE doesn't exist in the scheduler"); ue& u = ues[ue_index]; if (ue_it != pending_dl_ues_new_tx.end()) { + // This case can happen when ConRes indication is received first and then receive Msg4 indication from upper layers. + if (not ue_it->is_srb0.has_value()) { + ue_it->is_srb0 = is_srb0; + ue_it->pending_srb1_buffer_bytes = srb_buffer_bytes; + return; + } // Remove the UE from the pending UE list when the Buffer Status Update is 0. if (not u.has_pending_dl_newtx_bytes(is_srb0 ? LCID_SRB0 : LCID_SRB1)) { pending_dl_ues_new_tx.erase(ue_it); @@ -129,8 +144,28 @@ void ue_fallback_scheduler::handle_dl_buffer_state_indication_srb(du_ue_index_t // The UE doesn't exist in the internal fallback scheduler list, add it. if (u.has_pending_dl_newtx_bytes(is_srb0 ? LCID_SRB0 : LCID_SRB1)) { - pending_dl_ues_new_tx.push_back({ue_index, is_srb0, srb_buffer_bytes}); + pending_dl_ues_new_tx.push_back({ue_index, is_srb0, u.is_conres_ce_pending(), srb_buffer_bytes}); + } +} + +void ue_fallback_scheduler::handle_conres_indication(du_ue_index_t ue_index) +{ + if (not ues.contains(ue_index)) { + logger.error("ue_index={} not found in the scheduler", ue_index); + return; + } + + auto ue_it = std::find_if(pending_dl_ues_new_tx.begin(), + pending_dl_ues_new_tx.end(), + [ue_index](const fallback_ue& ue) { return ue.ue_index == ue_index; }); + + // Entry for this UE already exists. + if (ue_it != pending_dl_ues_new_tx.end()) { + ue_it->is_conres_pending = true; + return; } + + pending_dl_ues_new_tx.push_back({ue_index, std::nullopt, true, 0}); } void ue_fallback_scheduler::handle_ul_bsr_indication(du_ue_index_t ue_index, const ul_bsr_indication_message& bsr_ind) @@ -180,7 +215,7 @@ bool ue_fallback_scheduler::schedule_dl_retx(cell_resource_allocator& res_alloc) if (h_dl->has_pending_retx()) { std::optional most_recent_tx_ack = get_most_recent_slot_tx(u.ue_index); - dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, next_ue_harq_retx.is_srb0, h_dl, most_recent_tx_ack); + dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, h_dl, most_recent_tx_ack, next_ue_harq_retx.is_srb0); // This is the case of the scheduler reaching the maximum number of sched attempts. if (outcome == dl_sched_outcome::stop_dl_scheduling) { return false; @@ -210,20 +245,65 @@ void ue_fallback_scheduler::schedule_ul_new_tx_and_retx(cell_resource_allocator& } } -bool ue_fallback_scheduler::schedule_dl_new_tx_srb0(cell_resource_allocator& res_alloc) +bool ue_fallback_scheduler::schedule_dl_conres_new_tx(cell_resource_allocator& res_alloc) { - for (auto next_ue = pending_dl_ues_new_tx.begin(); next_ue != pending_dl_ues_new_tx.end();) { - if (not next_ue->is_srb0) { + auto next_ue = pending_dl_ues_new_tx.begin(); + while (next_ue != pending_dl_ues_new_tx.end()) { + // The UE might have been deleted in the meantime, check if still exists. + if (not ues.contains(next_ue->ue_index)) { + next_ue = pending_dl_ues_new_tx.erase(next_ue); + continue; + } + + // Check whether UE has any pending ConRes CE to be scheduled. + if (not next_ue->is_conres_pending) { ++next_ue; continue; } + // Check whether Msg4 over SRB has been received or not. + // NOTE: UE with both pending ConRes + Msg4 is handled by \c schedule_dl_new_tx_srb0 and \c schedule_dl_new_tx_srb1. + if (next_ue->is_srb0.has_value()) { + ++next_ue; + continue; + } + + auto& u = ues[next_ue->ue_index]; + if (not u.is_conres_ce_pending()) { + ++next_ue; + continue; + } + + std::optional most_recent_tx_ack = get_most_recent_slot_tx(u.ue_index); + dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, nullptr, most_recent_tx_ack, next_ue->is_srb0); + if (outcome == dl_sched_outcome::success) { + next_ue = pending_dl_ues_new_tx.erase(next_ue); + } else if (outcome == dl_sched_outcome::next_ue) { + ++next_ue; + } else { + // This is the case the DL fallback scheduler has reached the maximum number of scheduling attempts and the fnc + // returns \ref stop_dl_scheduling. + return false; + } + } + return true; +} + +bool ue_fallback_scheduler::schedule_dl_new_tx_srb0(cell_resource_allocator& res_alloc) +{ + for (auto next_ue = pending_dl_ues_new_tx.begin(); next_ue != pending_dl_ues_new_tx.end();) { // The UE might have been deleted in the meantime, check if still exists. if (not ues.contains(next_ue->ue_index)) { next_ue = pending_dl_ues_new_tx.erase(next_ue); continue; } + // Check whether UE has any pending SRB0 data to be scheduled. + if (not next_ue->is_srb0.has_value() or not next_ue->is_srb0.value()) { + ++next_ue; + continue; + } + auto& u = ues[next_ue->ue_index]; std::optional most_recent_tx_ack = get_most_recent_slot_tx(u.ue_index); if (not u.has_pending_dl_newtx_bytes(LCID_SRB0)) { @@ -231,11 +311,14 @@ bool ue_fallback_scheduler::schedule_dl_new_tx_srb0(cell_resource_allocator& res continue; } - dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, true, nullptr, most_recent_tx_ack); + dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, nullptr, most_recent_tx_ack, next_ue->is_srb0); if (outcome == dl_sched_outcome::success) { next_ue = pending_dl_ues_new_tx.erase(next_ue); } else if (outcome == dl_sched_outcome::next_ue) { ++next_ue; + } else if (outcome == dl_sched_outcome::srb_pending) { + next_ue->is_conres_pending = false; + ++next_ue; } else { // This is the case the DL fallback scheduler has reached the maximum number of scheduling attempts and the fnc // returns \ref stop_dl_scheduling. @@ -248,17 +331,18 @@ bool ue_fallback_scheduler::schedule_dl_new_tx_srb0(cell_resource_allocator& res void ue_fallback_scheduler::schedule_dl_new_tx_srb1(cell_resource_allocator& res_alloc) { for (auto next_ue = pending_dl_ues_new_tx.begin(); next_ue != pending_dl_ues_new_tx.end();) { - if (next_ue->is_srb0) { - ++next_ue; - continue; - } - // The UE might have been deleted in the meantime, check if still exists. if (not ues.contains(next_ue->ue_index)) { next_ue = pending_dl_ues_new_tx.erase(next_ue); continue; } + // Check whether UE has any pending SRB1 data to be scheduled. + if (not next_ue->is_srb0.has_value() or next_ue->is_srb0.value()) { + ++next_ue; + continue; + } + auto& u = ues[next_ue->ue_index]; std::optional most_recent_tx_ack = get_most_recent_slot_tx(u.ue_index); // NOTE: Since SRB1 data can be segmented, it could happen that not all the SRB1 bytes are scheduled at once. The @@ -270,15 +354,15 @@ void ue_fallback_scheduler::schedule_dl_new_tx_srb1(cell_resource_allocator& res continue; } - dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, false, nullptr, most_recent_tx_ack); + dl_sched_outcome outcome = schedule_dl_srb(res_alloc, u, nullptr, most_recent_tx_ack, next_ue->is_srb0); if (outcome == dl_sched_outcome::success) { + next_ue->is_conres_pending = false; // Move to the next UE ONLY IF the UE has no more pending bytes for SRB1. This is to give priority to the same UE, // if there are still some SRB1 bytes left in the buffer. At the next iteration, the scheduler will try again with // the same scheduler, but starting from the next available slot. if (not has_pending_bytes_for_srb1(u.ue_index)) { ++next_ue; } - } else if (outcome == dl_sched_outcome::next_ue) { ++next_ue; } else { @@ -308,9 +392,9 @@ static slot_point get_next_srb_slot(const cell_configuration& cell_cfg, slot_poi ue_fallback_scheduler::dl_sched_outcome ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_alloc, ue& u, - bool is_srb0, dl_harq_process* h_dl_retx, - std::optional most_recent_tx_ack_slots) + std::optional most_recent_tx_ack_slots, + std::optional is_srb0) { const auto& bwp_cfg_common = cell_cfg.dl_cfg_common.init_dl_bwp; // Search valid PDSCH time domain resource. @@ -334,9 +418,9 @@ ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_a // \ref starting_slot is the slot from which the SRB0 starts scheduling this given UE. Assuming the UE was assigned // a PDSCH grant for SRB1 that was fragmented, we want to avoid allocating the second part of SRB1 in a PDSCH that - // is scheduled for an earlier slot than the PDSCH of the first part of the SRB1. NOTE: The \c most_recent_tx_slot - // is not necessarily more recent than sched_ref_slot; hence we need to check that - // most_recent_tx_ack_slots.value().most_recent_tx_slot > sched_ref_slot. + // is scheduled for an earlier slot than the PDSCH of the first part of the SRB1. + // NOTE: The \c most_recent_tx_slot is not necessarily more recent than sched_ref_slot; hence we need to check that + // most_recent_tx_ack_slots.value().most_recent_tx_slot >= sched_ref_slot. slot_point starting_slot = most_recent_tx_ack_slots.has_value() and most_recent_tx_ack_slots.value().most_recent_tx_slot > sched_ref_slot ? most_recent_tx_ack_slots.value().most_recent_tx_slot @@ -370,7 +454,7 @@ ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_a } const pdsch_time_domain_resource_allocation& pdsch_td_cfg = get_pdsch_td_cfg(time_res_idx.value()); - srsran_sanity_check(pdsch_td_cfg.k0 == 0, "Fallback scheduler only only supports k0=0"); + srsran_sanity_check(pdsch_td_cfg.k0 == 0, "Fallback scheduler only supports k0=0"); // We do not support multiplexing of PDSCH for SRB0 and SRB1 when in fallback with CSI-RS. const bool is_csi_rs_slot = next_slot == sched_ref_slot ? not pdsch_alloc.result.dl.csi_rs.empty() @@ -383,11 +467,22 @@ ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_a if (pdcch_alloc.result.dl.dl_pdcchs.full() or pdsch_alloc.result.dl.ue_grants.full()) { logger.debug("rnti={}: Failed to allocate PDSCH for {}. Cause: No space available in scheduler output list", u.crnti, - is_srb0 ? "SRB0" : "SRB1"); + not is_srb0.has_value() ? "ConRes CE" + : *is_srb0 ? "SRB0 PDU" + : "SRB1 PDU"); slots_with_no_pdxch_space[next_slot.to_uint() % FALLBACK_SCHED_RING_BUFFER_SIZE] = true; continue; } + // Check whether PDSCH for the UE already exists or not. + const auto* ue_pdsch_exists_it = + std::find_if(pdsch_alloc.result.dl.ue_grants.begin(), + pdsch_alloc.result.dl.ue_grants.end(), + [rnti = u.crnti](const dl_msg_alloc& pdsch) { return pdsch.pdsch_cfg.rnti == rnti; }); + if (ue_pdsch_exists_it != pdsch_alloc.result.dl.ue_grants.end()) { + return dl_sched_outcome::next_ue; + } + // As it is not possible to schedule a PDSCH whose related PUCCH falls in a slot that is the same as or older than // the most recent already scheduled ACK slot (for the same UE), whenever we detect this is the case we skip the // allocation in advance. @@ -399,22 +494,33 @@ ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_a most_recent_ack_slot = most_recent_tx_ack_slots.value().most_recent_ack_slot; } - sched_srb_results sched_res = - is_srb0 ? schedule_dl_srb0( - u, res_alloc, time_res_idx.value(), offset_to_sched_ref_slot, most_recent_ack_slot, h_dl_retx) - : schedule_dl_srb1(u, - sched_ref_slot, - res_alloc, - time_res_idx.value(), - offset_to_sched_ref_slot, - most_recent_ack_slot, - h_dl_retx); + sched_srb_results sched_res; + if (not is_srb0.has_value()) { + sched_res = schedule_dl_conres_ce( + u, res_alloc, time_res_idx.value(), offset_to_sched_ref_slot, most_recent_ack_slot, h_dl_retx); + } else { + if (is_srb0.value()) { + sched_res = schedule_dl_srb0( + u, res_alloc, time_res_idx.value(), offset_to_sched_ref_slot, most_recent_ack_slot, h_dl_retx); + } else { + sched_res = schedule_dl_srb1(u, + sched_ref_slot, + res_alloc, + time_res_idx.value(), + offset_to_sched_ref_slot, + most_recent_ack_slot, + h_dl_retx); + } + } const bool alloc_successful = sched_res.h_dl != nullptr; if (alloc_successful) { if (not is_retx) { // The srb1_payload_bytes is meaningful only for SRB1. - store_harq_tx(u.ue_index, sched_res.h_dl, is_srb0, sched_res.nof_srb1_scheduled_bytes); + store_harq_tx(u.ue_index, sched_res.h_dl, sched_res.nof_srb1_scheduled_bytes, is_srb0); + } + if (sched_res.is_srb_data_pending) { + return dl_sched_outcome::srb_pending; } return dl_sched_outcome::success; } @@ -426,12 +532,180 @@ ue_fallback_scheduler::schedule_dl_srb(cell_resource_allocator& res_a slot_point pdcch_slot = res_alloc[0].slot; logger.debug("rnti={}: Skipped {} allocation in slots:[{},{}). Cause: no PDCCH/PDSCH/PUCCH resources available", u.crnti, - is_srb0 ? "SRB0" : "SRB1", + not is_srb0.has_value() ? "ConRes CE" + : *is_srb0 ? "SRB0 PDU" + : "SRB1 PDU", pdcch_slot, pdcch_slot + max_dl_slots_ahead_sched + 1); return dl_sched_outcome::next_ue; } +ue_fallback_scheduler::sched_srb_results +ue_fallback_scheduler::schedule_dl_conres_ce(ue& u, + cell_resource_allocator& res_alloc, + unsigned pdsch_time_res, + unsigned slot_offset, + slot_point most_recent_ack_slot, + dl_harq_process* h_dl_retx) +{ + ue_cell& ue_pcell = u.get_pcell(); + const subcarrier_spacing scs = cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs; + const pdsch_time_domain_resource_allocation& pdsch_td_cfg = get_pdsch_td_cfg(pdsch_time_res); + + const bool is_retx = h_dl_retx != nullptr; + + // Search for empty HARQ. + dl_harq_process* h_dl = is_retx ? h_dl_retx : ue_pcell.harqs.find_empty_dl_harq(); + if (h_dl == nullptr) { + logger.warning("rnti={}: UE must have empty HARQs during ConRes CE allocation", u.crnti); + return {}; + } + + dci_dl_rnti_config_type dci_type = + is_retx ? h_dl_retx->last_alloc_params().dci_cfg_type : dci_dl_rnti_config_type::tc_rnti_f1_0; + srsran_assert(dci_type == dci_dl_rnti_config_type::tc_rnti_f1_0, + "Only DCI 1_0 with TC-RNTI is supported for ConRes CE scheduling"); + + pdsch_config_params pdsch_cfg = get_pdsch_config_f1_0_tc_rnti(cell_cfg, pdsch_td_cfg); + + cell_slot_resource_allocator& pdsch_alloc = res_alloc[slot_offset + pdsch_td_cfg.k0]; + auto cset0_crbs_lim = pdsch_helper::get_ra_crb_limits_common(cell_cfg.dl_cfg_common.init_dl_bwp, ss_cfg.get_id()); + prb_bitmap used_crbs = + pdsch_alloc.dl_res_grid.used_crbs(initial_active_dl_bwp.scs, cset0_crbs_lim, pdsch_cfg.symbols); + + sch_prbs_tbs prbs_tbs{}; + sch_mcs_index mcs_idx = 0; + if (is_retx) { + // Use the same MCS, nof PRBs and TBS as the last allocation. + mcs_idx = h_dl->last_alloc_params().tb[0]->mcs; + prbs_tbs.nof_prbs = h_dl->last_alloc_params().rbs.type1().length(); + prbs_tbs.tbs_bytes = h_dl->last_alloc_params().tb[0].value().tbs_bytes; + } else { + // Fetch the pending MAC Contention Resolution CEs bytes. + const unsigned pending_bytes = u.pending_conres_ce_bytes(); + + crb_interval unused_crbs = + rb_helper::find_next_empty_interval(used_crbs, cset0_crbs_lim.start(), cset0_crbs_lim.stop()); + + if (unused_crbs.empty()) { + logger.debug( + "rnti={}: Postponed ConRes CE scheduling for slot {}. Cause: No space in PDSCH.", u.crnti, pdsch_alloc.slot); + // If there is no free PRBs left on this slot for this UE, then this slot should be avoided by the other UEs + // too. + slots_with_no_pdxch_space[pdsch_alloc.slot.to_uint() % FALLBACK_SCHED_RING_BUFFER_SIZE] = true; + return {}; + } + + // Try to find least MCS to fit ConRes CE. + while (mcs_idx <= expert_cfg.max_msg4_mcs) { + // As per TS 38.214, clause 5.1.3.1, if the PDSCH is scheduled by a PDCCH with CRC scrambled by TC-RNTI, the UE + // use "Table 5.1.3.1-1: MCS index table 1" for MCS mapping. This is not stated explicitly, but can be inferred + // from the sentence "... the UE shall use I_MCS and Table 5.1.3.1-1 to determine the modulation order (Qm) and + // Target code rate (R) used in the physical downlink shared channel.". + + // At this point, xOverhead is not configured yet. As per TS 38.214, Clause 5.1.3.2, xOverhead is assumed to be + // 0. + const sch_mcs_description mcs_config = pdsch_mcs_get_config(pdsch_mcs_table::qam64, mcs_idx); + prbs_tbs = get_nof_prbs(prbs_calculator_sch_config{pending_bytes, + static_cast(pdsch_cfg.symbols.length()), + calculate_nof_dmrs_per_rb(pdsch_cfg.dmrs), + pdsch_cfg.nof_oh_prb, + mcs_config, + pdsch_cfg.nof_layers}); + if (unused_crbs.length() >= prbs_tbs.nof_prbs) { + break; + } + ++mcs_idx; + } + + if (prbs_tbs.tbs_bytes < pending_bytes) { + logger.debug( + "rnti={}: ConRes CE size ({}) exceeds TBS calculated ({})", pending_bytes, prbs_tbs.tbs_bytes, u.crnti); + return {}; + } + + if (mcs_idx > expert_cfg.max_msg4_mcs) { + logger.debug( + "rnti={}: Postponing ConRes CE allocation. Cause: MCS index chosen ({}) for ConRes CE exceeds maximum" + " allowed MCS index ({})", + u.crnti, + mcs_idx, + expert_cfg.max_msg4_mcs); + return {}; + } + } + + crb_interval ue_grant_crbs = rb_helper::find_empty_interval_of_length(used_crbs, prbs_tbs.nof_prbs, 0); + if (ue_grant_crbs.length() < prbs_tbs.nof_prbs) { + logger.debug("rnti={}: Postponed ConRes CE scheduling. Cause: Not enough PRBs ({} < {})", + u.crnti, + ue_grant_crbs.length(), + prbs_tbs.nof_prbs); + return {}; + } + + // Allocate PDCCH resources. + cell_slot_resource_allocator& pdcch_alloc = res_alloc[slot_offset]; + pdcch_dl_information* pdcch = + pdcch_sch.alloc_dl_pdcch_common(pdcch_alloc, u.crnti, ss_cfg.get_id(), aggregation_level::n4); + if (pdcch == nullptr) { + logger.debug( + "rnti={}: Postponed ConRes CE scheduling for slot={}. Cause: No space in PDCCH.", u.crnti, pdsch_alloc.slot); + // If there is no PDCCH space on this slot for this UE, then this slot should be avoided by the other UEs too. + slots_with_no_pdxch_space[pdsch_alloc.slot.to_uint() % FALLBACK_SCHED_RING_BUFFER_SIZE] = true; + return {}; + } + + // Allocate PUCCH resources. + unsigned k1 = dci_1_0_k1_values.front(); + // Minimum k1 value supported is 4. + std::optional pucch_res_indicator; + for (const auto k1_candidate : dci_1_0_k1_values) { + // Skip k1 values that would result in a PUCCH transmission in a slot that is older than the most recent ACK slot. + if (pdsch_alloc.slot + k1_candidate <= most_recent_ack_slot) { + continue; + } + pucch_res_indicator = + is_retx ? pucch_alloc.alloc_common_and_ded_harq_res( + res_alloc, u.crnti, u.get_pcell().cfg(), slot_offset + pdsch_td_cfg.k0, k1_candidate, *pdcch) + : pucch_alloc.alloc_common_pucch_harq_ack_ue( + res_alloc, u.crnti, slot_offset + pdsch_td_cfg.k0, k1_candidate, *pdcch); + if (pucch_res_indicator.has_value()) { + k1 = k1_candidate; + break; + } + } + if (not pucch_res_indicator.has_value()) { + logger.debug("rnti={}: Failed to allocate PDSCH for ConRes CE for slot={}. Cause: No space in PUCCH", + u.crnti, + pdsch_alloc.slot); + pdcch_sch.cancel_last_pdcch(pdcch_alloc); + return {}; + } + + // Mark resources as occupied in the Resource grid. + pdsch_alloc.dl_res_grid.fill(grant_info{scs, pdsch_td_cfg.symbols, ue_grant_crbs}); + + fill_dl_srb_grant(u, + pdsch_alloc.slot, + *h_dl, + *pdcch, + dci_type, + pdsch_alloc.result.dl.ue_grants.emplace_back(), + pucch_res_indicator.value(), + pdsch_time_res, + k1, + mcs_idx, + ue_grant_crbs, + pdsch_cfg, + prbs_tbs.tbs_bytes, + is_retx, + std::nullopt); + + // No need to pass the nof SRB scheduled bytes. + return sched_srb_results{.h_dl = h_dl}; +} + ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0(ue& u, cell_resource_allocator& res_alloc, unsigned pdsch_time_res, @@ -443,10 +717,10 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 const subcarrier_spacing scs = cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs; const pdsch_time_domain_resource_allocation& pdsch_td_cfg = get_pdsch_td_cfg(pdsch_time_res); - pdsch_config_params pdsch_cfg = get_pdsch_config_f1_0_tc_rnti(cell_cfg, pdsch_td_cfg); - const bool is_retx = h_dl_retx != nullptr; + ue_fallback_scheduler::sched_srb_results result{}; + // Search for empty HARQ. dl_harq_process* h_dl = is_retx ? h_dl_retx : ue_pcell.harqs.find_empty_dl_harq(); if (h_dl == nullptr) { @@ -454,6 +728,24 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 return {}; } + dci_dl_rnti_config_type dci_type = dci_dl_rnti_config_type::c_rnti_f1_0; + if (is_retx) { + dci_type = h_dl->last_alloc_params().dci_cfg_type; + } else if (u.is_conres_ce_pending()) { + dci_type = dci_dl_rnti_config_type::tc_rnti_f1_0; + } + srsran_assert(dci_type == dci_dl_rnti_config_type::tc_rnti_f1_0 or dci_type == dci_dl_rnti_config_type::c_rnti_f1_0, + "Only DCI 1_0 with TC-RNTI or C-RNTI is supported for SRB0 scheduling"); + + pdsch_config_params pdsch_cfg = dci_type == dci_dl_rnti_config_type::tc_rnti_f1_0 + ? get_pdsch_config_f1_0_tc_rnti(cell_cfg, pdsch_td_cfg) + : get_pdsch_config_f1_0_c_rnti(cell_cfg, nullptr, pdsch_td_cfg); + + // For DCI 1-0 scrambled with TC-RNTI, as per TS 38.213, Section 7.3.1.2.1, we should consider the size of CORESET#0 + // as the size for the BWP. + // For DCI 1-0 scrambled with C-RNTI, if the DCI is monitored in a common search space and CORESET#0 is configured + // for the cell, as per TS 38.213, Section 7.3.1.0, we should consider the size of CORESET#0 as the size for the + // BWP. cell_slot_resource_allocator& pdsch_alloc = res_alloc[slot_offset + pdsch_td_cfg.k0]; auto cset0_crbs_lim = pdsch_helper::get_ra_crb_limits_common(cell_cfg.dl_cfg_common.init_dl_bwp, ss_cfg.get_id()); prb_bitmap used_crbs = @@ -467,8 +759,16 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 prbs_tbs.nof_prbs = h_dl->last_alloc_params().rbs.type1().length(); prbs_tbs.tbs_bytes = h_dl->last_alloc_params().tb[0].value().tbs_bytes; } else { - // Fetch the pending bytes in the SRB0 buffer + pending MAC Contention Resolution CEs bytes. - const unsigned pending_bytes = u.pending_dl_srb0_or_srb1_newtx_bytes(true); + // Required PRBs and TBS information for scheduling only ConRes CE in case PDSCH does not have enough space to + // accommodate ConRes CE + SRB0. + sch_prbs_tbs only_conres_prbs_tbs{}; + // MCS index to use for scheduling only ConRes CE in case PDSCH does not have enough space to accommodate ConRes CE + // + SRB0. + std::optional only_conres_mcs_idx; + + // Fetch the pending bytes. + const unsigned only_conres_pending_bytes = u.pending_conres_ce_bytes(); + unsigned pending_bytes = u.pending_dl_newtx_bytes(LCID_SRB0) + only_conres_pending_bytes; crb_interval unused_crbs = rb_helper::find_next_empty_interval(used_crbs, cset0_crbs_lim.start(), cset0_crbs_lim.stop()); @@ -498,12 +798,40 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 pdsch_cfg.nof_oh_prb, mcs_config, pdsch_cfg.nof_layers}); + // If MCS index to use to schedule only ConRes CE is not set compute required PRBs and TBS information. + // NOTE: The \c only_conres_prbs_tbs and \c only_conres_mcs_idx is used in case PDSCH does not have enough space + // to accommodate ConRes CE + SRB0. + if (u.is_conres_ce_pending() and not only_conres_mcs_idx.has_value()) { + only_conres_prbs_tbs = + get_nof_prbs(prbs_calculator_sch_config{only_conres_pending_bytes, + static_cast(pdsch_cfg.symbols.length()), + calculate_nof_dmrs_per_rb(pdsch_cfg.dmrs), + pdsch_cfg.nof_oh_prb, + mcs_config, + pdsch_cfg.nof_layers}); + if (unused_crbs.length() >= only_conres_prbs_tbs.nof_prbs) { + only_conres_mcs_idx = mcs_idx; + } + } + if (unused_crbs.length() >= prbs_tbs.nof_prbs) { break; } ++mcs_idx; } + crb_interval ue_grant_crbs = rb_helper::find_empty_interval_of_length(used_crbs, prbs_tbs.nof_prbs, 0); + + // Check whether SRB0 can be scheduled along with ConRes CE or not. + if (u.is_conres_ce_pending() and (prbs_tbs.tbs_bytes < pending_bytes or mcs_idx > expert_cfg.max_msg4_mcs or + ue_grant_crbs.length() < prbs_tbs.nof_prbs)) { + // Schedule only ConRes CE. + result.is_srb_data_pending = true; + prbs_tbs = only_conres_prbs_tbs; + mcs_idx = only_conres_mcs_idx.value(); + pending_bytes = only_conres_pending_bytes; + } + if (prbs_tbs.tbs_bytes < pending_bytes) { logger.debug( "rnti={}: SRB0 PDU size ({}) exceeds TBS calculated ({})", pending_bytes, prbs_tbs.tbs_bytes, u.crnti); @@ -570,8 +898,10 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 // Mark resources as occupied in the ResourceGrid. pdsch_alloc.dl_res_grid.fill(grant_info{scs, pdsch_td_cfg.symbols, ue_grant_crbs}); - const bool is_srb0 = true; - dci_dl_rnti_config_type dci_type = dci_dl_rnti_config_type::tc_rnti_f1_0; + std::optional is_srb0; + if (not result.is_srb_data_pending) { + is_srb0 = true; + } fill_dl_srb_grant(u, pdsch_alloc.slot, *h_dl, @@ -589,7 +919,8 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb0 is_srb0); // No need to pass the nof SRB scheduled bytes with SRB0. - return sched_srb_results{.h_dl = h_dl}; + result.h_dl = h_dl; + return result; } ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb1(ue& u, @@ -707,6 +1038,22 @@ ue_fallback_scheduler::sched_srb_results ue_fallback_scheduler::schedule_dl_srb1 "ue={} rnti={}: Failed to allocate PDSCH. Cause: no MCS such that code rate <= 0.95.", u.ue_index, u.crnti); return {}; } + + // If ConRes CE is pending then ensure that there is enough RBs/TBS such that ConRes CE is not segmented. + if (u.is_conres_ce_pending()) { + const unsigned only_conres_ce_pending_bytes = u.pending_conres_ce_bytes(); + grant_prbs_mcs only_conres_mcs_prbs_estimate = + ue_pcell.required_dl_prbs(pdsch_td_cfg, only_conres_ce_pending_bytes, dci_dl_rnti_config_type::tc_rnti_f1_0); + if ((mcs_prbs_estimate.n_prbs < only_conres_mcs_prbs_estimate.n_prbs) or + (mcs_tbs->tbs < (only_conres_ce_pending_bytes + FIXED_SIZED_MAC_CE_SUBHEADER_SIZE))) { + logger.debug("ue={} rnti={}: Postponed SRB1 PDU scheduling for slot={}. Cause: Grant is too small to fit even " + "ConRes CE.", + u.ue_index, + u.crnti); + return {}; + } + } + final_mcs_tbs = mcs_tbs.value(); } @@ -785,7 +1132,7 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, const pdsch_config_params& pdsch_params, unsigned tbs_bytes, bool is_retx, - bool is_srb0) + std::optional is_srb0) { // Allocate DL HARQ. // NOTE: We do not multiplex the SRB1 PUCCH with existing PUCCH HARQs, thus both DAI and HARQ-ACK bit index are 0. @@ -806,7 +1153,6 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, // Fill DL PDCCH DCI. static const uint8_t msg4_rv = 0; - srsran_sanity_check(is_srb0 ? dci_type == dci_dl_rnti_config_type::tc_rnti_f1_0 : true, "Invalid DCI type for SRB0"); switch (dci_type) { case dci_dl_rnti_config_type::tc_rnti_f1_0: { build_dci_f1_0_tc_rnti(pdcch.dci, @@ -860,9 +1206,8 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, // Set MAC logical channels to schedule in this PDU. if (not is_retx) { - u.build_dl_fallback_transport_block_info( - msg.tb_list.emplace_back(), msg.pdsch_cfg.codewords[0].tb_size_bytes, is_srb0); - if (not is_srb0) { + u.build_dl_fallback_transport_block_info(msg.tb_list.emplace_back(), msg.pdsch_cfg.codewords[0].tb_size_bytes); + if (is_srb0.has_value() and not is_srb0.value()) { auto* msg_lcid_it = std::find_if(msg.tb_list.back().lc_chs_to_sched.begin(), msg.tb_list.back().lc_chs_to_sched.end(), @@ -874,8 +1219,10 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, } break; } - // This is only for SRB1. case dci_dl_rnti_config_type::c_rnti_f1_0: { + srsran_assert(is_srb0.has_value(), + "Invalid DCI type={} used for scheduling ConRes CE only", + dci_dl_rnti_config_rnti_type(dci_type)); build_pdsch_f1_0_c_rnti(msg.pdsch_cfg, pdsch_params, tbs_bytes, @@ -887,9 +1234,8 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, not is_retx); // Set MAC logical channels to schedule in this PDU. if (not is_retx) { - u.build_dl_transport_block_info( - msg.tb_list.emplace_back(), msg.pdsch_cfg.codewords[0].tb_size_bytes, lcid_t::LCID_SRB1); - if (not is_srb0) { + u.build_dl_fallback_transport_block_info(msg.tb_list.emplace_back(), msg.pdsch_cfg.codewords[0].tb_size_bytes); + if (is_srb0.has_value() and not is_srb0.value()) { auto* mcs_lcid_it = std::find_if(msg.tb_list.back().lc_chs_to_sched.begin(), msg.tb_list.back().lc_chs_to_sched.end(), @@ -902,7 +1248,7 @@ unsigned ue_fallback_scheduler::fill_dl_srb_grant(ue& u, break; } default: { - srsran_assert(false, "Invalid DCI type for SRB1"); + srsran_assert(false, "Invalid DCI type for SRB0 or SRB1"); } } @@ -921,7 +1267,7 @@ ue_fallback_scheduler::schedule_ul_ue(cell_resource_allocator& res_alloc, ue& u, // Fetch applicable PUSCH Time Domain resource index list. static_vector pusch_td_res_index_list = - get_pusch_td_res_indices_common(cell_cfg, pdcch_slot); + get_pusch_td_resource_indices(cell_cfg, pdcch_slot); bool is_retx = h_ul_retx != nullptr; @@ -1292,10 +1638,10 @@ ue_fallback_scheduler::get_most_recent_slot_tx(du_ue_index_t ue_idx) const return most_recent_tx_ack_slot; } -void ue_fallback_scheduler::store_harq_tx(du_ue_index_t ue_index, - dl_harq_process* h_dl, - bool is_srb0, - unsigned srb_payload_bytes) +void ue_fallback_scheduler::store_harq_tx(du_ue_index_t ue_index, + dl_harq_process* h_dl, + unsigned srb_payload_bytes, + std::optional is_srb0) { srsran_sanity_check(ongoing_ues_ack_retxs.end() == std::find_if(ongoing_ues_ack_retxs.begin(), @@ -1305,13 +1651,13 @@ void ue_fallback_scheduler::store_harq_tx(du_ue_index_t ue_index, }), "This UE and HARQ process were already in the list"); - ongoing_ues_ack_retxs.emplace_back(ue_index, h_dl, is_srb0, ues, srb_payload_bytes); + ongoing_ues_ack_retxs.emplace_back(ue_index, h_dl, ues, srb_payload_bytes, is_srb0); } unsigned ue_fallback_scheduler::get_srb1_pending_tot_bytes(du_ue_index_t ue_idx) const { - auto it = std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const srb_ue& ue) { - return ue.ue_index == ue_idx; + auto it = std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const fallback_ue& ue) { + return ue.ue_index == ue_idx and ue.is_srb0.has_value() and not ue.is_srb0.value(); }); if (it == pending_dl_ues_new_tx.end()) { return 0U; @@ -1333,9 +1679,10 @@ unsigned ue_fallback_scheduler::get_srb1_pending_tot_bytes(du_ue_index_t ue_idx) void ue_fallback_scheduler::update_srb1_buffer_state_after_alloc(du_ue_index_t ue_idx, unsigned allocated_bytes) { // Retrieve the UE from the list of UEs that need to be scheduled. - auto ue_it = std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const srb_ue& ue) { - return ue.ue_index == ue_idx; - }); + auto ue_it = + std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const fallback_ue& ue) { + return ue.ue_index == ue_idx and ue.is_srb0.has_value() and not ue.is_srb0.value(); + }); if (ue_it == pending_dl_ues_new_tx.end() or not ues.contains(ue_idx)) { return; } @@ -1352,9 +1699,10 @@ void ue_fallback_scheduler::update_srb1_buffer_after_rlc_bsu(du_ue_index_t ue_id unsigned buffer_status_report) { // Retrieve the UE from the list of UEs that need to be scheduled. - auto ue_it = std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const srb_ue& ue) { - return ue.ue_index == ue_idx; - }); + auto ue_it = + std::find_if(pending_dl_ues_new_tx.begin(), pending_dl_ues_new_tx.end(), [ue_idx](const fallback_ue& ue) { + return ue.ue_index == ue_idx and ue.is_srb0.has_value() and not ue.is_srb0.value(); + }); if (ue_it == pending_dl_ues_new_tx.end() or not ues.contains(ue_idx)) { return; } @@ -1364,7 +1712,8 @@ void ue_fallback_scheduler::update_srb1_buffer_after_rlc_bsu(du_ue_index_t ue_id // Remove the LCID-1 bytes that are already scheduled for future new transmissions, but yet to be transmitted, from // the RLC buffer state update received from upper layers. for (auto& ack_tracker : ongoing_ues_ack_retxs) { - if (ack_tracker.ue_index != ue_idx or ack_tracker.is_srb0) { + if (ack_tracker.ue_index != ue_idx or not ack_tracker.is_srb0.has_value() or + (ack_tracker.is_srb0.has_value() and ack_tracker.is_srb0.value())) { continue; } if (ack_tracker.h_dl->is_waiting_ack() and ack_tracker.h_dl->slot_tx() >= sl and @@ -1404,9 +1753,8 @@ void ue_fallback_scheduler::slot_indication(slot_point sl) // For SRB1, due to segmentation and to pre-allocation, the scheduler might not be able to estimate precisely when // the UE has received the SRB1 full buffer; we assume the UE has received the full SRB1 buffer data if UE exits // fallback mode. This is not needed for SRB0, which doesn't allow segmentation. - // NOTE: There is a drawback to this, which is false positive detection of CSI or SRs. This can stop the scheduling - // even before it has completed the SRB1 allocation. - if ((not ue_it->is_srb0) and (not ues[ue_it->ue_index].get_pcell().is_in_fallback_mode())) { + if ((ue_it->is_srb0.has_value() and not ue_it->is_srb0.value()) and + (not ues[ue_it->ue_index].get_pcell().is_in_fallback_mode())) { ue_it = pending_dl_ues_new_tx.erase(ue_it); continue; } @@ -1425,9 +1773,8 @@ void ue_fallback_scheduler::slot_indication(slot_point sl) // For SRB1, due to segmentation and to pre-allocation, the scheduler might not be able to estimate precisely when // the UE has received the SRB1 full buffer; we assume the UE has received the full SRB1 buffer data if UE exits // fallback mode. This is not needed for SRB0, which doesn't allow segmentation. - // NOTE: There is a drawback to this, which is false positive detection of CSI or SRs. This can stop the scheduling - // even before it has completed the SRB1 allocation. - if ((not it_ue_harq->is_srb0) and (not ues[it_ue_harq->ue_index].get_pcell().is_in_fallback_mode())) { + if ((it_ue_harq->is_srb0.has_value() and not it_ue_harq->is_srb0.value()) and + (not ues[it_ue_harq->ue_index].get_pcell().is_in_fallback_mode())) { const unsigned tb_index = 0U; it_ue_harq->h_dl->cancel_harq_retxs(tb_index); it_ue_harq = ongoing_ues_ack_retxs.erase(it_ue_harq); @@ -1454,7 +1801,7 @@ void ue_fallback_scheduler::slot_indication(slot_point sl) // transmissions. for (auto ue_it = pending_dl_ues_new_tx.begin(); ue_it != pending_dl_ues_new_tx.end();) { auto& ue = *ue_it; - if (ue.is_srb0) { + if (not ue.is_srb0.has_value() or ue.is_srb0.value()) { ++ue_it; continue; } diff --git a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h index a7a12a7d3c..e5bb7a255f 100644 --- a/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h +++ b/lib/scheduler/ue_scheduling/ue_fallback_scheduler.h @@ -51,6 +51,10 @@ class ue_fallback_scheduler void handle_dl_buffer_state_indication_srb(du_ue_index_t ue_index, bool is_srb0, slot_point sl, unsigned srb_buffer_bytes); + /// Handle Contention Resolution indication sent by the MAC. + /// \param[in] ue_index UE's DU Index for which Contention Resolution CE needs to be scheduled. + void handle_conres_indication(du_ue_index_t ue_index); + /// Handles UL Buffer State Report indication reported by UE. /// \param[in] ue_index UE's DU Index for which UL SRB1 message needs to be scheduled. /// \param[in] bsr_ind Buffer State Report indication message. @@ -72,10 +76,24 @@ class ue_fallback_scheduler /// Helper that schedules new UL SRB1 tx. void schedule_ul_new_tx_and_retx(cell_resource_allocator& res_alloc); + /// Helper that schedules new DL ConRes CE when Msg4 over SRB has not yet been received. Returns false if the DL + /// fallback schedule should exit, true otherwise. + /// \remark This function handles the following scenarios: + /// - Schedules ConRes CE only if ConRes indication is received from MAC but no buffer status update is received + /// for SRB0/SRB1. + bool schedule_dl_conres_new_tx(cell_resource_allocator& res_alloc); + /// Helper that schedules new DL SRB0 tx. Returns false if the DL fallback schedule should exit, true otherwise. + /// \remark This function handles the following scenarios: + /// - Schedules SRB0 only (not empty) if ConRes CE has already sent. + /// - Schedules SRB0 (not empty) + ConRes CE (if pending) if there is enough space in PDSCH resource grid. + /// - Schedules ConRes CE only (if pending) if there is not enough space in PDSCH resource grid to fit SRB0 (not + /// empty) + ConRes CE. bool schedule_dl_new_tx_srb0(cell_resource_allocator& res_alloc); /// Helper that schedules new DL SRB1 tx. + /// \remark This function handles the following scenarios: + /// - Schedules SRB1 (not empty) + ConRes CE (if pending). void schedule_dl_new_tx_srb1(cell_resource_allocator& res_alloc); /// Size of the ring buffer used to store the slots where the scheduler has found no PDCCH/PDSCH resources. @@ -91,15 +109,17 @@ class ue_fallback_scheduler slot_point most_recent_ack_slot; }; - enum class dl_sched_outcome { success, next_ue, stop_dl_scheduling }; + /// \remark srb_pending => Only ConRes was scheduled and Msg4 is yet to be scheduled. + enum class dl_sched_outcome { success, next_ue, stop_dl_scheduling, srb_pending }; - /// \brief Tries to schedule DL SRB0/SRB1 message for a UE, iterating over several PDSCH slots ahead of the current - /// reference slot. + /// \brief Tries to schedule DL SRB0/SRB1 message and/or ConRes CE only for a UE, iterating over several PDSCH slots + /// ahead of the current reference slot. + /// \remark If \c is_srb0 is empty, then only ConRes CE is scheduled. dl_sched_outcome schedule_dl_srb(cell_resource_allocator& res_alloc, ue& u, - bool is_srb0, dl_harq_process* h_dl_retx, - std::optional most_recent_tx_ack_slots); + std::optional most_recent_tx_ack_slots = std::nullopt, + std::optional is_srb0 = std::nullopt); enum class ul_srb_sched_outcome { next_ue, next_slot, stop_ul_scheduling }; @@ -109,12 +129,30 @@ class ue_fallback_scheduler struct sched_srb_results { dl_harq_process* h_dl = nullptr; + // This field represents whether SRB data was scheduled along with ConRes CE or only ConRes CE was scheduled. + bool is_srb_data_pending = false; // This is only meaningful for SRB1, and represents the number of LCID-1 bytes (excluding any overhead) that have // been scheduled for transmission. unsigned nof_srb1_scheduled_bytes = 0; }; + /// \brief Tries to schedule DL ConRes CE for a UE and for a specific PDSCH slot. + /// \remark This function handles the following scenarios: + /// - Schedules ConRes CE only if ConRes indication is received from MAC but no buffer status update is received + /// for SRB0/SRB1. + sched_srb_results schedule_dl_conres_ce(ue& u, + cell_resource_allocator& res_alloc, + unsigned pdsch_time_res, + unsigned slot_offset, + slot_point most_recent_ack_slot, + dl_harq_process* h_dl_retx); + /// \brief Tries to schedule DL SRB0 message for a UE and for a specific PDSCH slot. + /// \remark This function handles the following scenarios: + /// - Schedules SRB0 only (not empty) if ConRes CE has already sent. + /// - Schedules SRB0 (not empty) + ConRes CE (if pending) if there is enough space in PDSCH resource grid. + /// - Schedules ConRes CE only (if pending) if there is not enough space in PDSCH resource grid to fit SRB0 (not + /// empty) + ConRes CE. sched_srb_results schedule_dl_srb0(ue& u, cell_resource_allocator& res_alloc, unsigned pdsch_time_res, @@ -123,6 +161,8 @@ class ue_fallback_scheduler dl_harq_process* h_dl_retx); /// \brief Tries to schedule DL SRB1 message for a UE and for a specific PDSCH slot. + /// \remark This function handles the following scenarios: + /// - Schedules SRB1 (not empty) + ConRes CE (if pending). sched_srb_results schedule_dl_srb1(ue& u, slot_point sched_ref_slot, cell_resource_allocator& res_alloc, @@ -152,7 +192,7 @@ class ue_fallback_scheduler const pdsch_config_params& pdsch_params, unsigned tbs_bytes, bool is_retx, - bool is_srb0); + std::optional is_srb0 = std::nullopt); void fill_ul_srb_grant(ue& u, slot_point pdcch_slot, @@ -175,10 +215,14 @@ class ue_fallback_scheduler slot_point sl_tx, const dl_harq_process* h_dl_retx) const; - /// Defines the information that is needed to track the DL UEs that are pending for new SRB0/SRB1 TX. - struct srb_ue { + /// Defines the information that is needed to track the DL UEs that are pending for new SRB0/SRB1/ConRes CE TX. + struct fallback_ue { du_ue_index_t ue_index; - bool is_srb0; + // This field is empty if only ConRes indication is received from MAC and buffer status from upper layers for + // SRB0/SRB1 is not yet received. + std::optional is_srb0; + // This field indicated whether ConRes CE pending to be sent or not. + bool is_conres_pending; // Represents the number of LCID-1 bytes (excluding any overhead) that are pending for this UE. // This is only meaningful for SRB1 and gets updated every time an RLC buffer state update is received or when we // schedule a new PDSCH TX for this UE. @@ -186,7 +230,7 @@ class ue_fallback_scheduler }; /// List of UE's DU Indexes for which SRB0 and SRB1 messages needs to be scheduled. - std::vector pending_dl_ues_new_tx; + std::vector pending_dl_ues_new_tx; /// List of UE's DU Indexes that are pending for new TX or RE-TX. std::vector pending_ul_ues; @@ -195,11 +239,11 @@ class ue_fallback_scheduler class ack_and_retx_tracker { public: - explicit ack_and_retx_tracker(du_ue_index_t ue_idx, - dl_harq_process* h_dl_, - bool is_srb0_, - ue_repository& ues_, - unsigned srb_payload_bytes_) : + explicit ack_and_retx_tracker(du_ue_index_t ue_idx, + dl_harq_process* h_dl_, + ue_repository& ues_, + unsigned srb_payload_bytes_, + std::optional is_srb0_ = std::nullopt) : ue_index(ue_idx), is_srb0(is_srb0_), h_dl(h_dl_), srb1_payload_bytes(srb_payload_bytes_) { } @@ -210,18 +254,22 @@ class ue_fallback_scheduler return ue_index == ue_idx_ and h_dl == h_dl_; } - du_ue_index_t ue_index; - bool is_srb0; - dl_harq_process* h_dl; + du_ue_index_t ue_index; + // This field is empty if HARQ is used to schedule ConRes CE only. + std::optional is_srb0; + dl_harq_process* h_dl; // Represents the number of LCID-1 bytes (excluding any overhead) that have been allocated for this TX. // This is only meaningful for SRB1, unsigned srb1_payload_bytes = 0; }; - void store_harq_tx(du_ue_index_t ue_index, dl_harq_process* h_dl, bool is_srb0, unsigned srb_payload_bytes); + void store_harq_tx(du_ue_index_t ue_index, + dl_harq_process* h_dl, + unsigned srb_payload_bytes, + std::optional is_srb0 = std::nullopt); - // If there are any pending SRB0 or SRB1 transmissions for the UE, the function returns the most recent slot with - // PDSCH for SRB0/SRB1 and the most recent slot with the corresponding PUCCH. + // If there are any pending SRB0, SRB1 transmissions or ConRes CE for the UE, the function returns the most recent + // slot with PDSCH for SRB0/SRB1/ConRes CE allocation and the most recent slot with the corresponding PUCCH. std::optional get_most_recent_slot_tx(du_ue_index_t ue_idx) const; // Returns the total number of bytes pending for SRB1 for a given UE, including MAC CE and MAC subheaders. @@ -254,7 +302,7 @@ class ue_fallback_scheduler // TODO: Find proper values for these 2 parameters. // Set the max number of slots the scheduler can look ahead in the resource grid (with respect to the current slot) to // find PDSCH space for SRB0 or SRB1. - const unsigned max_dl_slots_ahead_sched = 10U; + const unsigned max_dl_slots_ahead_sched = 8U; // Set the max number of attempts the scheduler can do while running through the nested loops over the PDSCH time // allocation indices and the ahead slots for all UEs. This is to avoid excessive long iterations in case many UEs. // NOTE: max_dl_sched_attempts = (max_dl_slots_ahead_sched + 1) * max_pdsch_time_res guarantees that at 1 UE will be diff --git a/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h b/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h new file mode 100644 index 0000000000..ee1eb0e853 --- /dev/null +++ b/lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h @@ -0,0 +1,306 @@ +/* + * + * 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 "ue.h" + +namespace srsran { + +/// \brief This class assists with the search of PDSCH allocation parameters given an UE config that ensures a valid UE +/// PDSCH resource allocation in the Cell Resource Grid. The search iterates through different SearchSpace and PDSCH +/// time-domain resource candidates, returning only the valid ones. This class operates as a range with begin() +/// and end() iterators, and only searches for valid candidates in a lazy fashion. That means that we only compute +/// all valid candidates if we iterate from begin() to end(). +/// +/// To be a valid candidate, the following conditions must be met: +/// - SearchSpace must be part of UE dedicated configuration only. +/// - The slot must have sufficient DL symbols, given the PDSCH time-domain resource. +/// - For \c is_retx equal to true, we ensure the candidates will lead to allocations with the same number of DL +/// symbols and RNTI type as the previous HARQ allocation. +class ue_pdsch_alloc_param_candidate_searcher +{ + using search_space_candidate_list = static_vector; + using ss_iter = static_vector::const_iterator; + +public: + struct iterator; + + /// Parameters for a given PDSCH candidate. + struct candidate { + /// Chosen Search Space. + const search_space_info& ss() const { return **ss_it; } + + /// Chosen PDSCH Time Domain Resource. + const pdsch_time_domain_resource_allocation& pdsch_td_res() const + { + return (*ss_it)->pdsch_time_domain_list[time_res]; + } + + /// Chosen PDSCH Time Domain Resource Index. + unsigned pdsch_td_res_index() const { return time_res; } + + /// Chosen DCI DL RNTI Config Type. + dci_dl_rnti_config_type dci_dl_rnti_cfg_type() const + { + if (rnti_type.has_value()) { + return rnti_type.value(); + } + return ss().get_dl_dci_format() == dci_dl_format::f1_0 ? dci_dl_rnti_config_type::c_rnti_f1_0 + : dci_dl_rnti_config_type::c_rnti_f1_1; + } + + bool operator==(const candidate& other) const + { + return ss_it == other.ss_it and time_res == other.time_res and rnti_type == other.rnti_type; + } + bool operator!=(const candidate& other) const { return not(*this == other); } + /// A candidate being less than another means that the first has higher priority than the second. + bool operator<(const candidate& other) const + { + return (ss_it < other.ss_it) or (ss_it == other.ss_it and time_res < other.time_res); + } + bool operator>=(const candidate& other) const { return not(*this < other); } + bool operator<=(const candidate& other) const { return *this < other or *this == other; } + bool operator>(const candidate& other) const { return not(*this < other) and *this != other; } + + private: + friend struct iterator; + friend class ue_pdsch_alloc_param_candidate_searcher; + + candidate(ss_iter ss, unsigned time_res_, std::optional rnti_type_) : + ss_it(ss), time_res(time_res_), rnti_type(rnti_type_) + { + } + + ss_iter ss_it; + unsigned time_res; + std::optional rnti_type; + }; + + /// Iterator for list of candidates. + struct iterator { + using value_type = candidate; + using reference = value_type&; + using pointer = value_type*; + + iterator(ue_pdsch_alloc_param_candidate_searcher& parent_, ss_iter ss_it, unsigned time_res_) : + parent(&parent_), current(ss_it, time_res_, parent->preferred_rnti_type) + { + // Cell is not part of UE configured cells. + if (parent->ue_cc == nullptr) { + return; + } + + parent->iterate_until_valid_candidate_found(current); + if (not parent->is_candidate_valid(current)) { + // No valid candidates found and iteration finished. + current.ss_it = parent->ss_candidate_list.end(); + current.time_res = 0; + } + } + + candidate& operator*() { return current; } + const candidate& operator*() const { return current; } + candidate* operator->() { return ¤t; } + const candidate* operator->() const { return ¤t; } + + /// Incrementing the iterator means moving up one position in the list of candidates. + iterator& operator++() + { + // Cell is not part of UE configured cells. + if (parent->ue_cc == nullptr) { + return *this; + } + + ++current.time_res; + // When we increment the time_res, it may be pointing to the end of the PDSCH time domain resource list. + // So, we need to move to the next SearchSpace and reset time resource to 0. + if (current.time_res >= (*current.ss_it)->pdsch_time_domain_list.size()) { + current.time_res = 0; + ++current.ss_it; + if (current.ss_it == parent->ss_candidate_list.end()) { + // Iteration finished. + return *this; + } + } + parent->iterate_until_valid_candidate_found(current); + if (not parent->is_candidate_valid(current)) { + // No valid candidates found and iteration finished. + current.ss_it = parent->ss_candidate_list.end(); + current.time_res = 0; + } + return *this; + } + + bool operator==(const iterator& other) const { return current == other.current; } + bool operator!=(const iterator& other) const { return not(*this == other); } + + private: + ue_pdsch_alloc_param_candidate_searcher* parent; + candidate current; + }; + + /// Create a searcher for UE PDSCH parameters. + ue_pdsch_alloc_param_candidate_searcher(const ue& ue_ref_, + du_cell_index_t cell_index, + dl_harq_process& dl_harq_, + slot_point pdcch_slot_) : + ue_ref(ue_ref_), + ue_cc(ue_ref.find_cell(cell_index)), + dl_harq(dl_harq_), + is_retx(not dl_harq.empty()), + pdcch_slot(pdcch_slot_) + { + // Cell is not part of UE configured cells. + if (ue_cc == nullptr) { + return; + } + + if (not dl_harq.empty()) { + preferred_rnti_type = dl_harq.last_alloc_params().dci_cfg_type; + } + + // Generate list of Search Spaces. + generate_ss_candidates(); + } + + /// Get begin to the list of candidates. + iterator begin() { return iterator{*this, ss_candidate_list.begin(), 0}; } + iterator end() { return iterator{*this, ss_candidate_list.end(), 0}; } + + /// Returns whether there are candidates or not. + bool is_empty() { return ss_candidate_list.empty(); } + +private: + // Generate Search Space candidates for a given HARQ. + void generate_ss_candidates() + { + // Update alloc_params list. + ss_candidate_list = ue_cc->get_active_dl_search_spaces(pdcch_slot, preferred_rnti_type); + // Consider SearchSpaces only in UE dedicated configuration. + span ue_ded_cfg_ss = + ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces; + bool is_css_in_ue_dedicated_cfg = + std::any_of(ue_ded_cfg_ss.begin(), ue_ded_cfg_ss.end(), [](const search_space_configuration& ss) { + return ss.is_common_search_space(); + }); + // Filter out any SearchSpace configured which belong to common configuration only if there is no Common + // SearchSpace (CSS) configured in UE dedicated configuration. If there is at least one CSS configured in UE + // dedicated configuration then we need to consider also the SearchSpaces configured in common configuration as they + // have higher priority due to lower SearchSpace index. + if (not is_css_in_ue_dedicated_cfg) { + const search_space_info** ss_iterator = ss_candidate_list.begin(); + while (ss_iterator != ss_candidate_list.end()) { + const auto* ue_ded_cfg_ss_it = std::find_if( + ue_ded_cfg_ss.begin(), + ue_ded_cfg_ss.end(), + [id = (*ss_iterator)->cfg->get_id()](const search_space_configuration& ss) { return ss.get_id() == id; }); + if (ue_ded_cfg_ss_it == ue_ded_cfg_ss.end()) { + ss_iterator = ss_candidate_list.erase(ss_iterator); + } else { + ++ss_iterator; + } + } + } + } + + // Check if a candidate has valid parameters for an allocation. + bool is_candidate_valid(const candidate& current) const + { + // Check whether SearchSpace is valid. + if (current.ss_it == nullptr or current.ss_it == ss_candidate_list.end()) { + return false; + } + + // Check whether PDSCH Time Domain resource index is valid. + if (current.time_res >= (*current.ss_it)->pusch_time_domain_list.size()) { + return false; + } + + // Check whether PDSCH slot is DL enabled. + if (not ue_cc->cfg().cell_cfg_common.is_dl_enabled(pdcch_slot + current.pdsch_td_res().k0)) { + return false; + } + + // Check whether PDSCH time domain resource fits in DL symbols of the slot. + if (ue_cc->cfg().cell_cfg_common.get_nof_dl_symbol_per_slot(pdcch_slot + current.pdsch_td_res().k0) < + current.pdsch_td_res().symbols.stop()) { + return false; + } + + // Check whether PDSCH time domain resource does not overlap with CORESET. + if (current.pdsch_td_res().symbols.start() < + current.ss().cfg->get_first_symbol_index() + current.ss().coreset->duration) { + return false; + } + + // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for + // the first transmission. + if (is_retx and current.pdsch_td_res().symbols.length() != dl_harq.last_alloc_params().nof_symbols) { + return false; + } + + return true; + } + + // Iterate over the list of candidates until a valid one is found. + void iterate_until_valid_candidate_found(candidate& current) + { + for (; current.ss_it != ss_candidate_list.end(); ++current.ss_it) { + // NOTE: At this point UE is no longer in fallback mode. + // Skip SearchSpaces without PDCCH candidates to be monitored in this slot. + if (not(*current.ss_it) + ->get_pdcch_candidates( + ue_cc->get_aggregation_level( + ue_cc->link_adaptation_controller().get_effective_cqi(), **current.ss_it, true), + pdcch_slot) + .empty()) { + for (; current.time_res < (*current.ss_it)->pdsch_time_domain_list.size(); ++current.time_res) { + if (is_candidate_valid(current)) { + // Valid candidate found. + return; + } + } + } + } + } + + // UE being allocated. + const ue& ue_ref; + // UE cell being allocated. + const ue_cell* ue_cc; + + // DL HARQ considered for allocation. + const dl_harq_process& dl_harq; + // Whether the current search is for a newTx or a reTx. + const bool is_retx; + // List of Search Space candidates for the DL HARQ considered for allocation. + search_space_candidate_list ss_candidate_list; + // RNTI type used to generate ss_candidate_list. + std::optional preferred_rnti_type; + + // PDCCH slot point used to verify if the PDSCH fits a DL slot. + slot_point pdcch_slot; +}; + +} // namespace srsran diff --git a/lib/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher.h b/lib/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher.h deleted file mode 100644 index d15d77b925..0000000000 --- a/lib/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher.h +++ /dev/null @@ -1,315 +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 "ue.h" - -namespace srsran { - -/// \brief This class assists with the search of PDSCH parameters given an UE config that ensures a valid UE PDSCH -/// resource allocation in the Cell Resource Grid. The search iterates through different HARQ, SearchSpace and PDSCH -/// time-domain resource candidates, returning only the valid ones. This class operates as a range with begin() -/// and end() iterators, and only searches for valid candidates in a lazy fashion. That means that we only compute -/// all valid candidates if we iterate from begin() to end(). -/// -/// To be a valid candidate, the following conditions must be met: -/// - The HARQ process state must match the \c is_retx state passed in the constructor. If \c is_retx is false, there -/// should be at most one empty HARQ candidate returned during the whole search. If \c is_retx is true, all the HARQs -/// of the candidates returned must have a pending retransmission. -/// - We avoid SearchSpace#0 for UE PDSCH allocation. -/// - The slot must have sufficient DL symbols, given the PDSCH time-domain resource. -/// - For \c is_retx equal to true, we ensure the candidates will lead to allocations with the same number of DL -/// symbols as the previous HARQ allocation. -class ue_pdsch_param_candidate_searcher -{ - using search_space_candidate_list = static_vector; - using dl_harq_iter = static_vector::const_iterator; - using ss_iter = static_vector::const_iterator; - -public: - struct iterator; - - /// Parameters for a given PDSCH candidate. - struct candidate { - /// DL HARQ process (may be newTx or reTx). - const dl_harq_process& harq() const { return **harq_it; } - - /// Chosen Search Space. - const search_space_info& ss() const { return **ss_it; } - - /// Chosen PDSCH Time Domain Resource. - const pdsch_time_domain_resource_allocation& pdsch_td_res() const - { - return (*ss_it)->pdsch_time_domain_list[time_res]; - } - - /// Chosen PDSCH Time Domain Resource Index. - unsigned pdsch_td_res_index() const { return time_res; } - - /// Chosen DCI DL RNTI Config Type. - dci_dl_rnti_config_type dci_dl_rnti_cfg_type() const - { - if (not harq().empty()) { - return harq().last_alloc_params().dci_cfg_type; - } - return ss().get_dl_dci_format() == dci_dl_format::f1_0 ? dci_dl_rnti_config_type::c_rnti_f1_0 - : dci_dl_rnti_config_type::c_rnti_f1_1; - } - - bool operator==(const candidate& other) const - { - return harq_it == other.harq_it and ss_it == other.ss_it and time_res == other.time_res; - } - bool operator!=(const candidate& other) const { return not(*this == other); } - /// A candidate being less than another means that the first has higher priority than the second. - bool operator<(const candidate& other) const - { - return harq_it < other.harq_it or (harq_it == other.harq_it and ss_it < other.ss_it) or - (harq_it == other.harq_it and ss_it == other.ss_it and time_res < other.time_res); - } - bool operator>=(const candidate& other) const { return not(*this < other); } - bool operator<=(const candidate& other) const { return *this < other or *this == other; } - bool operator>(const candidate& other) const { return not(*this < other) and *this != other; } - - private: - friend struct iterator; - friend class ue_pdsch_param_candidate_searcher; - - candidate(dl_harq_iter h, ss_iter ss, unsigned time_res_) : harq_it(h), ss_it(ss), time_res(time_res_) {} - - dl_harq_iter harq_it; - ss_iter ss_it; - unsigned time_res; - }; - - /// Iterator for list of candidates. - struct iterator { - using value_type = candidate; - using reference = value_type&; - using pointer = value_type*; - - iterator(ue_pdsch_param_candidate_searcher& parent_, dl_harq_iter h_it, ss_iter ss_it, unsigned time_res_) : - parent(&parent_), current(h_it, ss_it, time_res_) - { - parent->iterate_until_valid_candidate_found(current); - } - - candidate& operator*() { return current; } - const candidate& operator*() const { return current; } - candidate* operator->() { return ¤t; } - const candidate* operator->() const { return ¤t; } - - /// Incrementing the iterator means moving up one position in the list of candidates. - iterator& operator++() - { - ++current.time_res; - // When we increment the time_res, it may be pointing to the end of the PDSCH time domain resource list. - // So, we may need to move to the next SearchSpace or HARQ candidate. - parent->iterate_until_valid_candidate_found(current); - return *this; - } - - bool operator==(const iterator& other) const { return current == other.current; } - bool operator!=(const iterator& other) const { return not(*this == other); } - - private: - ue_pdsch_param_candidate_searcher* parent; - candidate current; - }; - - /// Create a searcher for UE PDSCH parameters. - ue_pdsch_param_candidate_searcher(const ue& ue_ref_, - ue_cell_index_t cell_index, - bool is_retx_, - slot_point pdcch_slot_, - srslog::basic_logger& logger_) : - ue_ref(ue_ref_), ue_cc(ue_ref.get_cell(cell_index)), is_retx(is_retx_), pdcch_slot(pdcch_slot_), logger(logger_) - { - if (is_retx) { - // 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() and (not h.last_alloc_params().is_fallback)) { - dl_harq_candidates.push_back(&h); - } - } - std::sort( - dl_harq_candidates.begin(), - dl_harq_candidates.end(), - [](const dl_harq_process* lhs, const dl_harq_process* rhs) { return lhs->slot_ack() < rhs->slot_ack(); }); - } else if (ue_cc.is_active()) { - // If there are no pending new Tx bytes or UE in fallback, return. - if (ue_cc.is_in_fallback_mode() or (not ue_ref.has_pending_dl_newtx_bytes())) { - return; - } - - // Find empty HARQ. Create a list with a single position. - const dl_harq_process* h = ue_cc.harqs.find_empty_dl_harq(); - if (h != nullptr) { - dl_harq_candidates.push_back(h); - } else { - // No empty HARQs are available. Log this occurrence. - if (ue_cc.harqs.find_dl_harq_waiting_ack() == nullptr) { - // A HARQ is already being retransmitted, or all HARQs are waiting for a grant for a retransmission. - logger.debug("ue={} rnti={} PDSCH allocation skipped. Cause: No available HARQs for new transmissions.", - ue_cc.ue_index, - ue_cc.rnti()); - } else { - // All HARQs are waiting for their respective HARQ-ACK. This may be a symptom of a long RTT for the PDSCH - // and HARQ-ACK. - logger.warning( - "ue={} rnti={} PDSCH allocation skipped. Cause: All the HARQs are allocated and waiting for their " - "respective HARQ-ACK. Check if any HARQ-ACK went missing in the lower layers or is arriving too late to " - "the scheduler.", - ue_cc.ue_index, - ue_cc.rnti()); - } - } - } - } - - /// Get begin to the list of candidates. - iterator begin() { return iterator{*this, dl_harq_candidates.begin(), ss_candidate_list.begin(), 0}; } - iterator end() { return iterator{*this, dl_harq_candidates.end(), nullptr, 0}; } - - /// List of DL HARQ candidates. - const static_vector& dl_harqs() const { return dl_harq_candidates; } - -private: - // Generate Search Space candidates for a given HARQ. - void generate_ss_candidates(dl_harq_iter current_harq_it) - { - if (harq_of_ss_list == *current_harq_it) { - // The HARQ candidate didn't change. We can early exit. - return; - } - srsran_assert(is_retx or harq_of_ss_list == nullptr, "Regenerating SS candidates should only be needed for reTxs"); - - // New HARQ. Search Space candidates are recomputed. - const auto* prev_h = harq_of_ss_list; - harq_of_ss_list = *current_harq_it; - - // Check which RNTI Type is preferred for this UE and HARQ. - std::optional preferred_rnti_type; - // NOTE: At this point UE is no longer in fallback mode. - if (is_retx) { - preferred_rnti_type = harq_of_ss_list->last_alloc_params().dci_cfg_type; - } - - if (prev_h != nullptr and preferred_rnti_type == current_rnti_type) { - // It is the same RNTI Type as the previous HARQ candidate. Search Space Candidate doesn't need to be regenerated, - // and we can use the previous list. - return; - } - - // Update alloc_params list. - ss_candidate_list = ue_cc.get_active_dl_search_spaces(pdcch_slot, preferred_rnti_type); - current_rnti_type = preferred_rnti_type; - } - - // Check if a candidate has valid parameters for an allocation. - bool is_candidate_valid(const candidate& current) const - { - if (ue_cc.cfg().cell_cfg_common.get_nof_dl_symbol_per_slot(pdcch_slot + current.pdsch_td_res().k0) < - current.pdsch_td_res().symbols.stop()) { - // Check whether PDSCH time domain resource fits in DL symbols of the slot. - return false; - } - - // Check whether PDSCH time domain resource does not overlap with CORESET. - if (current.pdsch_td_res().symbols.start() < - current.ss().cfg->get_first_symbol_index() + current.ss().coreset->duration) { - return false; - } - - // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for - // the first transmission. - if (is_retx and current.pdsch_td_res().symbols.length() != current.harq().last_alloc_params().nof_symbols) { - return false; - } - - return true; - } - - // Iterate over the list of candidates until a valid one is found. - void iterate_until_valid_candidate_found(candidate& current) - { - for (; current.harq_it != dl_harq_candidates.end(); ++current.harq_it) { - // If the HARQ candidate changed, generate new list of Search Spaces. - generate_ss_candidates(current.harq_it); - - for (; current.ss_it != ss_candidate_list.end(); ++current.ss_it) { - // NOTE: At this point UE is no longer in fallback mode. - if ((*current.ss_it) - ->get_pdcch_candidates( - ue_cc.get_aggregation_level( - ue_cc.link_adaptation_controller().get_effective_cqi(), **current.ss_it, true), - pdcch_slot) - .empty()) { - // Skip SearchSpaces without PDCCH candidates to be monitored in this slot. - continue; - } - - for (; current.time_res < (*current.ss_it)->pdsch_time_domain_list.size(); ++current.time_res) { - if (is_candidate_valid(current)) { - // Valid candidate found. - return; - } - } - current.time_res = 0; - } - current.ss_it = ss_candidate_list.begin(); - } - - // Iteration finished. - ss_candidate_list.clear(); - harq_of_ss_list = nullptr; - current.ss_it = nullptr; - current.time_res = 0; - } - - // UE being allocated. - const ue& ue_ref; - // UE cell being allocated. - const ue_cell& ue_cc; - // Whether the current search is for a newTx or a reTx. - const bool is_retx; - - // List of HARQ candidates considered for allocation. - static_vector dl_harq_candidates; - - // List of Search Space candidates for the current HARQ candidate being iterated over. - search_space_candidate_list ss_candidate_list; - // HARQ candidate for which the Search Space candidate list was generated. - const dl_harq_process* harq_of_ss_list = nullptr; - // RNTI type used to generate ss_candidate_list. - std::optional current_rnti_type; - - // PDCCH slot point used to verify if the PDSCH fits a DL slot. - slot_point pdcch_slot; - - // Logger - srslog::basic_logger& logger; -}; - -} // namespace srsran diff --git a/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h b/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h new file mode 100644 index 0000000000..f210be52f2 --- /dev/null +++ b/lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h @@ -0,0 +1,316 @@ +/* + * + * 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 "../support/pusch/pusch_td_resource_indices.h" +#include "ue.h" + +namespace srsran { + +/// \brief This class assists with the search of PUSCH allocation parameters given an UE config that ensures a valid UE +/// PUSCH resource allocation in the Cell Resource Grid. The search iterates through different SearchSpace and PUSCH +/// time-domain resource candidates, returning only the valid ones. This class operates as a range with begin() +/// and end() iterators, and only searches for valid candidates in a lazy fashion. That means that we only compute +/// all valid candidates if we iterate from begin() to end(). +/// +/// To be a valid candidate, the following conditions must be met: +/// - SearchSpace must be part of UE dedicated configuration only. +/// - The slot must have sufficient UL symbols, given the PUSCH time-domain resource. +/// - For \c is_retx equal to true, we ensure the candidates will lead to allocations with the same number of UL +/// symbols and RNTI type as the previous HARQ allocation. +class ue_pusch_alloc_param_candidate_searcher +{ + using search_space_candidate_list = static_vector; + using ss_iter = static_vector::const_iterator; + +public: + struct iterator; + + /// Parameters for a given PUSCH candidate. + struct candidate { + /// Chosen Search Space. + const search_space_info& ss() const { return **ss_it; } + + /// Chosen PUSCH Time Domain Resource. + const pusch_time_domain_resource_allocation& pusch_td_res() const + { + return (*ss_it)->pusch_time_domain_list[time_res]; + } + + /// Chosen PUSCH Time Domain Resource Index. + unsigned pusch_td_res_index() const { return time_res; } + + /// Chosen DCI UL RNTI Config Type. + dci_ul_rnti_config_type dci_ul_rnti_cfg_type() const + { + if (rnti_type.has_value()) { + return rnti_type.value(); + } + return ss().get_ul_dci_format() == dci_ul_format::f0_0 ? dci_ul_rnti_config_type::c_rnti_f0_0 + : dci_ul_rnti_config_type::c_rnti_f0_1; + } + + bool operator==(const candidate& other) const + { + return ss_it == other.ss_it and time_res == other.time_res and rnti_type == other.rnti_type; + } + bool operator!=(const candidate& other) const { return not(*this == other); } + /// A candidate being less than another means that the first has higher priority than the second. + bool operator<(const candidate& other) const + { + return (ss_it < other.ss_it) or (ss_it == other.ss_it and time_res < other.time_res); + } + bool operator>=(const candidate& other) const { return not(*this < other); } + bool operator<=(const candidate& other) const { return *this < other or *this == other; } + bool operator>(const candidate& other) const { return not(*this < other) and *this != other; } + + private: + friend struct iterator; + friend class ue_pusch_alloc_param_candidate_searcher; + + candidate(ss_iter ss, unsigned time_res_, std::optional rnti_type_) : + ss_it(ss), time_res(time_res_), rnti_type(rnti_type_) + { + } + + ss_iter ss_it; + unsigned time_res; + std::optional rnti_type; + }; + + /// Iterator for list of candidates. + struct iterator { + using value_type = candidate; + using reference = value_type&; + using pointer = value_type*; + + iterator(ue_pusch_alloc_param_candidate_searcher& parent_, ss_iter ss_it, unsigned time_res_) : + parent(&parent_), current(ss_it, time_res_, parent->preferred_rnti_type) + { + // Cell is not part of UE configured cells. + if (parent->ue_cc == nullptr) { + return; + } + + parent->iterate_until_valid_candidate_found(current); + if (not parent->is_candidate_valid(current)) { + // No valid candidates found and iteration finished. + current.ss_it = parent->ss_candidate_list.end(); + current.time_res = 0; + } + } + + candidate& operator*() { return current; } + const candidate& operator*() const { return current; } + candidate* operator->() { return ¤t; } + const candidate* operator->() const { return ¤t; } + + /// Incrementing the iterator means moving up one position in the list of candidates. + iterator& operator++() + { + // Cell is not part of UE configured cells. + if (parent->ue_cc == nullptr) { + return *this; + } + + ++current.time_res; + // When we increment the time_res, it may be pointing to the end of the PUSCH time domain resource list. + // So, we need to move to the next SearchSpace and reset time resource to 0. + if (current.time_res >= (*current.ss_it)->pusch_time_domain_list.size()) { + current.time_res = 0; + ++current.ss_it; + if (current.ss_it != parent->ss_candidate_list.end()) { + parent->valid_ss_pusch_td_res_indices = + get_pusch_td_resource_indices(parent->ue_cc->cfg().cell_cfg_common, parent->pdcch_slot, *current.ss_it); + } else { + // Iteration finished. + return *this; + } + } + parent->iterate_until_valid_candidate_found(current); + if (not parent->is_candidate_valid(current)) { + // No valid candidates found and iteration finished. + current.ss_it = parent->ss_candidate_list.end(); + current.time_res = 0; + } + return *this; + } + + bool operator==(const iterator& other) const { return current == other.current; } + bool operator!=(const iterator& other) const { return not(*this == other); } + + private: + ue_pusch_alloc_param_candidate_searcher* parent; + candidate current; + }; + + /// Create a searcher for UE PUSCH parameters. + ue_pusch_alloc_param_candidate_searcher(const ue& ue_ref_, + du_cell_index_t cell_index, + ul_harq_process& ul_harq_, + slot_point pdcch_slot_) : + ue_ref(ue_ref_), + ue_cc(ue_ref.find_cell(cell_index)), + ul_harq(ul_harq_), + is_retx(not ul_harq.empty()), + pdcch_slot(pdcch_slot_) + { + // Cell is not part of UE configured cells. + if (ue_cc == nullptr) { + return; + } + + if (not ul_harq.empty()) { + preferred_rnti_type = ul_harq.last_tx_params().dci_cfg_type; + } + + // Generate list of Search Spaces. + generate_ss_candidates(); + + // Generate valid PUSCH Time Domain Resource Indices for first SearchSpace in the SS candidate list. + if (not ss_candidate_list.empty()) { + valid_ss_pusch_td_res_indices = + get_pusch_td_resource_indices(ue_cc->cfg().cell_cfg_common, pdcch_slot, *ss_candidate_list.begin()); + } + } + + /// Get begin to the list of candidates. + iterator begin() { return iterator{*this, ss_candidate_list.begin(), 0}; } + iterator end() { return iterator{*this, ss_candidate_list.end(), 0}; } + + /// Returns whether there are candidates or not. + bool is_empty() { return ss_candidate_list.empty() or valid_ss_pusch_td_res_indices.empty(); } + +private: + // Generate Search Space candidates for a given HARQ. + void generate_ss_candidates() + { + // Update alloc_params list. + ss_candidate_list = ue_cc->get_active_ul_search_spaces(pdcch_slot, preferred_rnti_type); + // Consider SearchSpaces only in UE dedicated configuration. + span ue_ded_cfg_ss = + ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces; + bool is_css_in_ue_dedicated_cfg = + std::any_of(ue_ded_cfg_ss.begin(), ue_ded_cfg_ss.end(), [](const search_space_configuration& ss) { + return ss.is_common_search_space(); + }); + // Filter out any SearchSpace configured which belong to common configuration only if there is no Common + // SearchSpace (CSS) configured in UE dedicated configuration. If there is at least one CSS configured in UE + // dedicated configuration then we need to consider also the SearchSpaces configured in common configuration as they + // have higher priority due to lower SearchSpace index. + if (not is_css_in_ue_dedicated_cfg) { + const search_space_info** ss_iterator = ss_candidate_list.begin(); + while (ss_iterator != ss_candidate_list.end()) { + const auto* ue_ded_cfg_ss_it = std::find_if( + ue_ded_cfg_ss.begin(), + ue_ded_cfg_ss.end(), + [id = (*ss_iterator)->cfg->get_id()](const search_space_configuration& ss) { return ss.get_id() == id; }); + if (ue_ded_cfg_ss_it == ue_ded_cfg_ss.end()) { + ss_iterator = ss_candidate_list.erase(ss_iterator); + } else { + ++ss_iterator; + } + } + } + } + + // Check if a candidate has valid parameters for an allocation. + bool is_candidate_valid(const candidate& current) const + { + // Check whether SearchSpace is valid. + if (current.ss_it == nullptr or current.ss_it == ss_candidate_list.end()) { + return false; + } + + // Check whether PUSCH Time Domain resource index is valid. + if (current.time_res >= (*current.ss_it)->pusch_time_domain_list.size()) { + return false; + } + + // Check whether PUSCH slot is UL enabled. + if (not ue_cc->cfg().cell_cfg_common.is_ul_enabled(pdcch_slot + current.pusch_td_res().k2)) { + return false; + } + + // Check whether PUSCH time domain resource fits in UL symbols of the slot. + if (ue_cc->cfg().cell_cfg_common.get_nof_ul_symbol_per_slot(pdcch_slot + current.pusch_td_res().k2) != + current.pusch_td_res().symbols.length()) { + return false; + } + + // If it is a retx, we need to ensure we use a time_domain_resource with the same number of symbols as used for + // the first transmission. + if (is_retx and current.pusch_td_res().symbols.length() != ul_harq.last_tx_params().nof_symbols) { + return false; + } + + return true; + } + + // Iterate over the list of candidates until a valid one is found. + void iterate_until_valid_candidate_found(candidate& current) + { + for (; current.ss_it != ss_candidate_list.end(); ++current.ss_it) { + // NOTE: At this point UE is no longer in fallback mode. + // Skip SearchSpaces without PDCCH candidates to be monitored in this slot. + if (not(*current.ss_it) + ->get_pdcch_candidates( + ue_cc->get_aggregation_level( + ue_cc->link_adaptation_controller().get_effective_cqi(), **current.ss_it, false), + pdcch_slot) + .empty()) { + for (; current.time_res < (*current.ss_it)->pusch_time_domain_list.size(); ++current.time_res) { + if (std::find(valid_ss_pusch_td_res_indices.begin(), valid_ss_pusch_td_res_indices.end(), current.time_res) == + valid_ss_pusch_td_res_indices.end()) { + continue; + } + if (is_candidate_valid(current)) { + // Valid candidate found. + return; + } + } + } + } + } + + // UE being allocated. + const ue& ue_ref; + // UE cell being allocated. + const ue_cell* ue_cc; + + // UL HARQ considered for allocation. + const ul_harq_process& ul_harq; + // Whether the current search is for a newTx or a reTx. + const bool is_retx; + // List of SearchSpace candidates for the UL HARQ considered for allocation. + search_space_candidate_list ss_candidate_list; + // Valid PUSCH time domain resource indices for current SearchSpace. + static_vector valid_ss_pusch_td_res_indices; + // RNTI type used to generate ss_candidate_list. + std::optional preferred_rnti_type; + + // PDCCH slot point used to verify if the PUSCH fits a UL slot. + slot_point pdcch_slot; +}; + +} // namespace srsran diff --git a/lib/scheduler/ue_scheduling/ue_scheduler.h b/lib/scheduler/ue_scheduling/ue_scheduler.h index be5f05688b..788a078349 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler.h +++ b/lib/scheduler/ue_scheduling/ue_scheduler.h @@ -31,6 +31,7 @@ namespace srsran { class pdcch_resource_allocator; struct cell_resource_allocator; class sched_ue_configuration_handler; +class scheduler_event_logger; struct ue_scheduler_cell_params { du_cell_index_t cell_index; @@ -38,6 +39,7 @@ struct ue_scheduler_cell_params { pucch_allocator* pucch_alloc; uci_allocator* uci_alloc; cell_resource_allocator* cell_res_alloc; + scheduler_event_logger* ev_logger; }; /// Interface of data scheduler that is used to allocate UE DL and UL grants in a given slot diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp index c4221a8c6a..6e9f43e0a9 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.cpp @@ -27,13 +27,12 @@ using namespace srsran; ue_scheduler_impl::ue_scheduler_impl(const scheduler_ue_expert_config& expert_cfg_, sched_configuration_notifier& mac_notif, - scheduler_metrics_handler& metric_handler, - scheduler_event_logger& sched_ev_logger) : + scheduler_metrics_handler& metric_handler) : expert_cfg(expert_cfg_), sched_strategy(create_scheduler_strategy(scheduler_strategy_params{"time_rr", &srslog::fetch_basic_logger("SCHED")}, expert_cfg)), ue_alloc(expert_cfg, ue_db, srslog::fetch_basic_logger("SCHED")), - event_mng(ue_db, metric_handler, sched_ev_logger), + event_mng(ue_db, metric_handler), logger(srslog::fetch_basic_logger("SCHED")) { } @@ -42,8 +41,10 @@ void ue_scheduler_impl::add_cell(const ue_scheduler_cell_params& params) { ue_res_grid_view.add_cell(*params.cell_res_alloc); cells[params.cell_index] = std::make_unique(expert_cfg, params, ue_db); - event_mng.add_cell( - *params.cell_res_alloc, cells[params.cell_index]->fallback_sched, cells[params.cell_index]->uci_sched); + event_mng.add_cell(*params.cell_res_alloc, + cells[params.cell_index]->fallback_sched, + cells[params.cell_index]->uci_sched, + *params.ev_logger); ue_alloc.add_cell(params.cell_index, *params.pdcch_sched, *params.uci_alloc, *params.cell_res_alloc); } @@ -162,7 +163,7 @@ void ue_scheduler_impl::run_slot(slot_point slot_tx, du_cell_index_t cell_index) event_mng.run(slot_tx, cell_index); // Mark the start of a new slot in the UE grid allocator. - ue_alloc.slot_indication(); + ue_alloc.slot_indication(slot_tx); // Schedule periodic UCI (SR and CSI) before any UL grants. cells[cell_index]->uci_sched.run_slot(*cells[cell_index]->cell_res_alloc); diff --git a/lib/scheduler/ue_scheduling/ue_scheduler_impl.h b/lib/scheduler/ue_scheduling/ue_scheduler_impl.h index c451d1b00d..c179a3f1c9 100644 --- a/lib/scheduler/ue_scheduling/ue_scheduler_impl.h +++ b/lib/scheduler/ue_scheduling/ue_scheduler_impl.h @@ -47,8 +47,7 @@ class ue_scheduler_impl final : public ue_scheduler public: explicit ue_scheduler_impl(const scheduler_ue_expert_config& expert_cfg_, sched_configuration_notifier& mac_notif, - scheduler_metrics_handler& metric_handler, - scheduler_event_logger& sched_ev_logger); + scheduler_metrics_handler& metric_handler); void add_cell(const ue_scheduler_cell_params& params) override; diff --git a/lib/srsvec/compare.cpp b/lib/srsvec/compare.cpp index 0040571bfb..f8197f634b 100644 --- a/lib/srsvec/compare.cpp +++ b/lib/srsvec/compare.cpp @@ -34,7 +34,7 @@ const char* srsran::srsvec::detail::find(span input, const char* val unsigned index = 0; char v = *value; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ // Advances the input index to either the first SIMD word that contains value or the last index rounded to 32. for (unsigned simd_index_end = 32 * (input.size() / 32); index != simd_index_end; index += 32) { // Load 32 consecutive words starting at index. @@ -48,9 +48,9 @@ const char* srsran::srsvec::detail::find(span input, const char* val break; } } -#endif // HAVE_AVX2 +#endif // __AVX2__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON bool found = false; // Advances the input index to either the first SIMD word that contains value or the last index rounded to 16. for (unsigned simd_index_end = 16 * (input.size() / 16); index != simd_index_end; index += 16) { @@ -75,7 +75,7 @@ const char* srsran::srsvec::detail::find(span input, const char* val break; } } -#endif // HAVE_NEON +#endif // __ARM_NEON // Keeps iterating from the current index to the end. for (; index != input.size(); ++index) { diff --git a/lib/srsvec/simd.h b/lib/srsvec/simd.h index 611f1b50ba..b6f0391217 100644 --- a/lib/srsvec/simd.h +++ b/lib/srsvec/simd.h @@ -25,7 +25,7 @@ #include "srsran/adt/complex.h" // Enables intel intrinsics, it includes AVX, AVX2, FMA, AVX512. -#ifdef HAVE_SSE +#ifdef __SSE4_1__ // gcc-12 gives a likely false alarm when including AVX512 intrinsics. Disable maybe-uninitialized diagnostics. #ifndef __clang__ @@ -38,11 +38,11 @@ #ifndef __clang__ #pragma GCC diagnostic pop #endif // __clang__ -#endif // HAVE_SSE +#endif // __SSE4_1__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON #include -#endif // HAVE_NEON +#endif // __ARM_NEON namespace srsran { @@ -56,41 +56,41 @@ inline bool is_simd_addr_aligned(const void* addr, uintptr_t mask) /// SIMD Vector bit alignment. /// -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ constexpr unsigned SIMD_BYTE_ALIGN = 64; inline bool SIMD_IS_ALIGNED(const void* ptr) { return is_simd_addr_aligned(ptr, 0x3f); } -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX +#else /* __AVX512F__ */ +#ifdef __AVX__ constexpr unsigned SIMD_BYTE_ALIGN = 32; inline bool SIMD_IS_ALIGNED(const void* ptr) { return is_simd_addr_aligned(ptr, 0x1f); } -#else /* HAVE_AVX */ -#ifdef HAVE_SSE +#else /* __AVX__ */ +#ifdef __SSE4_1__ constexpr unsigned SIMD_BYTE_ALIGN = 16; inline bool SIMD_IS_ALIGNED(const void* ptr) { return is_simd_addr_aligned(ptr, 0x0f); } -#else /* HAVE_SSE */ +#else /* __SSE4_1__ */ constexpr unsigned SIMD_BYTE_ALIGN = 16; inline bool SIMD_IS_ALIGNED(const void* ptr) { return true; } -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX */ -#endif /* HAVE_AVX512 */ +#endif /* __SSE4_1__ */ +#endif /* __AVX__ */ +#endif /* __AVX512F__ */ /// /// Memory Sizes for Single Floating Point and fixed point. /// -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ #define SRSRAN_SIMD_F_SIZE 16 #define SRSRAN_SIMD_CF_SIZE 16 @@ -102,7 +102,7 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) #define SRSRAN_SIMD_C16_SIZE 0 #else -#ifdef HAVE_AVX2 +#ifdef __AVX2__ #define SRSRAN_SIMD_F_SIZE 8 #define SRSRAN_SIMD_CF_SIZE 8 @@ -113,8 +113,8 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) #define SRSRAN_SIMD_S_SIZE 16 #define SRSRAN_SIMD_C16_SIZE 16 -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ #define SRSRAN_SIMD_F_SIZE 4 #define SRSRAN_SIMD_CF_SIZE 4 @@ -125,8 +125,8 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) #define SRSRAN_SIMD_S_SIZE 8 #define SRSRAN_SIMD_C16_SIZE 8 -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON #define SRSRAN_SIMD_F_SIZE 4 #define SRSRAN_SIMD_CF_SIZE 4 @@ -136,7 +136,7 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) #define SRSRAN_SIMD_S_SIZE 8 #define SRSRAN_SIMD_C16_SIZE 8 -#else /* HAVE_NEON */ +#else /* __ARM_NEON */ #define SRSRAN_SIMD_F_SIZE 0 #define SRSRAN_SIMD_CF_SIZE 0 @@ -145,10 +145,10 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) #define SRSRAN_SIMD_S_SIZE 0 #define SRSRAN_SIMD_C16_SIZE 0 -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ #ifndef ENABLE_C16 #undef SRSRAN_SIMD_C16_SIZE @@ -161,21 +161,21 @@ inline bool SIMD_IS_ALIGNED(const void* ptr) /// Data types. /// -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ using simd_f_t = __m512; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ using simd_f_t = __m256; -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ using simd_f_t = __m128; -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON using simd_f_t = float32x4_t; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ /// /// Single precision Floating point functions. @@ -183,150 +183,150 @@ using simd_f_t = float32x4_t; inline simd_f_t srsran_simd_f_load(const float* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_load_ps(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_load_ps(ptr); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_load_ps(ptr); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_f32(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_loadu(const float* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_loadu_ps(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_loadu_ps(ptr); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_loadu_ps(ptr); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_f32(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_f_store(float* ptr, simd_f_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_store_ps(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_store_ps(ptr, simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_store_ps(ptr, simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_f32(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_f_storeu(float* ptr, simd_f_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_storeu_ps(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_storeu_ps(ptr, simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_storeu_ps(ptr, simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_f32(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_set1(float x) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_set1_ps(x); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_set1_ps(x); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_set1_ps(x); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vdupq_n_f32(x); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_mul(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_mul_ps(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_mul_ps(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_mul_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vmulq_f32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_rcp(simd_f_t a) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_rcp14_ps(a); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_rcp_ps(a); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_rcp_ps(a); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vmulq_f32(vrecpeq_f32(a), vrecpsq_f32(vrecpeq_f32(a), a)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 r = _mm512_add_ps(a, b); return _mm512_mask_sub_ps(r, 0b0101010101010101, a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_addsub_ps(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_addsub_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON // CURRENTLY USES GENERIC IMPLEMENTATION FOR NEON const float* a_ptr = reinterpret_cast(&a); const float* b_ptr = reinterpret_cast(&b); simd_f_t ret; @@ -339,110 +339,110 @@ inline simd_f_t srsran_simd_f_addsub(simd_f_t a, simd_f_t b) } } return ret; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_sub(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_sub_ps(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_sub_ps(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_sub_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vsubq_f32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_add(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_add_ps(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_add_ps(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_add_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vaddq_f32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_fma(simd_f_t acc, simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_fmadd_ps(a, b, acc); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_fmadd_ps(a, b, acc); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_fmadd_ps(a, b, acc); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vmlaq_f32(acc, a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_zero() { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_setzero_ps(); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_setzero_ps(); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_setzero_ps(); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vdupq_n_f32(0); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_swap(simd_f_t a) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_permute_ps(a, 0b10110001); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_permute_ps(a, 0b10110001); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_shuffle_ps(a, a, 0b10110001); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vcombine_f32(vrev64_f32(vget_low_f32(a)), vrev64_f32(vget_high_f32(a))); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_hadd(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ const __m512i idx1 = _mm512_setr_epi32((0b00000), (0b00010), (0b00100), @@ -464,35 +464,35 @@ inline simd_f_t srsran_simd_f_hadd(simd_f_t a, simd_f_t b) simd_f_t a1 = _mm512_permutex2var_ps(a, idx1, b); simd_f_t b1 = _mm512_permutex2var_ps(a, idx2, b); return _mm512_add_ps(a1, b1); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ simd_f_t a1 = _mm256_permute2f128_ps(a, b, 0b00100000); simd_f_t b1 = _mm256_permute2f128_ps(a, b, 0b00110001); return _mm256_hadd_ps(a1, b1); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_hadd_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vcombine_f32(vpadd_f32(vget_low_f32(a), vget_high_f32(a)), vpadd_f32(vget_low_f32(b), vget_high_f32(b))); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_sqrt(simd_f_t a) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_sqrt_ps(a); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_sqrt_ps(a); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_sqrt_ps(a); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON float32x4_t sqrt_reciprocal = vrsqrteq_f32(a); sqrt_reciprocal = vmulq_f32(vrsqrtsq_f32(vmulq_f32(a, sqrt_reciprocal), sqrt_reciprocal), sqrt_reciprocal); float32x4_t result = vmulq_f32(a, sqrt_reciprocal); @@ -504,77 +504,77 @@ inline simd_f_t srsran_simd_f_sqrt(simd_f_t a) uint32x4_t mask = vceqq_f32(a, zeros); // Force zero results and return. return vbslq_f32(mask, zeros, result); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_neg(simd_f_t a) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_xor_ps(_mm512_set1_ps(-0.0f), a); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_xor_ps(_mm256_set1_ps(-0.0f), a); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_xor_ps(_mm_set1_ps(-0.0f), a); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vnegq_f32(a); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_neg_mask(simd_f_t a, simd_f_t mask) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_xor_ps(mask, a); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_xor_ps(mask, a); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_xor_ps(mask, a); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return (float32x4_t)veorq_s32((int32x4_t)a, (int32x4_t)mask); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_abs(simd_f_t a) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_andnot_ps(_mm512_set1_ps(-0.0f), a); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_andnot_ps(_mm256_set1_ps(-0.0f), a); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_andnot_ps(_mm_set1_ps(-0.0f), a); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vabsq_f32(a); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_interleave_low(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_permutex2var_ps( a, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17), b); -#else /* HAVE_AVX512 */ +#else /* __AVX512F__ */ float reg_a[8], reg_b[8], reg_ret[8]; srsran_simd_f_storeu(reg_a, a); srsran_simd_f_storeu(reg_b, b); @@ -585,17 +585,17 @@ inline simd_f_t srsran_simd_f_interleave_low(simd_f_t a, simd_f_t b) } return srsran_simd_f_loadu(reg_ret); -#endif /* HAVE_AVX512 */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_interleave_high(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_permutex2var_ps( a, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f), b); -#else /* HAVE_AVX512 */ +#else /* __AVX512F__ */ float reg_a[SRSRAN_SIMD_F_SIZE], reg_b[SRSRAN_SIMD_F_SIZE], reg_ret[SRSRAN_SIMD_F_SIZE]; srsran_simd_f_storeu(reg_a, a); srsran_simd_f_storeu(reg_b, b); @@ -606,7 +606,7 @@ inline simd_f_t srsran_simd_f_interleave_high(simd_f_t a, simd_f_t b) } return srsran_simd_f_loadu(reg_ret); -#endif /* HAVE_AVX512 */ +#endif /* __AVX512F__ */ } inline void srsran_simd_f_fprintf(std::FILE* stream, simd_f_t a) @@ -626,7 +626,7 @@ inline void srsran_simd_f_fprintf(std::FILE* stream, simd_f_t a) #if SRSRAN_SIMD_CF_SIZE -#ifdef HAVE_NEON +#ifdef __ARM_NEON using simd_cf_t = float32x4x2_t; #else struct simd_cf_t { @@ -642,7 +642,7 @@ struct simd_cf_t { inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 in1 = _mm512_load_ps(reinterpret_cast(ptr)); __m512 in2 = _mm512_load_ps(reinterpret_cast(ptr + SRSRAN_SIMD_CF_SIZE / 2)); ret.re = _mm512_permutex2var_ps( @@ -653,32 +653,32 @@ inline simd_cf_t srsran_simd_cfi_load(const cf_t* ptr) in1, _mm512_setr_epi32(0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f), in2); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ __m256 in1 = _mm256_permute_ps(_mm256_load_ps(reinterpret_cast(ptr)), 0b11011000); __m256 in2 = _mm256_permute_ps(_mm256_load_ps(reinterpret_cast(ptr + 4)), 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ __m128 i1 = _mm_load_ps(reinterpret_cast(ptr)); __m128 i2 = _mm_load_ps(reinterpret_cast(ptr + 2)); ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2, 0, 2, 0)); ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3, 1, 3, 1)); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret = vld2q_f32(reinterpret_cast(ptr)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 in1 = _mm512_loadu_ps(reinterpret_cast(ptr)); __m512 in2 = _mm512_loadu_ps(reinterpret_cast(ptr + SRSRAN_SIMD_CF_SIZE / 2)); ret.re = _mm512_permutex2var_ps( @@ -689,81 +689,81 @@ inline simd_cf_t srsran_simd_cfi_loadu(const cf_t* ptr) in1, _mm512_setr_epi32(0x01, 0x03, 0x05, 0x07, 0x09, 0x0b, 0x0d, 0x0f, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1b, 0x1d, 0x1f), in2); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ __m256 in1 = _mm256_permute_ps(_mm256_loadu_ps(reinterpret_cast(ptr)), 0b11011000); __m256 in2 = _mm256_permute_ps(_mm256_loadu_ps(reinterpret_cast(ptr + 4)), 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ __m128 i1 = _mm_loadu_ps(reinterpret_cast(ptr)); __m128 i2 = _mm_loadu_ps(reinterpret_cast(ptr + 2)); ret.re = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(2, 0, 2, 0)); ret.im = _mm_shuffle_ps(i1, i2, _MM_SHUFFLE(3, 1, 3, 1)); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret = vld2q_f32(reinterpret_cast(ptr)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_load(const float* re, const float* im) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_load_ps(re); ret.im = _mm512_load_ps(im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_load_ps(re); ret.im = _mm256_load_ps(im); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_load_ps(re); ret.im = _mm_load_ps(im); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON ret.val[0] = vld1q_f32(re); ret.val[1] = vld1q_f32(im); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_loadu(const float* re, const float* im) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_loadu_ps(re); ret.im = _mm512_loadu_ps(im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_loadu_ps(re); ret.im = _mm256_loadu_ps(im); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_loadu_ps(re); ret.im = _mm_loadu_ps(im); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON ret.val[0] = vld1q_f32(re); ret.val[1] = vld1q_f32(im); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline void srsran_simd_cfi_store(cf_t* ptr, simd_cf_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 s1 = _mm512_permutex2var_ps( simdreg.re, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17), @@ -774,28 +774,28 @@ inline void srsran_simd_cfi_store(cf_t* ptr, simd_cf_t simdreg) simdreg.im); _mm512_store_ps(reinterpret_cast(ptr), s1); _mm512_store_ps(reinterpret_cast(ptr + 8), s2); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); _mm256_store_ps(reinterpret_cast(ptr), _mm256_unpacklo_ps(out1, out2)); _mm256_store_ps(reinterpret_cast(ptr + 4), _mm256_unpackhi_ps(out1, out2)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_store_ps(reinterpret_cast(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); _mm_store_ps(reinterpret_cast(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst2q_f32(reinterpret_cast(ptr), simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_cfi_storeu(cf_t* ptr, simd_cf_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 s1 = _mm512_permutex2var_ps( simdreg.re, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17), @@ -806,232 +806,232 @@ inline void srsran_simd_cfi_storeu(cf_t* ptr, simd_cf_t simdreg) simdreg.im); _mm512_storeu_ps(reinterpret_cast(ptr), s1); _mm512_storeu_ps(reinterpret_cast(ptr + 8), s2); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ __m256 out1 = _mm256_permute_ps(simdreg.re, 0b11011000); __m256 out2 = _mm256_permute_ps(simdreg.im, 0b11011000); _mm256_storeu_ps(reinterpret_cast(ptr), _mm256_unpacklo_ps(out1, out2)); _mm256_storeu_ps(reinterpret_cast(ptr + 4), _mm256_unpackhi_ps(out1, out2)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_storeu_ps(reinterpret_cast(ptr), _mm_unpacklo_ps(simdreg.re, simdreg.im)); _mm_storeu_ps(reinterpret_cast(ptr + 2), _mm_unpackhi_ps(simdreg.re, simdreg.im)); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst2q_f32(reinterpret_cast(ptr), simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_cf_store(float* re, float* im, simd_cf_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_store_ps(re, simdreg.re); _mm512_store_ps(im, simdreg.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_store_ps(re, simdreg.re); _mm256_store_ps(im, simdreg.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_SSE +#else /* __AVX512F__ */ +#ifdef __SSE4_1__ _mm_store_ps(re, simdreg.re); _mm_store_ps(im, simdreg.im); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst1q_f32(re, simdreg.val[0]); vst1q_f32(im, simdreg.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_cf_storeu(float* re, float* im, simd_cf_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_storeu_ps(re, simdreg.re); _mm512_storeu_ps(im, simdreg.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_storeu_ps(re, simdreg.re); _mm256_storeu_ps(im, simdreg.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_SSE +#else /* __AVX512F__ */ +#ifdef __SSE4_1__ _mm_storeu_ps(re, simdreg.re); _mm_storeu_ps(im, simdreg.im); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst1q_f32(re, simdreg.val[0]); vst1q_f32(im, simdreg.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_cf_re(simd_cf_t in) { -#ifdef HAVE_NEON +#ifdef __ARM_NEON simd_f_t out = in.val[0]; #else simd_f_t out = in.re; -#endif /* HAVE_NEON */ -#ifndef HAVE_AVX512 -#ifdef HAVE_AVX2 +#endif /* __ARM_NEON */ +#ifndef __AVX512F__ +#ifdef __AVX2__ // Permute for AVX registers (reorders data across 128-bit registers). const __m256i idx = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7); out = _mm256_permutevar8x32_ps(out, idx); -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return out; } inline simd_f_t srsran_simd_cf_im(simd_cf_t in) { -#ifdef HAVE_NEON +#ifdef __ARM_NEON simd_f_t out = in.val[1]; #else simd_f_t out = in.im; -#endif /* HAVE_NEON */ -#ifndef HAVE_AVX512 -#ifdef HAVE_AVX2 +#endif /* __ARM_NEON */ +#ifndef __AVX512F__ +#ifdef __AVX2__ // Permute for AVX registers (reorders data across 128-bit registers). const __m256i idx = _mm256_setr_epi32(0, 2, 4, 6, 1, 3, 5, 7); out = _mm256_permutevar8x32_ps(out, idx); -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return out; } inline simd_cf_t srsran_simd_cf_set1(cf_t x) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_set1_ps(x.real()); ret.im = _mm512_set1_ps(x.imag()); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_set1_ps(x.real()); ret.im = _mm256_set1_ps(x.imag()); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_set1_ps(x.real()); ret.im = _mm_set1_ps(x.imag()); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON ret.val[0] = vdupq_n_f32(x.real()); ret.val[1] = vdupq_n_f32(x.imag()); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_prod(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_sub_ps(_mm512_mul_ps(a.re, b.re), _mm512_mul_ps(a.im, b.im)); ret.im = _mm512_add_ps(_mm512_mul_ps(a.re, b.im), _mm512_mul_ps(a.im, b.re)); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 -#ifdef HAVE_FMA +#else /* __AVX512F__ */ +#ifdef __AVX2__ +#ifdef __FMA__ ret.re = _mm256_fmsub_ps(a.re, b.re, _mm256_mul_ps(a.im, b.im)); ret.im = _mm256_fmadd_ps(a.re, b.im, _mm256_mul_ps(a.im, b.re)); -#else /* HAVE_FMA */ +#else /* __FMA__ */ ret.re = _mm256_sub_ps(_mm256_mul_ps(a.re, b.re), _mm256_mul_ps(a.im, b.im)); ret.im = _mm256_add_ps(_mm256_mul_ps(a.re, b.im), _mm256_mul_ps(a.im, b.re)); -#endif /* HAVE_FMA */ +#endif /* __FMA__ */ #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_sub_ps(_mm_mul_ps(a.re, b.re), _mm_mul_ps(a.im, b.im)); ret.im = _mm_add_ps(_mm_mul_ps(a.re, b.im), _mm_mul_ps(a.im, b.re)); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret.val[0] = vsubq_f32(vmulq_f32(a.val[0], b.val[0]), vmulq_f32(a.val[1], b.val[1])); ret.val[1] = vaddq_f32(vmulq_f32(a.val[0], b.val[1]), vmulq_f32(a.val[1], b.val[0])); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_conjprod(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_add_ps(_mm512_mul_ps(a.re, b.re), _mm512_mul_ps(a.im, b.im)); ret.im = _mm512_sub_ps(_mm512_mul_ps(a.im, b.re), _mm512_mul_ps(a.re, b.im)); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_add_ps(_mm256_mul_ps(a.re, b.re), _mm256_mul_ps(a.im, b.im)); ret.im = _mm256_sub_ps(_mm256_mul_ps(a.im, b.re), _mm256_mul_ps(a.re, b.im)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_add_ps(_mm_mul_ps(a.re, b.re), _mm_mul_ps(a.im, b.im)); ret.im = _mm_sub_ps(_mm_mul_ps(a.im, b.re), _mm_mul_ps(a.re, b.im)); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret.val[0] = vaddq_f32(vmulq_f32(a.val[0], b.val[0]), vmulq_f32(a.val[1], b.val[1])); ret.val[1] = vsubq_f32(vmulq_f32(a.val[1], b.val[0]), vmulq_f32(a.val[0], b.val[1])); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_add(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_add_ps(a.re, b.re); ret.im = _mm512_add_ps(a.im, b.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_add_ps(a.re, b.re); ret.im = _mm256_add_ps(a.im, b.im); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_add_ps(a.re, b.re); ret.im = _mm_add_ps(a.im, b.im); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON ret.val[0] = vaddq_f32(a.val[0], b.val[0]); ret.val[1] = vaddq_f32(a.val[1], b.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_sub(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_sub_ps(a.re, b.re); ret.im = _mm512_sub_ps(a.im, b.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_sub_ps(a.re, b.re); ret.im = _mm256_sub_ps(a.im, b.im); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_sub_ps(a.re, b.re); ret.im = _mm_sub_ps(a.im, b.im); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON ret.val[0] = vsubq_f32(a.val[0], b.val[0]); ret.val[1] = vsubq_f32(a.val[1], b.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } @@ -1044,33 +1044,33 @@ inline simd_f_t srsran_simd_cf_norm_sq(simd_cf_t a) inline simd_cf_t srsran_simd_cf_mul(simd_cf_t a, simd_f_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_mul_ps(a.re, b); ret.im = _mm512_mul_ps(a.im, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ b = _mm256_permutevar8x32_ps(b, _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7)); ret.re = _mm256_mul_ps(a.re, b); ret.im = _mm256_mul_ps(a.im, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_mul_ps(a.re, b); ret.im = _mm_mul_ps(a.im, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON ret.val[0] = vmulq_f32(a.val[0], b); ret.val[1] = vmulq_f32(a.val[1], b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ simd_f_t a2re = _mm512_mul_ps(a.re, a.re); simd_f_t a2im = _mm512_mul_ps(a.im, a.im); simd_f_t mod2 = _mm512_add_ps(a2re, a2im); @@ -1078,8 +1078,8 @@ inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) simd_f_t neg_a_im = _mm512_xor_ps(_mm512_set1_ps(-0.0f), a.im); ret.re = _mm512_mul_ps(a.re, rcp); ret.im = _mm512_mul_ps(neg_a_im, rcp); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ simd_f_t a2re = _mm256_mul_ps(a.re, a.re); simd_f_t a2im = _mm256_mul_ps(a.im, a.im); simd_f_t mod2 = _mm256_add_ps(a2re, a2im); @@ -1087,8 +1087,8 @@ inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) simd_f_t neg_a_im = _mm256_xor_ps(_mm256_set1_ps(-0.0f), a.im); ret.re = _mm256_mul_ps(a.re, rcp); ret.im = _mm256_mul_ps(neg_a_im, rcp); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ simd_f_t a2re = _mm_mul_ps(a.re, a.re); simd_f_t a2im = _mm_mul_ps(a.im, a.im); simd_f_t mod2 = _mm_add_ps(a2re, a2im); @@ -1096,8 +1096,8 @@ inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) simd_f_t neg_a_im = _mm_xor_ps(_mm_set1_ps(-0.0f), a.im); ret.re = _mm_mul_ps(a.re, rcp); ret.im = _mm_mul_ps(neg_a_im, rcp); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON simd_f_t a2re = vmulq_f32(a.val[0], a.val[0]); simd_f_t a2im = vmulq_f32(a.val[1], a.val[1]); simd_f_t mod2 = vaddq_f32(a2re, a2im); @@ -1105,99 +1105,99 @@ inline simd_cf_t srsran_simd_cf_rcp(simd_cf_t a) simd_f_t neg_a_im = vnegq_f32(a.val[1]); ret.val[0] = vmulq_f32(a.val[0], rcp); ret.val[1] = vmulq_f32(neg_a_im, rcp); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_neg(simd_cf_t a) { simd_cf_t ret; -#if HAVE_NEON +#if __ARM_NEON ret.val[0] = srsran_simd_f_neg(a.val[0]); ret.val[1] = srsran_simd_f_neg(a.val[1]); -#else /* HAVE_NEON */ +#else /* __ARM_NEON */ ret.re = srsran_simd_f_neg(a.re); ret.im = srsran_simd_f_neg(a.im); -#endif /* HAVE_NEON */ +#endif /* __ARM_NEON */ return ret; } inline simd_cf_t srsran_simd_cf_neg_mask(simd_cf_t a, simd_f_t mask) { simd_cf_t ret; -#ifndef HAVE_AVX512 -#ifdef HAVE_AVX2 +#ifndef __AVX512F__ +#ifdef __AVX2__ mask = _mm256_permutevar8x32_ps(mask, _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7)); -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ -#if HAVE_NEON +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ +#if __ARM_NEON ret.val[0] = srsran_simd_f_neg_mask(a.val[0], mask); ret.val[1] = srsran_simd_f_neg_mask(a.val[1], mask); -#else /* HAVE_NEON */ +#else /* __ARM_NEON */ ret.re = srsran_simd_f_neg_mask(a.re, mask); ret.im = srsran_simd_f_neg_mask(a.im, mask); -#endif /* HAVE_NEON */ +#endif /* __ARM_NEON */ return ret; } inline simd_cf_t srsran_simd_cf_conj(simd_cf_t a) { simd_cf_t ret; -#if HAVE_NEON +#if __ARM_NEON ret.val[0] = a.val[0]; ret.val[1] = srsran_simd_f_neg(a.val[1]); -#else /* HAVE_NEON */ +#else /* __ARM_NEON */ ret.re = a.re; ret.im = srsran_simd_f_neg(a.im); -#endif /* HAVE_NEON */ +#endif /* __ARM_NEON */ return ret; } inline simd_cf_t srsran_simd_cf_mulj(simd_cf_t a) { simd_cf_t ret; -#if HAVE_NEON +#if __ARM_NEON ret.val[0] = srsran_simd_f_neg(a.val[1]); ret.val[1] = a.val[0]; -#else /* HAVE_NEON */ +#else /* __ARM_NEON */ ret.re = srsran_simd_f_neg(a.im); ret.im = a.re; -#endif /* HAVE_NEON */ +#endif /* __ARM_NEON */ return ret; } inline simd_cf_t srsran_simd_cf_zero() { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_setzero_ps(); ret.im = _mm512_setzero_ps(); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ ret.re = _mm256_setzero_ps(); ret.im = _mm256_setzero_ps(); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ ret.re = _mm_setzero_ps(); ret.im = _mm_setzero_ps(); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON ret.val[0] = vdupq_n_f32(0); ret.val[1] = vdupq_n_f32(0); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_interleave_low(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_permutex2var_ps( a.re, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17), @@ -1206,8 +1206,8 @@ inline simd_cf_t srsran_simd_cf_interleave_low(simd_cf_t a, simd_cf_t b) a.im, _mm512_setr_epi32(0x00, 0x10, 0x01, 0x11, 0x02, 0x12, 0x03, 0x13, 0x04, 0x14, 0x05, 0x15, 0x06, 0x16, 0x07, 0x17), b.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_SSE +#else /* __AVX512F__ */ +#ifdef __SSE4_1__ // Interleave real and imaginary parts of a. __m256 temp_a0 = _mm256_permute_ps(a.re, 0b11011000); __m256 temp_a1 = _mm256_permute_ps(a.im, 0b11011000); @@ -1233,8 +1233,8 @@ inline simd_cf_t srsran_simd_cf_interleave_low(simd_cf_t a, simd_cf_t b) __m256 in2 = _mm256_permute_ps(temp_b, 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON cf_t reg_a[SRSRAN_SIMD_CF_SIZE], reg_b[SRSRAN_SIMD_CF_SIZE], reg_ret[SRSRAN_SIMD_CF_SIZE]; srsran_simd_cfi_storeu(reg_a, a); srsran_simd_cfi_storeu(reg_b, b); @@ -1245,16 +1245,16 @@ inline simd_cf_t srsran_simd_cf_interleave_low(simd_cf_t a, simd_cf_t b) } ret = srsran_simd_cfi_loadu(reg_ret); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_cf_t srsran_simd_cf_interleave_high(simd_cf_t a, simd_cf_t b) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_permutex2var_ps( a.re, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f), @@ -1263,8 +1263,8 @@ inline simd_cf_t srsran_simd_cf_interleave_high(simd_cf_t a, simd_cf_t b) a.im, _mm512_setr_epi32(0x08, 0x18, 0x09, 0x19, 0x0a, 0x1a, 0x0b, 0x1b, 0x0c, 0x1c, 0x0d, 0x1d, 0x0e, 0x1e, 0x0f, 0x1f), b.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_SSE +#else /* __AVX512F__ */ +#ifdef __SSE4_1__ // Interleave real and imaginary parts of a. __m256 temp_a0 = _mm256_permute_ps(a.re, 0b11011000); __m256 temp_a1 = _mm256_permute_ps(a.im, 0b11011000); @@ -1290,8 +1290,8 @@ inline simd_cf_t srsran_simd_cf_interleave_high(simd_cf_t a, simd_cf_t b) __m256 in2 = _mm256_permute_ps(temp_b, 0b11011000); ret.re = _mm256_unpacklo_ps(in1, in2); ret.im = _mm256_unpackhi_ps(in1, in2); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON cf_t reg_a[SRSRAN_SIMD_CF_SIZE], reg_b[SRSRAN_SIMD_CF_SIZE], reg_ret[SRSRAN_SIMD_CF_SIZE]; srsran_simd_cfi_storeu(reg_a, a); srsran_simd_cfi_storeu(reg_b, b); @@ -1302,9 +1302,9 @@ inline simd_cf_t srsran_simd_cf_interleave_high(simd_cf_t a, simd_cf_t b) } ret = srsran_simd_cfi_loadu(reg_ret); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX512F__ */ return ret; } @@ -1325,240 +1325,240 @@ inline void srsran_simd_cf_fprintf(std::FILE* stream, simd_cf_t a) #if SRSRAN_SIMD_I_SIZE -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ using simd_i_t = __m512i; using simd_sel_t = __mmask16; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ using simd_i_t = __m256i; using simd_sel_t = __m256; -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ using simd_i_t = __m128i; using simd_sel_t = __m128; -#else /* HAVE_AVX2 */ -#ifdef HAVE_NEON +#else /* __AVX2__ */ +#ifdef __ARM_NEON using simd_i_t = int32x4_t; using simd_sel_t = uint32x4_t; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ inline simd_i_t srsran_simd_i_load(const int32_t* x) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_load_epi32(reinterpret_cast(x)); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_load_si256(reinterpret_cast(x)); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_load_si128(reinterpret_cast(x)); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vld1q_s32(reinterpret_cast(x)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_i_store(int32_t* x, simd_i_t reg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_store_epi32(reinterpret_cast<__m512i*>(x), reg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_store_si256(reinterpret_cast<__m256i*>(x), reg); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ _mm_store_si128(reinterpret_cast<__m128i*>(x), reg); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON vst1q_s32(reinterpret_cast(x), reg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_i_t srsran_simd_i_set1(int x) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_set1_epi32(x); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_set1_epi32(x); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_set1_epi32(x); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vdupq_n_s32(x); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_i_t srsran_simd_i_add(simd_i_t a, simd_i_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_add_epi32(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_add_epi32(a, b); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_add_epi32(a, b); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vaddq_s32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_i_t srsran_simd_i_mul(simd_i_t a, simd_i_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_mullo_epi32(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_mullo_epi32(a, b); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_mul_epi32(a, b); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vmulq_s32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_i_t srsran_simd_i_and(simd_i_t a, simd_i_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_and_si512(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_and_si256(a, b); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_and_si128(a, b); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vandq_s32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_sel_t srsran_simd_sel_and(simd_sel_t a, simd_sel_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _kand_mask16(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_and_ps(a, b); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_and_ps(a, b); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON return vandq_u32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_sel_t srsran_simd_f_max(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_cmp_ps_mask(a, b, _CMP_GT_OS); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_cmp_ps(a, b, _CMP_GT_OS); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return (simd_sel_t)_mm_cmpgt_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vcgtq_f32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_sel_t srsran_simd_f_min(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_cmp_ps_mask(a, b, _CMP_LT_OS); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_cmp_ps(a, b, _CMP_LT_OS); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return (simd_sel_t)_mm_cmplt_ps(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vcltq_f32(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_f_t srsran_simd_f_select(simd_f_t a, simd_f_t b, simd_sel_t selector) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_mask_blend_ps(selector, (__m512)a, (__m512)b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_blendv_ps(a, b, selector); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return _mm_blendv_ps(a, b, selector); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vbslq_f32(selector, b, a); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_cf_t srsran_simd_cf_select(simd_cf_t a, simd_cf_t b, simd_sel_t selector) { simd_cf_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ ret.re = _mm512_mask_blend_ps(selector, a.re, b.re); ret.im = _mm512_mask_blend_ps(selector, a.im, b.im); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ /* Permute for AVX registers (mis SSE registers) */ selector = _mm256_permutevar8x32_ps(selector, _mm256_setr_epi32(0, 4, 1, 5, 2, 6, 3, 7)); ret.re = _mm256_blendv_ps(a.re, b.re, selector); ret.im = _mm256_blendv_ps(a.im, b.im, selector); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_blendv_ps(a.re, b.re, selector); ret.im = _mm_blendv_ps(a.im, b.im, selector); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON ret.val[0] = vbslq_f32(selector, b.val[0], a.val[0]); ret.val[1] = vbslq_f32(selector, b.val[1], a.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } @@ -1574,159 +1574,159 @@ inline simd_f_t srsran_simd_f_add_sel(simd_f_t a, simd_f_t b, simd_sel_t sel) inline simd_i_t srsran_simd_i_select(simd_i_t a, simd_i_t b, simd_sel_t selector) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return (__m512i)_mm512_mask_blend_ps(selector, (__m512)a, (__m512)b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return (__m256i)_mm256_blendv_ps((__m256)a, (__m256)b, selector); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ return (__m128i)_mm_blendv_ps((__m128)a, (__m128)b, selector); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vbslq_s32(selector, b, a); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } #endif /* SRSRAN_SIMD_I_SIZE */ #if SRSRAN_SIMD_S_SIZE -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ using simd_s_t = __m512i; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ using simd_s_t = __m256i; -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ using simd_s_t = __m128i; -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON using simd_s_t = int16x8_t; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ inline simd_s_t srsran_simd_s_load(const int16_t* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_load_si512(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_load_si256(reinterpret_cast(ptr)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_load_si128(reinterpret_cast(ptr)); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_s16(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_loadu(const int16_t* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_loadu_si512(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_loadu_si256(reinterpret_cast(ptr)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_loadu_si128(reinterpret_cast(ptr)); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_s16(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_s_store(int16_t* ptr, simd_s_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_store_si512(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_store_si128(reinterpret_cast<__m128i*>(ptr), simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_s16(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_s_storeu(int16_t* ptr, simd_s_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_storeu_si512(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_s16(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_zero() { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_setzero_si512(); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_setzero_si256(); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_setzero_si128(); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vdupq_n_s16(0); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_mul(simd_s_t a, simd_s_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_mullo_epi16(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_mullo_epi16(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_mullo_epi16(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vmulq_s16(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m256i a0 = _mm512_extracti64x4_epi64(a, 0); __m256i a1 = _mm512_extracti64x4_epi64(a, 1); __m256i b0 = _mm512_extracti64x4_epi64(b, 0); @@ -1734,14 +1734,14 @@ inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) __m256i r0 = _mm256_sign_epi16(a0, b0); __m256i r1 = _mm256_sign_epi16(a1, b1); return _mm512_inserti64x4(_mm512_broadcast_i64x4(r0), r1, 1); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_sign_epi16(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_sign_epi16(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON // Taken and modified from sse2neon.h licensed under MIT. // Source: https://github.com/DLTcollab/sse2neon int16x8_t zero = vdupq_n_s16(0); @@ -1757,80 +1757,80 @@ inline simd_s_t srsran_simd_s_neg(simd_s_t a, simd_s_t b) // res = masked & (~zeroMask) int16x8_t res = vbicq_s16(masked, zeroMask); return res; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_add(simd_s_t a, simd_s_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_add_epi16(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_add_epi16(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_add_epi16(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vaddq_s16(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_s_t srsran_simd_s_sub(simd_s_t a, simd_s_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_sub_epi16(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_sub_epi16(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_sub_epi16(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vsubq_s16(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } #endif /* SRSRAN_SIMD_S_SIZE */ #if SRSRAN_SIMD_C16_SIZE -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ struct simd_c16_t { __m512i re; __m512i im; }; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ struct simd_c16_t { __m256i re; __m256i im; }; #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ struct simd_c16_t { __m128i re; __m128i im; }; #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON struct simd_c16_t { int16x8x2_t m128; }; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ /// /// Fixed point precision (16-bit) functions. @@ -1839,211 +1839,211 @@ struct simd_c16_t { inline simd_c16_t srsran_simd_c16i_load(const c16_t* ptr) { simd_c16_t ret; -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512i in1 = _mm512_load_si512(reinterpret_cast(ptr)); __m512i in2 = _mm512_load_si512(reinterpret_cast(ptr + 8)); ret.re = _mm512_mask_blend_epi16( 0xaaaaaaaa, in1, _mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in2, 0b10100000), 0b10100000)); ret.im = _mm512_mask_blend_epi16( 0xaaaaaaaa, _mm512_shufflelo_epi16(_mm512_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2); -#else /* HAVE_AVX2 */ -#ifdef HAVE_AVX2 +#else /* __AVX2__ */ +#ifdef __AVX2__ __m256i in1 = _mm256_load_si256(reinterpret_cast(ptr)); __m256i in2 = _mm256_load_si256(reinterpret_cast(ptr + 8)); ret.re = _mm256_blend_epi16(in1, _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); ret.im = _mm256_blend_epi16(_mm256_shufflelo_epi16(_mm256_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2, 0b10101010); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ __m128i in1 = _mm_load_si128(reinterpret_cast(ptr)); __m128i in2 = _mm_load_si128(reinterpret_cast(ptr + 8)); ret.re = _mm_blend_epi16(in1, _mm_shufflelo_epi16(_mm_shufflehi_epi16(in2, 0b10100000), 0b10100000), 0b10101010); ret.im = _mm_blend_epi16(_mm_shufflelo_epi16(_mm_shufflehi_epi16(in1, 0b11110101), 0b11110101), in2, 0b10101010); -#else /* HAVE_SSE*/ -#ifdef HAVE_NEON +#else /* __SSE4_1__*/ +#ifdef __ARM_NEON ret.m128 = vld2q_s16(reinterpret_cast(ptr)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ return ret; } inline simd_c16_t srsran_simd_c16_load(const int16_t* re, const int16_t* im) { simd_c16_t ret; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ ret.re = _mm256_load_si256(reinterpret_cast(re)); ret.im = _mm256_load_si256(reinterpret_cast(im)); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_load_si128(reinterpret_cast(re)); ret.im = _mm_load_si128(reinterpret_cast(im)); -#else /* HAVE_SSE*/ -#ifdef HAVE_NEON +#else /* __SSE4_1__*/ +#ifdef __ARM_NEON ret.m128.val[0] = vld1q_s16(re); ret.m128.val[1] = vld1q_s16(im); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ return ret; } inline simd_c16_t srsran_simd_c16_loadu(const int16_t* re, const int16_t* im) { simd_c16_t ret; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ ret.re = _mm256_loadu_si256(reinterpret_cast(re)); ret.im = _mm256_loadu_si256(reinterpret_cast(im)); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_loadu_si128(reinterpret_cast(re)); ret.im = _mm_loadu_si128(reinterpret_cast(im)); -#else /* HAVE_SSE*/ -#ifdef HAVE_NEON +#else /* __SSE4_1__*/ +#ifdef __ARM_NEON ret.m128.val[0] = vld1q_s16(re); ret.m128.val[1] = vld1q_s16(im); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ return ret; } inline void srsran_simd_c16i_store(c16_t* ptr, simd_c16_t simdreg) { -#ifdef HAVE_AVX2 +#ifdef __AVX2__ __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), _mm256_blend_epi16(simdreg.re, im_sw, 0b10101010)); _mm256_store_si256(reinterpret_cast<__m256i*>(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im, 0b10101010)); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); _mm_store_si128(reinterpret_cast<__m128i*>(ptr), _mm_blend_epi16(simdreg.re, im_sw, 0b10101010)); _mm_store_si128(reinterpret_cast<__m128i*>(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im, 0b10101010)); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst2q_s16(reinterpret_cast(ptr), simdreg.m128); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ } inline void srsran_simd_c16i_storeu(c16_t* ptr, simd_c16_t simdreg) { -#ifdef HAVE_AVX2 +#ifdef __AVX2__ __m256i re_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); __m256i im_sw = _mm256_shufflelo_epi16(_mm256_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), _mm256_blend_epi16(simdreg.re, im_sw, 0b10101010)); _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr + 8), _mm256_blend_epi16(re_sw, simdreg.im, 0b10101010)); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ __m128i re_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.re, 0b10110001), 0b10110001); __m128i im_sw = _mm_shufflelo_epi16(_mm_shufflehi_epi16(simdreg.im, 0b10110001), 0b10110001); _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), _mm_blend_epi16(simdreg.re, im_sw, 0b10101010)); _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr + 8), _mm_blend_epi16(re_sw, simdreg.im, 0b10101010)); -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON vst2q_s16(reinterpret_cast(ptr), simdreg.m128); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ } inline void srsran_simd_c16_store(int16_t* re, int16_t* im, simd_c16_t simdreg) { -#ifdef HAVE_AVX2 +#ifdef __AVX2__ _mm256_store_si256(reinterpret_cast<__m256i*>(re), simdreg.re); _mm256_store_si256(reinterpret_cast<__m256i*>(im), simdreg.im); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ _mm_store_si128(reinterpret_cast<__m128i*>(re), simdreg.re); _mm_store_si128(reinterpret_cast<__m128i*>(im), simdreg.im); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON vst1q_s16(re, simdreg.m128.val[0]); vst1q_s16(im, simdreg.m128.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ } inline void srsran_simd_c16_storeu(int16_t* re, int16_t* im, simd_c16_t simdreg) { -#ifdef HAVE_AVX2 +#ifdef __AVX2__ _mm256_storeu_si256(reinterpret_cast<__m256i*>(re), simdreg.re); _mm256_storeu_si256(reinterpret_cast<__m256i*>(im), simdreg.im); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ _mm_storeu_si128(reinterpret_cast<__m128i*>(re), simdreg.re); _mm_storeu_si128(reinterpret_cast<__m128i*>(im), simdreg.im); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON vst1q_s16(re, simdreg.m128.val[0]); vst1q_s16(im, simdreg.m128.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ } inline simd_c16_t srsran_simd_c16_prod(simd_c16_t a, simd_c16_t b) { simd_c16_t ret; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ ret.re = _mm256_sub_epi16(_mm256_mulhrs_epi16(a.re, _mm256_slli_epi16(b.re, 1)), _mm256_mulhrs_epi16(a.im, _mm256_slli_epi16(b.im, 1))); ret.im = _mm256_add_epi16(_mm256_mulhrs_epi16(a.re, _mm256_slli_epi16(b.im, 1)), _mm256_mulhrs_epi16(a.im, _mm256_slli_epi16(b.re, 1))); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_sub_epi16(_mm_mulhrs_epi16(a.re, _mm_slli_epi16(b.re, 1)), _mm_mulhrs_epi16(a.im, _mm_slli_epi16(b.im, 1))); ret.im = _mm_add_epi16(_mm_mulhrs_epi16(a.re, _mm_slli_epi16(b.im, 1)), _mm_mulhrs_epi16(a.im, _mm_slli_epi16(b.re, 1))); -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ return ret; } inline simd_c16_t srsran_simd_c16_add(simd_c16_t a, simd_c16_t b) { simd_c16_t ret; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ ret.re = _mm256_add_epi16(a.re, b.re); ret.im = _mm256_add_epi16(a.im, b.im); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_add_epi16(a.re, b.re); ret.im = _mm_add_epi16(a.im, b.im); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret.m128.val[0] = vaddq_s16(a.m128.val[0], a.m128.val[0]); ret.m128.val[1] = vaddq_s16(a.m128.val[1], a.m128.val[1]); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ return ret; } inline simd_c16_t srsran_simd_c16_zero() { simd_c16_t ret; -#ifdef HAVE_AVX2 +#ifdef __AVX2__ ret.re = _mm256_setzero_si256(); ret.im = _mm256_setzero_si256(); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ ret.re = _mm_setzero_si128(); ret.im = _mm_setzero_si128(); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON ret.m128.val[0] = vdupq_n_s16(0); ret.m128.val[1] = vdupq_n_s16(0); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ return ret; } @@ -2053,7 +2053,7 @@ inline simd_c16_t srsran_simd_c16_zero() inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m512 aa = _mm512_permutex2var_ps( a, _mm512_setr_epi32(0x00, 0x01, 0x02, 0x03, 0x08, 0x09, 0x0a, 0x0b, 0x10, 0x11, 0x12, 0x13, 0x18, 0x19, 0x1a, 0x1b), @@ -2065,29 +2065,29 @@ inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) __m512i ai = _mm512_cvt_roundps_epi32(aa, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); __m512i bi = _mm512_cvt_roundps_epi32(bb, _MM_FROUND_TO_NEAREST_INT | _MM_FROUND_NO_EXC); return _mm512_packs_epi32(ai, bi); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ __m256 aa = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x20), _MM_FROUND_NINT); __m256 bb = _mm256_round_ps(_mm256_permute2f128_ps(a, b, 0x31), _MM_FROUND_NINT); __m256i ai = _mm256_cvtps_epi32(aa); __m256i bi = _mm256_cvtps_epi32(bb); return _mm256_packs_epi32(ai, bi); #else -#ifdef HAVE_SSE +#ifdef __SSE4_1__ __m128 aa = _mm_round_ps(a, _MM_FROUND_NINT); __m128 bb = _mm_round_ps(b, _MM_FROUND_NINT); __m128i ai = _mm_cvtps_epi32(aa); __m128i bi = _mm_cvtps_epi32(bb); return _mm_packs_epi32(ai, bi); #else -#ifdef HAVE_NEON +#ifdef __ARM_NEON int32x4_t ai = vcvtnq_s32_f32(a); int32x4_t bi = vcvtnq_s32_f32(b); return (simd_s_t)vcombine_s16(vqmovn_s32(ai), vqmovn_s32(bi)); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } #endif /* SRSRAN_SIMD_F_SIZE && SRSRAN_SIMD_C16_SIZE */ @@ -2098,158 +2098,158 @@ inline simd_s_t srsran_simd_convert_2f_s(simd_f_t a, simd_f_t b) /// Data types. /// -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ using simd_b_t = __m512i; -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ using simd_b_t = __m256i; -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ using simd_b_t = __m128i; -#else /* HAVE_NEON */ -#ifdef HAVE_NEON +#else /* __ARM_NEON */ +#ifdef __ARM_NEON using simd_b_t = int8x16_t; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ inline simd_b_t srsran_simd_b_load(const int8_t* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_load_si512(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_load_si256(reinterpret_cast(ptr)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_load_si128(reinterpret_cast(ptr)); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_s8(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_b_t srsran_simd_b_loadu(const int8_t* ptr) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_loadu_si512(ptr); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_loadu_si256(reinterpret_cast(ptr)); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_loadu_si128(reinterpret_cast(ptr)); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vld1q_s8(ptr); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_b_store(int8_t* ptr, simd_b_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_store_si512(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_store_si256(reinterpret_cast<__m256i*>(ptr), simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_store_si128(reinterpret_cast<__m128i*>(ptr), simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_s8(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline void srsran_simd_b_storeu(int8_t* ptr, simd_b_t simdreg) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ _mm512_storeu_si512(ptr, simdreg); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ _mm256_storeu_si256(reinterpret_cast<__m256i*>(ptr), simdreg); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ _mm_storeu_si128(reinterpret_cast<__m128i*>(ptr), simdreg); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON vst1q_s8(ptr, simdreg); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_b_t srsran_simd_b_xor(simd_b_t a, simd_b_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_xor_epi32(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_xor_si256(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_xor_si128(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return veorq_s8(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_b_t srsran_simd_b_add(simd_b_t a, simd_b_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_adds_epi8(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_adds_epi8(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_adds_epi8(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vqaddq_s8(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_b_t srsran_simd_b_sub(simd_b_t a, simd_b_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ return _mm512_subs_epi8(a, b); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_subs_epi8(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_subs_epi8(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON return vqsubq_s8(a, b); -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) { -#ifdef HAVE_AVX512 +#ifdef __AVX512F__ __m256i a0 = _mm512_extracti64x4_epi64(a, 0); __m256i a1 = _mm512_extracti64x4_epi64(a, 1); __m256i b0 = _mm512_extracti64x4_epi64(b, 0); @@ -2257,14 +2257,14 @@ inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) __m256i r0 = _mm256_sign_epi8(a0, b0); __m256i r1 = _mm256_sign_epi8(a1, b1); return _mm512_inserti64x4(_mm512_broadcast_i64x4(r0), r1, 1); -#else /* HAVE_AVX512 */ -#ifdef HAVE_AVX2 +#else /* __AVX512F__ */ +#ifdef __AVX2__ return _mm256_sign_epi8(a, b); -#else /* HAVE_AVX2 */ -#ifdef HAVE_SSE +#else /* __AVX2__ */ +#ifdef __SSE4_1__ return _mm_sign_epi8(a, b); -#else /* HAVE_SSE */ -#ifdef HAVE_NEON +#else /* __SSE4_1__ */ +#ifdef __ARM_NEON // Taken and modified from sse2neon.h licensed under MIT. // Source: https://github.com/DLTcollab/sse2neon int8x16_t zero = vdupq_n_s8(0); @@ -2280,10 +2280,10 @@ inline simd_b_t srsran_simd_b_neg(simd_b_t a, simd_b_t b) // res = masked & (~zeroMask) int8x16_t res = vbicq_s8(masked, zeroMask); return res; -#endif /* HAVE_NEON */ -#endif /* HAVE_SSE */ -#endif /* HAVE_AVX2 */ -#endif /* HAVE_AVX512 */ +#endif /* __ARM_NEON */ +#endif /* __SSE4_1__ */ +#endif /* __AVX2__ */ +#endif /* __AVX512F__ */ } #endif /* SRSRAN_SIMD_B_SIZE */ diff --git a/lib/support/byte_buffer.cpp b/lib/support/byte_buffer.cpp index 83f2ee07c4..381b6e86e4 100644 --- a/lib/support/byte_buffer.cpp +++ b/lib/support/byte_buffer.cpp @@ -580,17 +580,25 @@ void byte_buffer::warn_alloc_failure() logger.warning("POOL: Failure to allocate byte buffer segment"); } -byte_buffer srsran::make_byte_buffer(const std::string& hex_str) +expected srsran::make_byte_buffer(const std::string& hex_str) { - srsran_assert(hex_str.size() % 2 == 0, "The number of hex digits must be even"); + if (hex_str.size() % 2 != 0) { + // Failed to parse hex string. + return default_error_t{}; + } byte_buffer ret{byte_buffer::fallback_allocation_tag{}}; for (size_t i = 0, e = hex_str.size(); i != e; i += 2) { uint8_t val; - std::sscanf(hex_str.data() + i, "%02hhX", &val); + if (std::sscanf(hex_str.data() + i, "%02hhX", &val) <= 0) { + // Failed to parse Hex digit. + return default_error_t{}; + } bool success = ret.append(val); - srsran_sanity_check(success, "Failed to append byte to byte_buffer with fallback allocator"); - (void)success; + if (not success) { + // Note: This shouldn't generally happen as we use a fallback allocator. + return default_error_t{}; + } } return ret; } diff --git a/lib/support/cpu_architecture_info.cpp b/lib/support/cpu_architecture_info.cpp index ce1982dabe..2b6f5eb443 100644 --- a/lib/support/cpu_architecture_info.cpp +++ b/lib/support/cpu_architecture_info.cpp @@ -65,8 +65,11 @@ const cpu_architecture_info::cpu_description cpu_architecture_info::cpu_desc = cpu_architecture_info::cpu_description cpu_architecture_info::discover_cpu_architecture() { - // Clean-up cgroups possibly left from a previous run. - cleanup_cgroups(); + // Check if custom cgroups exist in the system (possibly left from a previous run). + auto detected_cgroups = check_cgroups(); + if (detected_cgroups.has_value()) { + fmt::print("Possible performance impairment by custom cgroups in: {}.", detected_cgroups.value()); + } cpu_description cpuinfo; cpu_set_t& cpuset = cpuinfo.cpuset; diff --git a/lib/support/sysinfo.cpp b/lib/support/sysinfo.cpp index 57518acb20..02e5aaaf71 100644 --- a/lib/support/sysinfo.cpp +++ b/lib/support/sysinfo.cpp @@ -29,6 +29,9 @@ #include #include +static const std::string isolated_cgroup_path = "/sys/fs/cgroup/srs_isolated"; +static const std::string housekeeping_cgroup_path = "/sys/fs/cgroup/srs_housekeeping"; + /// Executes system command, deletes the given path if the command fails. static bool exec_system_command(const std::string& command, const std::string& cleanup_path = "") { @@ -103,7 +106,7 @@ bool srsran::configure_cgroups(const srsran::os_sched_affinity_bitmask& isol_cpu /// Create cgroup for OS tasks, call it 'housekeeping' cgroup. if (!os_cpus.empty()) { - std::string housekeeping = cgroup_path + "/housekeeping"; + std::string housekeeping = cgroup_path + "/srs_housekeeping"; if ((::mkdir(housekeeping.c_str(), 0755)) < 0 && errno != EEXIST) { fmt::print("Could not create {} directory. error=\"{}\"\n", housekeeping, strerror(errno)); return false; @@ -136,7 +139,7 @@ bool srsran::configure_cgroups(const srsran::os_sched_affinity_bitmask& isol_cpu } /// Create cgroup with isolated CPUs dedicated for the gNB app. - std::string isol_cgroup_path = cgroup_path + "/isolated"; + std::string isol_cgroup_path = cgroup_path + "/srs_isolated"; if ((::mkdir(isol_cgroup_path.c_str(), 0755)) < 0 && errno != EEXIST) { fmt::print("Could not create {} directory. error=\"{}\"\n", isol_cgroup_path, strerror(errno)); return false; @@ -157,10 +160,8 @@ void srsran::cleanup_cgroups() { using namespace std::chrono_literals; - bool cgroup_changed = false; - std::string isolated_cgroup_path = "/sys/fs/cgroup/isolated"; - std::string housekeeping_cgroup_path = "/sys/fs/cgroup/housekeeping"; - std::string root_cgroup_path = "/sys/fs/cgroup/cgroup.procs"; + bool cgroup_changed = false; + std::string root_cgroup_path = "/sys/fs/cgroup/cgroup.procs"; struct stat sysfs_info; if (::stat("/sys/fs/cgroup", &sysfs_info) < 0) { @@ -190,6 +191,29 @@ void srsran::cleanup_cgroups() } } +std::optional srsran::check_cgroups() +{ + // Return false if the system doesn't have cgroups-v2 mounted. + struct stat sysfs_info; + if (::stat("/sys/fs/cgroup", &sysfs_info) < 0) { + return {}; + } + + fmt::memory_buffer buffer; + struct stat info; + if (::stat(housekeeping_cgroup_path.c_str(), &info) == 0) { + fmt::format_to(buffer, "{}", housekeeping_cgroup_path); + } + if (::stat(isolated_cgroup_path.c_str(), &info) == 0) { + if (buffer.size() != 0) { + fmt::format_to(buffer, ", "); + } + fmt::format_to(buffer, "{}", isolated_cgroup_path); + } + + return (buffer.size() != 0) ? to_string(buffer) : std::optional{}; +} + bool srsran::check_cpu_governor(srslog::basic_logger& logger) { unsigned int n_cpus = std::thread::hardware_concurrency(); diff --git a/lib/support/timers.cpp b/lib/support/timers.cpp index 0616e578b2..2eba111207 100644 --- a/lib/support/timers.cpp +++ b/lib/support/timers.cpp @@ -102,15 +102,15 @@ void timer_manager::tick() } // Process new commands coming from the front-end. - for (variant>& event : cmds_to_process) { - if (variant_holds_alternative>(event)) { + for (std::variant>& event : cmds_to_process) { + if (std::holds_alternative>(event)) { // New timer was created in the frontend. - create_timer_handle(std::move(variant_get>(event))); + create_timer_handle(std::move(std::get>(event))); continue; } // Existing timer. - const cmd_t& cmd = variant_get(event); + const cmd_t& cmd = std::get(event); timer_handle& timer = timer_list[static_cast(cmd.id)]; // Update the timer backend cmd_id to match frontend. diff --git a/tests/benchmarks/du_high/du_high_benchmark.cpp b/tests/benchmarks/du_high/du_high_benchmark.cpp index 984a27e15c..29ef85e5d2 100644 --- a/tests/benchmarks/du_high/du_high_benchmark.cpp +++ b/tests/benchmarks/du_high/du_high_benchmark.cpp @@ -287,6 +287,8 @@ class cu_cp_simulator : public srs_du::f1c_connection_client case asn1::f1ap::f1ap_elem_procs_o::init_msg_c::types_opts::init_ul_rrc_msg_transfer: { // Send UE Context Modification to create DRB1. f1ap_message msg2 = generate_ue_context_modification_request({drb_id_t::drb1}); + // Do not send RRC container. + msg2.pdu.init_msg().value.ue_context_mod_request()->rrc_container_present = false; // Note: Use UM because AM requires status PDUs. auto& drb1 = msg2.pdu.init_msg() .value.ue_context_mod_request() @@ -349,6 +351,8 @@ class cu_up_simulator : public f1u_du_gateway } void remove_du_bearer(const up_transport_layer_info& dl_tnl) override {} + + expected get_du_bind_address(gnb_du_id_t gnb_du_id) override { return std::string("127.0.0.1"); } }; /// \brief Instantiation of the DU-high workers and executors for the benchmark. @@ -583,7 +587,7 @@ class du_high_bench report_fatal_error_if_not(bsr_mac_subpdu.append(lbsr_buff_sz), "Failed to allocate PDU"); // Instantiate a DU-high object. - cfg.gnb_du_id = 1; + cfg.gnb_du_id = (gnb_du_id_t)1; cfg.gnb_du_name = fmt::format("srsgnb{}", cfg.gnb_du_id); cfg.du_bind_addr = transport_layer_address::create_from_string(fmt::format("127.0.0.{}", cfg.gnb_du_id)); cfg.exec_mapper = &workers.du_high_exec_mapper; @@ -722,12 +726,12 @@ class du_high_bench using namespace std::chrono_literals; // Wait until it's a full UL slot to send Msg3. - auto next_msg3_opportunity_condition = [this]() { + auto next_ul_slot = [this]() { return not cfg.cells[to_du_cell_index(0)].tdd_ul_dl_cfg_common.has_value() or not is_tdd_full_ul_slot(cfg.cells[to_du_cell_index(0)].tdd_ul_dl_cfg_common.value(), slot_point(next_sl_tx - tx_rx_delay - 1).slot_index()); }; - run_slot_until(next_msg3_opportunity_condition); + run_slot_until(next_ul_slot); // Received Msg3 with UL-CCCH message. mac_rx_data_indication rx_ind; @@ -900,15 +904,18 @@ class du_high_bench // 1 Byte for LCID and other fields and 1/2 Byte(s) for payload length. static const uint8_t mac_header_size = payload_len > 255 ? 3 : 2; - // Early return. + + // Early return - the TB is too small. if (pusch.pusch_cfg.tb_size_bytes <= mac_header_size + bsr_mac_subpdu.length()) { return; } - // Prepare MAC PDU for LCID 4. - payload_len -= mac_header_size + bsr_mac_subpdu.length(); + + // Prepare MAC SDU for LCID 4. static const lcid_t drb_lcid = uint_to_lcid(4); mac_rx_pdu rx_pdu{pusch.pusch_cfg.rnti, 0, pusch.pusch_cfg.harq_id, {}}; // Pack header and payload length. + // Subtract BSR length. + payload_len -= mac_header_size + bsr_mac_subpdu.length(); if (payload_len > 255) { report_fatal_error_if_not(rx_pdu.pdu.append(0x40 | drb_lcid), "Failed to allocate PDU"); report_fatal_error_if_not(rx_pdu.pdu.append((payload_len & 0xff00) >> 8), "Failed to allocate PDU"); @@ -1132,7 +1139,7 @@ void benchmark_dl_ul_only_rlc_um(benchmarker& bm, bench.run_slot(); }, [&bench]() { - // Push DL PDUs and UL PDUs. + // Push DL PDUs. bench.push_pdcp_pdus(); // Advance slot @@ -1192,13 +1199,15 @@ int main(int argc, char** argv) static const std::size_t byte_buffer_segment_size = 2048; // Set DU-high logging. - srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("RLC").set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("MAC", true).set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("SCHED", true).set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("DU-F1").set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("UE-MNG").set_level(srslog::basic_levels::warning); - srslog::fetch_basic_logger("DU-MNG").set_level(srslog::basic_levels::warning); + auto test_log_level = srslog::basic_levels::warning; + srslog::fetch_basic_logger("TEST").set_level(test_log_level); + srslog::fetch_basic_logger("RLC").set_level(test_log_level); + srslog::fetch_basic_logger("MAC", true).set_level(test_log_level); + srslog::fetch_basic_logger("SCHED", true).set_level(test_log_level); + srslog::fetch_basic_logger("DU-F1").set_level(test_log_level); + srslog::fetch_basic_logger("DU-F1-U").set_level(test_log_level); + srslog::fetch_basic_logger("UE-MNG").set_level(test_log_level); + srslog::fetch_basic_logger("DU-MNG").set_level(test_log_level); srslog::init(); std::string tracing_filename = ""; diff --git a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp index 3f2aa16a19..28f9e1dea3 100644 --- a/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp +++ b/tests/benchmarks/phy/upper/channel_processors/pusch/pusch_processor_benchmark.cpp @@ -589,7 +589,21 @@ static pusch_processor_factory& get_pusch_processor_factory() pusch_proc_factory = create_pusch_processor_factory_sw(pusch_proc_factory_config); TESTASSERT(pusch_proc_factory); - pusch_proc_factory = create_pusch_processor_pool(std::move(pusch_proc_factory), nof_threads, true); + pusch_proc_factory_config.decoder_factory = + create_pusch_decoder_empty_factory(MAX_RB, pusch_constants::MAX_NOF_LAYERS); + TESTASSERT(pusch_proc_factory_config.decoder_factory); + std::shared_ptr uci_proc_factory = + create_pusch_processor_factory_sw(pusch_proc_factory_config); + TESTASSERT(uci_proc_factory); + + pusch_processor_pool_factory_config pusch_proc_pool_config; + pusch_proc_pool_config.factory = pusch_proc_factory; + pusch_proc_pool_config.uci_factory = uci_proc_factory; + pusch_proc_pool_config.nof_regular_processors = nof_threads; + pusch_proc_pool_config.nof_uci_processors = nof_threads; + pusch_proc_pool_config.blocking = true; + + pusch_proc_factory = create_pusch_processor_pool(pusch_proc_pool_config); TESTASSERT(pusch_proc_factory); return *pusch_proc_factory; diff --git a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp index 532e58a8a5..c6ccbb4086 100644 --- a/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp +++ b/tests/benchmarks/rlc/rlc_am_rx_benchmark.cpp @@ -151,7 +151,7 @@ std::vector generate_pdus(bench_params params, rx_order order) // Make PDUs std::vector pdus; - rlc_tx = std::make_unique(0, + rlc_tx = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, @@ -250,7 +250,7 @@ void benchmark_rx_pdu(const bench_params& params, rx_order order) config.t_reassembly = 200; // Create RLC AM RX entity - std::unique_ptr rlc_rx = std::make_unique(0, + std::unique_ptr rlc_rx = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, diff --git a/tests/benchmarks/rlc/rlc_handle_status_report.cpp b/tests/benchmarks/rlc/rlc_handle_status_report.cpp index 32892e3306..14b14b1103 100644 --- a/tests/benchmarks/rlc/rlc_handle_status_report.cpp +++ b/tests/benchmarks/rlc/rlc_handle_status_report.cpp @@ -120,7 +120,7 @@ void benchmark_status_pdu_handling(rlc_am_status_pdu status, const bench_params& // Run benchmark auto context = [&rlc, &tester, config, &timers, &pcell_worker, &ue_worker, &pcap]() { - rlc = std::make_unique(0, + rlc = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, diff --git a/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp b/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp index 9113d9b265..7ef9219232 100644 --- a/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp +++ b/tests/benchmarks/scheduler/scheduler_multi_ue_benchmark.cpp @@ -232,13 +232,13 @@ class multi_ue_sched_simulator const unsigned dl_pipeline_delay = 4; const unsigned uci_process_delay = 2; - sched_cfg_dummy_notifier cfg_notif; - sched_dummy_metric_notifier metric_notif; - scheduler_expert_config expert_cfg; - cell_config_builder_params builder_params; - std::vector du_cell_cfgs; - srslog::basic_logger& logger; - optional pucch_res_mng; + sched_cfg_dummy_notifier cfg_notif; + sched_dummy_metric_notifier metric_notif; + scheduler_expert_config expert_cfg; + cell_config_builder_params builder_params; + std::vector du_cell_cfgs; + srslog::basic_logger& logger; + std::optional pucch_res_mng; std::unique_ptr sch; diff --git a/tests/e2e/tests/handover.py b/tests/e2e/tests/handover.py index 108c94dc68..e71f1a4493 100644 --- a/tests/e2e/tests/handover.py +++ b/tests/e2e/tests/handover.py @@ -30,6 +30,7 @@ from pytest import mark from retina.client.manager import RetinaTestManager from retina.launcher.artifacts import RetinaTestData +from retina.launcher.public import MetricsSummary from retina.launcher.utils import configure_artifacts, param from retina.protocol.fivegc_pb2_grpc import FiveGCStub from retina.protocol.gnb_pb2_grpc import GNBStub @@ -37,6 +38,7 @@ from retina.protocol.ue_pb2_grpc import UEStub from .steps.configuration import configure_test_parameters +from .steps.kpis import get_kpis from .steps.stub import ( ping_start, ping_wait_until_finish, @@ -67,6 +69,7 @@ def test_zmq_handover_sequentially( ue_8: UEStub, fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -81,6 +84,7 @@ def test_zmq_handover_sequentially( ue_array=ue_8, gnb=gnb, fivegc=fivegc, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -125,6 +129,7 @@ def test_zmq_handover_parallel( ue_8: UEStub, fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -139,6 +144,7 @@ def test_zmq_handover_parallel( ue_array=ue_8, gnb=gnb, fivegc=fivegc, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -170,6 +176,7 @@ def _handover_multi_ues( ue_array: Sequence[UEStub], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -218,26 +225,29 @@ def _handover_multi_ues( ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) - # HO while pings - movement_duration = (movement_steps + 1) * sleep_between_movement_steps - movements: Tuple[Tuple[Tuple[float, float, float], Tuple[float, float, float], int, int], ...] = ( - (original_position, cell_position_offset, movement_steps, sleep_between_movement_steps), - (cell_position_offset, original_position, movement_steps, sleep_between_movement_steps), - (original_position, cell_position_offset, movement_steps, sleep_between_movement_steps), - (cell_position_offset, original_position, movement_steps, sleep_between_movement_steps), - ) - traffic_seconds = (len(movements) * movement_duration) + len(ue_array) + try: + # HO while pings + movement_duration = (movement_steps + 1) * sleep_between_movement_steps + movements: Tuple[Tuple[Tuple[float, float, float], Tuple[float, float, float], int, int], ...] = ( + (original_position, cell_position_offset, movement_steps, sleep_between_movement_steps), + (cell_position_offset, original_position, movement_steps, sleep_between_movement_steps), + (original_position, cell_position_offset, movement_steps, sleep_between_movement_steps), + (cell_position_offset, original_position, movement_steps, sleep_between_movement_steps), + ) + traffic_seconds = (len(movements) * movement_duration) + len(ue_array) - yield ue_attach_info_dict, movements, traffic_seconds + yield ue_attach_info_dict, movements, traffic_seconds - # Pings after handover - logging.info("Starting Pings after all HO have been completed") - ping_wait_until_finish(ping_start(ue_attach_info_dict, fivegc, movement_duration)) + # Pings after handover + logging.info("Starting Pings after all HO have been completed") + ping_wait_until_finish(ping_start(ue_attach_info_dict, fivegc, movement_duration)) - for ue_stub in ue_array: - ue_validate_no_reattaches(ue_stub) + for ue_stub in ue_array: + ue_validate_no_reattaches(ue_stub) - stop(ue_array, gnb, fivegc, retina_data, warning_as_errors=warning_as_errors) + stop(ue_array, gnb, fivegc, retina_data, warning_as_errors=warning_as_errors) + finally: + get_kpis(gnb, ue_array=ue_array, metrics_summary=metrics_summary) def _do_ho( diff --git a/tests/e2e/tests/reestablishment.py b/tests/e2e/tests/reestablishment.py index 83886cdad6..becad0cc60 100644 --- a/tests/e2e/tests/reestablishment.py +++ b/tests/e2e/tests/reestablishment.py @@ -28,6 +28,7 @@ from pytest import mark from retina.client.manager import RetinaTestManager from retina.launcher.artifacts import RetinaTestData +from retina.launcher.public import MetricsSummary from retina.launcher.utils import configure_artifacts, param from retina.protocol.fivegc_pb2_grpc import FiveGCStub from retina.protocol.gnb_pb2_grpc import GNBStub @@ -35,6 +36,7 @@ from retina.protocol.ue_pb2_grpc import UEStub from .steps.configuration import configure_test_parameters +from .steps.kpis import get_kpis from .steps.stub import ( iperf_parallel, iperf_start, @@ -68,6 +70,7 @@ def test_zmq_reestablishment_sequentially( ue_32: Tuple[UEStub, ...], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -85,6 +88,7 @@ def test_zmq_reestablishment_sequentially( ue_array=ue_32, fivegc=fivegc, gnb=gnb, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -133,6 +137,7 @@ def test_zmq_reestablishment_sequentially_full_rate( ue_8: Tuple[UEStub, ...], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -152,6 +157,7 @@ def test_zmq_reestablishment_sequentially_full_rate( ue_array=ue_8, fivegc=fivegc, gnb=gnb, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -181,70 +187,6 @@ def test_zmq_reestablishment_sequentially_full_rate( iperf_wait_until_finish(ue_attached_info, fivegc, task, iperf_request) -# pylint: disable=too-many-arguments,too-many-locals -def _iterator_over_attached_ues( - retina_manager: RetinaTestManager, - retina_data: RetinaTestData, - ue_array: Sequence[UEStub], - fivegc: FiveGCStub, - gnb: GNBStub, - band: int, - common_scs: int, - bandwidth: int, - sample_rate: Optional[int], - global_timing_advance: int, - time_alignment_calibration: Union[int, str], - always_download_artifacts: bool, - noise_spd: int, - warning_as_errors: bool = True, -) -> Generator[Tuple[Dict[UEStub, UEAttachedInfo], Dict[UEStub, UEAttachedInfo]], None, None]: - - logging.info("Reestablishment Test") - - configure_test_parameters( - retina_manager=retina_manager, - retina_data=retina_data, - band=band, - common_scs=common_scs, - bandwidth=bandwidth, - sample_rate=sample_rate, - global_timing_advance=global_timing_advance, - time_alignment_calibration=time_alignment_calibration, - noise_spd=noise_spd, - enable_qos_reestablishment=True, - ) - - configure_artifacts( - retina_data=retina_data, - always_download_artifacts=always_download_artifacts, - ) - - start_network(ue_array, gnb, fivegc, gnb_post_cmd="log --mac_level=debug --cu_level=debug") - - ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) - - # Reestablishment while traffic - for ue_stub in ue_array: - logging.info( - "Starting Reestablishment for UE [%s] (%s) + Traffic running in background for all UEs", - id(ue_stub), - ue_attach_info_dict[ue_stub].ipv4, - ) - other_ue_attach_info_dict = dict(ue_attach_info_dict) - other_ue_attach_info_dict.pop(ue_stub) - - yield {ue_stub: ue_attach_info_dict[ue_stub]}, other_ue_attach_info_dict - - # Pings after reest - logging.info("Starting traffic after all reestablishments have been completed") - yield {}, ue_attach_info_dict - - for ue_stub in ue_array: - ue_validate_no_reattaches(ue_stub) - - stop(ue_array, gnb, fivegc, retina_data, warning_as_errors=warning_as_errors) - - @mark.parametrize( "band, common_scs, bandwidth, noise_spd", ( @@ -263,6 +205,7 @@ def test_zmq_reestablishment_parallel( ue_32: Tuple[UEStub, ...], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -274,12 +217,13 @@ def test_zmq_reestablishment_parallel( number_of_reestablishments = 10 reestablishment_time = 10 - with _parallel_reestablishments( + with _test_reestablishments( retina_manager=retina_manager, retina_data=retina_data, ue_array=ue_32, fivegc=fivegc, gnb=gnb, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -326,6 +270,7 @@ def test_zmq_reestablishment_parallel_full_rate( ue_8: Tuple[UEStub, ...], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -339,12 +284,13 @@ def test_zmq_reestablishment_parallel_full_rate( number_of_reestablishments = 10 reestablishment_time = 10 - with _parallel_reestablishments( + with _test_reestablishments( retina_manager=retina_manager, retina_data=retina_data, ue_array=ue_8, fivegc=fivegc, gnb=gnb, + metrics_summary=metrics_summary, band=band, common_scs=common_scs, bandwidth=bandwidth, @@ -377,14 +323,69 @@ def test_zmq_reestablishment_parallel_full_rate( ) +# pylint: disable=too-many-arguments,too-many-locals +def _iterator_over_attached_ues( + retina_manager: RetinaTestManager, + retina_data: RetinaTestData, + ue_array: Sequence[UEStub], + fivegc: FiveGCStub, + gnb: GNBStub, + metrics_summary: MetricsSummary, + band: int, + common_scs: int, + bandwidth: int, + sample_rate: Optional[int], + global_timing_advance: int, + time_alignment_calibration: Union[int, str], + always_download_artifacts: bool, + noise_spd: int, + warning_as_errors: bool = True, +) -> Generator[Tuple[Dict[UEStub, UEAttachedInfo], Dict[UEStub, UEAttachedInfo]], None, None]: + + with _test_reestablishments( + retina_manager=retina_manager, + retina_data=retina_data, + ue_array=ue_array, + fivegc=fivegc, + metrics_summary=metrics_summary, + gnb=gnb, + band=band, + common_scs=common_scs, + bandwidth=bandwidth, + sample_rate=sample_rate, + global_timing_advance=global_timing_advance, + time_alignment_calibration=time_alignment_calibration, + always_download_artifacts=always_download_artifacts, + noise_spd=noise_spd, + warning_as_errors=warning_as_errors, + ) as ue_attach_info_dict: + + # Reestablishment while traffic + for ue_stub in ue_array: + logging.info( + "Starting Reestablishment for UE [%s] (%s) + Traffic running in background for all UEs", + id(ue_stub), + ue_attach_info_dict[ue_stub].ipv4, + ) + other_ue_attach_info_dict = dict(ue_attach_info_dict) + other_ue_attach_info_dict.pop(ue_stub) + + yield {ue_stub: ue_attach_info_dict[ue_stub]}, other_ue_attach_info_dict + + # Pings after reest + logging.info("Starting traffic after all reestablishments have been completed") + yield {}, ue_attach_info_dict + + # pylint: disable=too-many-arguments,too-many-locals @contextmanager -def _parallel_reestablishments( +def _test_reestablishments( retina_manager: RetinaTestManager, retina_data: RetinaTestData, ue_array: Sequence[UEStub], fivegc: FiveGCStub, gnb: GNBStub, + metrics_summary: MetricsSummary, band: int, common_scs: int, bandwidth: int, @@ -420,9 +421,13 @@ def _parallel_reestablishments( ue_attach_info_dict = ue_start_and_attach(ue_array, gnb, fivegc) - yield ue_attach_info_dict + try: + yield ue_attach_info_dict + + for ue_stub in ue_array: + ue_validate_no_reattaches(ue_stub) - for ue_stub in ue_array: - ue_validate_no_reattaches(ue_stub) + stop(ue_array, gnb, fivegc, retina_data, warning_as_errors=warning_as_errors) - stop(ue_array, gnb, fivegc, retina_data, warning_as_errors=warning_as_errors) + finally: + get_kpis(gnb, ue_array=ue_array, metrics_summary=metrics_summary) diff --git a/tests/e2e/tests/steps/kpis.py b/tests/e2e/tests/steps/kpis.py new file mode 100644 index 0000000000..0b0a7c2bea --- /dev/null +++ b/tests/e2e/tests/steps/kpis.py @@ -0,0 +1,118 @@ +# +# 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/. +# + +""" +KPI related logic +""" + +from contextlib import suppress +from dataclasses import dataclass, fields +from typing import Optional, Sequence + +from google.protobuf.empty_pb2 import Empty +from retina.launcher.public import MetricsSummary +from retina.protocol import RanStub +from retina.protocol.base_pb2 import Metrics +from retina.viavi.client import ViaviFailureManager + + +@dataclass +# pylint: disable=too-many-instance-attributes +class KPIs: + """ + KPIs from a test + """ + + ul_brate_aggregate: float = 0 + dl_brate_aggregate: float = 0 + ul_brate_min: float = 0 + dl_brate_min: float = 0 + ul_brate_max: float = 0 + dl_brate_max: float = 0 + ul_bler_aggregate: float = 0 + dl_bler_aggregate: float = 0 + nof_ko_aggregate: int = 0 + nof_attach_failures: int = 0 + nof_reestablishments: int = 0 + nof_handovers: int = 0 + + +# pylint: disable=too-many-locals +def get_kpis( + gnb: RanStub, + ue_array: Sequence[RanStub] = (), + viavi_failure_manager: Optional[ViaviFailureManager] = None, + metrics_summary: Optional[MetricsSummary] = None, +) -> KPIs: + """ + Get KPIs from gnb, ue and viavi + """ + + kpis = 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 + + for ue_info in gnb_metrics.ue_array: + kpis.ul_brate_aggregate += ue_info.ul_bitrate + kpis.dl_brate_aggregate += ue_info.dl_bitrate + + 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.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) + + 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 + + kpis.ul_bler_aggregate = ul_nof_ko_aggregate / (ul_nof_ok_aggregate + ul_nof_ko_aggregate) + kpis.dl_bler_aggregate = dl_nof_ko_aggregate / (dl_nof_ok_aggregate + dl_nof_ko_aggregate) + + # UE + for ue in ue_array: + ue_metrics: Metrics = ue.GetMetrics(Empty()) + for ue_info in ue_metrics.ue_array: + kpis.nof_reestablishments += ue_info.nof_reestablishments + kpis.nof_handovers += ue_info.nof_handovers + + # Viavi + if viavi_failure_manager: + nof_failure = viavi_failure_manager.get_nof_failure_by_group_procedure("EMM_PROCEDURE", "attach") + if nof_failure: + kpis.nof_attach_failures += nof_failure + + # Save KPIs + if metrics_summary is not None: + for field in fields(kpis): + field_value = getattr(kpis, field.name) + metrics_summary.write_metric(field.name, field_value) + + return kpis diff --git a/tests/e2e/tests/steps/stub.py b/tests/e2e/tests/steps/stub.py index 758914655c..6e0df25e9e 100644 --- a/tests/e2e/tests/steps/stub.py +++ b/tests/e2e/tests/steps/stub.py @@ -24,7 +24,6 @@ import logging from concurrent.futures import as_completed, ThreadPoolExecutor from contextlib import contextmanager, suppress -from dataclasses import dataclass from time import sleep, time from typing import Dict, Generator, List, Optional, Sequence, Tuple @@ -61,17 +60,6 @@ ATTACH_TIMEOUT: int = 90 -@dataclass -class GnbMetrics: - """ - Metrics from a GNB - """ - - ul_brate_agregate: float - dl_brate_agregate: float - nof_kos_aggregate: float - - # pylint: disable=too-many-arguments,too-many-locals def start_and_attach( ue_array: Sequence[UEStub], @@ -742,26 +730,3 @@ def _get_metrics_msg(stub: RanStub, name: str, fail_if_kos: bool = False) -> str return f"{name} has {nof_kos} KOs / retrxs" return "" - - -def get_metrics(stub: RanStub) -> GnbMetrics: - """ - Get metrics from a stub - """ - with suppress(grpc.RpcError): - metrics: Metrics = stub.GetMetrics(Empty()) - - ul_brate_aggregate = 0 - dl_brate_aggregate = 0 - nof_kos_aggregate = 0 - - for ue_info in metrics.ue_array: - if ue_info.ul_bitrate: - ul_brate_aggregate += ue_info.ul_bitrate - if ue_info.dl_bitrate: - dl_brate_aggregate += ue_info.dl_bitrate - nof_kos = ue_info.dl_nof_ko + ue_info.ul_nof_ko - if nof_kos: - nof_kos_aggregate += nof_kos - - return GnbMetrics(ul_brate_aggregate, dl_brate_aggregate, nof_kos_aggregate) diff --git a/tests/e2e/tests/test_mode.py b/tests/e2e/tests/test_mode.py index 349867a5e8..c0b4a0f332 100644 --- a/tests/e2e/tests/test_mode.py +++ b/tests/e2e/tests/test_mode.py @@ -40,6 +40,8 @@ from .steps.stub import FIVEGC_STARTUP_TIMEOUT, GNB_STARTUP_TIMEOUT, handle_start_error, stop +_POD_ERROR = "Error creating the pod" + @mark.parametrize( "extra_config, nof_ant", @@ -141,6 +143,10 @@ def test_ue( @mark.test_mode +@mark.flaky( + reruns=2, + only_rerun=[_POD_ERROR], +) def test_ru( # Retina retina_manager: RetinaTestManager, @@ -155,6 +161,10 @@ def test_ru( @mark.test_mode_not_crash +@mark.flaky( + reruns=2, + only_rerun=[_POD_ERROR], +) def test_ru_not_crash( # Retina retina_manager: RetinaTestManager, diff --git a/tests/e2e/tests/test_mode/config_ru.yml b/tests/e2e/tests/test_mode/config_ru.yml index fde75a3e37..3380218b46 100644 --- a/tests/e2e/tests/test_mode/config_ru.yml +++ b/tests/e2e/tests/test_mode/config_ru.yml @@ -55,12 +55,12 @@ buffer_pool: expert_execution: affinities: - low_priority_cpus: 6-10,18-22 + low_priority_cpus: {{low_priority_cpus}} cell_affinities: - - l1_dl_cpus: 2-10,14-22 - l1_ul_cpus: 2-10,14-22 - l2_cell_cpus: 1,13 - ru_cpus: 2,14 + - l1_dl_cpus: {{l1_dl_cpus}} + l1_ul_cpus: {{l1_ul_cpus}} + l2_cell_cpus: {{l2_cell_cpus}} + ru_cpus: {{ru_cpus}} expert_phy: max_request_headroom_slots: 3 diff --git a/tests/e2e/tests/viavi.py b/tests/e2e/tests/viavi.py index e51a85e922..8f89eb1bed 100644 --- a/tests/e2e/tests/viavi.py +++ b/tests/e2e/tests/viavi.py @@ -41,7 +41,8 @@ from retina.viavi.client import CampaignStatusEnum, Viavi from .steps.configuration import configure_metric_server_for_gnb -from .steps.stub import get_metrics, GNB_STARTUP_TIMEOUT, GnbMetrics, handle_start_error, stop +from .steps.kpis import get_kpis, KPIs +from .steps.stub import GNB_STARTUP_TIMEOUT, handle_start_error, stop _OMIT_VIAVI_FAILURE_LIST = ["authentication"] _POD_ERROR = "Error creating the pod" @@ -419,27 +420,21 @@ def check_metrics_criteria( is_ok = True # Check metrics - gnb_metrics: GnbMetrics = get_metrics(gnb) + viavi_failure_manager = viavi.get_test_failures() + kpis: KPIs = get_kpis(gnb, viavi_failure_manager=viavi_failure_manager, metrics_summary=metrics_summary) is_ok &= check_and_print_criteria( - "DL bitrate", gnb_metrics.dl_brate_agregate, test_configuration.expected_dl_bitrate, operator.gt + "DL bitrate", kpis.dl_brate_aggregate, test_configuration.expected_dl_bitrate, operator.gt, False ) is_ok &= check_and_print_criteria( - "UL bitrate", gnb_metrics.ul_brate_agregate, test_configuration.expected_ul_bitrate, operator.gt + "UL bitrate", kpis.ul_brate_aggregate, test_configuration.expected_ul_bitrate, operator.gt, False ) is_ok &= ( - check_and_print_criteria("Number of KOs and/or retrxs", gnb_metrics.nof_kos_aggregate, 0, operator.eq) + check_and_print_criteria("Number of KOs and/or retrxs", kpis.nof_ko_aggregate, 0, operator.eq, not fail_if_kos) or not fail_if_kos ) - # Save metrics - if metrics_summary is not None: - metrics_summary.write_metric("dl_bitrate", gnb_metrics.dl_brate_agregate) - metrics_summary.write_metric("ul_bitrate", gnb_metrics.ul_brate_agregate) - metrics_summary.write_metric("kos", gnb_metrics.nof_kos_aggregate) - # Check procedure table - viavi_failure_manager = viavi.get_test_failures() viavi_failure_manager.print_failures(_OMIT_VIAVI_FAILURE_LIST) is_ok &= viavi_failure_manager.get_number_of_failures(_OMIT_VIAVI_FAILURE_LIST) == 0 @@ -448,13 +443,19 @@ def check_metrics_criteria( def check_and_print_criteria( - name: str, current: float, expected: float, operator_method: Callable[[float, float], bool] + name: str, + current: float, + expected: float, + operator_method: Callable[[float, float], bool], + force_log_info: bool = False, ) -> bool: """ Check and print criteria """ is_ok = operator_method(current, expected) - (logging.info if is_ok else logging.error)(f"{name} expected: {expected}, actual: {current}") + (logging.info if is_ok or force_log_info else logging.error)( + f"{name} expected: {expected:.2e}, actual: {current:.2e}" + ) return is_ok diff --git a/tests/e2e/tests/viavi/config.yml b/tests/e2e/tests/viavi/config.yml index f1c8f0ba1b..14e8f4b6e4 100644 --- a/tests/e2e/tests/viavi/config.yml +++ b/tests/e2e/tests/viavi/config.yml @@ -35,12 +35,18 @@ cell_cfg: pdsch: mcs_table: qam256 max_alloc_attempts: 8 + olla_target_bler: 0.1 + olla_cqi_inc_step: 0.05 + olla_max_cqi_offset: 10 ul_common: max_ul_grants_per_slot: 16 max_pucchs_per_slot: 14 pusch: mcs_table: qam256 min_k2: 2 + olla_target_bler: 0.1 + olla_snr_inc_step: 0.05 + olla_max_snr_offset: 20 tdd_ul_dl_cfg: nof_dl_symbols: 7 nof_dl_slots: 7 diff --git a/tests/e2e/tests/viavi/test_declaration.yml b/tests/e2e/tests/viavi/test_declaration.yml index f144786919..5dbfa3764f 100644 --- a/tests/e2e/tests/viavi/test_declaration.yml +++ b/tests/e2e/tests/viavi/test_declaration.yml @@ -18,17 +18,30 @@ # and at http://www.gnu.org/licenses/. # +# campaign_filename: path to Viavi campaign file +# test_name: name of test in Viavi campaign file +# test_timeout: timeout for test in seconds +# gnb_extra_commands: extra GNB configuration +# id: name of test in Pytest +# max_pdschs_per_slot: maximum number of PDSCHs per slot +# max_puschs_per_slot: maximum number of PUSCHs per slot +# enable_qos_viavi: enable QoS in GNB configuration for Viavi +# expected_dl_bitrate: pass/fail criteria, expected downlink bitrate in bps +# expected_ul_bitrate: pass/fail criteria, expected uplink bitrate in bps +# fail_if_kos: fail if KPIs are out of spec +# warning_as_errors: treat warnings as errors + campaign_filename: &campaign_filename "C:\\ci\\CI 4x4 ORAN-FH-complete.xml" -gnb_extra_commands: &gnb_extra_commands "log --phy_level=info" -expected_dl_bitrate_high: &expected_dl_bitrate_high 80000000 -expected_ul_bitrate_high: &expected_ul_bitrate_high 80000000 -expected_dl_bitrate_low: &expected_dl_bitrate_low 1000 +gnb_extra_commands: &gnb_extra_commands "log --mac_level=info" +expected_dl_bitrate_high: &expected_dl_bitrate_high 1400000000 +expected_ul_bitrate_high: &expected_ul_bitrate_high 100000000 +expected_dl_bitrate_low: &expected_dl_bitrate_low 14000 expected_ul_bitrate_low: &expected_ul_bitrate_low 1000 tests: - campaign_filename: *campaign_filename test_name: "1UE ideal UDP bidirectional" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "1UE ideal UDP bidirectional" max_pdschs_per_slot: 8 @@ -42,7 +55,7 @@ tests: - campaign_filename: *campaign_filename test_name: "32UE ideal UDP bidirectional" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE ideal UDP bidirectional" max_pdschs_per_slot: 1 @@ -56,147 +69,147 @@ tests: - campaign_filename: *campaign_filename test_name: "1UE fading UDP uplink" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "1UE fading UDP uplink" max_pdschs_per_slot: 8 max_puschs_per_slot: 8 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename - test_name: "1UE fading,noise UDP uplink" - test_timeout: 2700 # 45 * 60 + test_name: "1UE fading noise UDP uplink" + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands - id: "1UE fading,noise UDP uplink" + id: "1UE fading noise UDP uplink" max_pdschs_per_slot: 8 max_puschs_per_slot: 8 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename test_name: "1UE ideal TCP downlink" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "1UE ideal TCP downlink" max_pdschs_per_slot: 8 max_puschs_per_slot: 8 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: true warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE ideal TCP downlink" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE ideal TCP downlink" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: true warning_as_errors: true - campaign_filename: *campaign_filename test_name: "1UE fading TCP downlink" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "1UE fading TCP downlink" max_pdschs_per_slot: 8 max_puschs_per_slot: 8 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE fading TCP bidirectional" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE fading TCP bidirectional" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE ideal UDP attach-detach with traffic" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE ideal UDP attach-detach with traffic" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: true warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE fading UDP attach-detach with traffic" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE fading UDP attach-detach with traffic" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: false - campaign_filename: *campaign_filename test_name: "1UE birth-death UDP bidirectional" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "1UE birth-death UDP bidirectional" max_pdschs_per_slot: 8 max_puschs_per_slot: 8 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE birth-death UDP bidirectional" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE birth-death UDP bidirectional" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: *expected_dl_bitrate_high - expected_ul_bitrate: *expected_ul_bitrate_high + expected_dl_bitrate: *expected_dl_bitrate_low + expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: false warning_as_errors: true - campaign_filename: *campaign_filename test_name: "32UE ideal ping" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE ideal ping" max_pdschs_per_slot: 1 @@ -210,7 +223,7 @@ tests: - campaign_filename: *campaign_filename test_name: "32UE ideal ping with traffic" - test_timeout: 2700 # 45 * 60 + test_timeout: 2700 # 45 * 60 gnb_extra_commands: *gnb_extra_commands id: "32UE ideal ping with traffic" max_pdschs_per_slot: 1 @@ -220,4 +233,4 @@ tests: expected_dl_bitrate: *expected_dl_bitrate_low expected_ul_bitrate: *expected_ul_bitrate_low fail_if_kos: true - warning_as_errors: true \ No newline at end of file + warning_as_errors: true diff --git a/tests/e2e/tests/viavi/test_declaration_debug.yml b/tests/e2e/tests/viavi/test_declaration_debug.yml index 4beb905667..e50f74ffdb 100644 --- a/tests/e2e/tests/viavi/test_declaration_debug.yml +++ b/tests/e2e/tests/viavi/test_declaration_debug.yml @@ -18,18 +18,35 @@ # and at http://www.gnu.org/licenses/. # -tests: +# campaign_filename: path to Viavi campaign file +# test_name: name of test in Viavi campaign file +# test_timeout: timeout for test in seconds +# gnb_extra_commands: extra GNB configuration +# id: name of test in Pytest +# max_pdschs_per_slot: maximum number of PDSCHs per slot +# max_puschs_per_slot: maximum number of PUSCHs per slot +# enable_qos_viavi: enable QoS in GNB configuration for Viavi +# expected_dl_bitrate: pass/fail criteria, expected downlink bitrate in bps +# expected_ul_bitrate: pass/fail criteria, expected uplink bitrate in bps +# fail_if_kos: fail if KPIs are out of spec +# warning_as_errors: treat warnings as errors + +campaign_filename: &campaign_filename "C:\\ci\\CI 4x4 ORAN-FH-complete.xml" +gnb_extra_commands: &gnb_extra_commands "" +expected_dl_bitrate_high: &expected_dl_bitrate_high 80000000 +expected_ul_bitrate_high: &expected_ul_bitrate_high 80000000 - - campaign_filename: "C:\\ci\\CI 4x4 ORAN-FH.xml" - test_name: "32UE static DL + UL UDP - Dell" - test_timeout: 2700 # 45 * 60 - gnb_extra_commands: "log --phy_level=info" - id: "32UE Bidirectional UDP - Debug" +tests: + - campaign_filename: *campaign_filename + test_name: "32UE ideal UDP bidirectional" + test_timeout: 2700 # 45 * 60 + gnb_extra_commands: *gnb_extra_commands + id: "32UE ideal UDP bidirectional - Debug" max_pdschs_per_slot: 1 max_puschs_per_slot: 1 enable_qos_viavi: false # test/fail criteria - expected_dl_bitrate: 80000000 - expected_ul_bitrate: 80000000 + expected_dl_bitrate: *expected_dl_bitrate_high + expected_ul_bitrate: *expected_ul_bitrate_high fail_if_kos: false - warning_as_errors: false \ No newline at end of file + warning_as_errors: false diff --git a/tests/integrationtests/du_high/CMakeLists.txt b/tests/integrationtests/du_high/CMakeLists.txt index 439112aff3..7a48f61664 100644 --- a/tests/integrationtests/du_high/CMakeLists.txt +++ b/tests/integrationtests/du_high/CMakeLists.txt @@ -20,28 +20,25 @@ set_directory_properties(PROPERTIES LABELS "du_high;integrationtest") -add_library(du_high_test_bench test_utils/du_high_env_simulator.cpp) -target_link_libraries(du_high_test_bench srsran_du_high mac_test_doubles f1ap_test_doubles) -target_include_directories(du_high_test_bench PRIVATE ${CMAKE_SOURCE_DIR}) - -add_executable(du_high_test du_high_test.cpp du_high_many_ues_test.cpp du_high_many_cells_test.cpp) +add_executable(du_high_test du_high_test.cpp + du_high_many_ues_test.cpp + du_high_many_cells_test.cpp + paging_test.cpp + test_utils/du_high_env_simulator.cpp) target_link_libraries(du_high_test - du_high_test_bench + srsran_du_high f1ap_du_test_helpers srsran_gateway gtest gtest_main - pdcp_test_doubles) + pdcp_test_doubles + sched_test_doubles + mac_test_doubles + f1ap_test_doubles) target_include_directories(du_high_test PRIVATE ${CMAKE_SOURCE_DIR}) add_test(du_high_test du_high_test) set_tests_properties(du_high_test PROPERTIES LABELS "tsan") -add_executable(paging_du_high_test paging_test.cpp) -target_link_libraries(paging_du_high_test du_high_test_bench f1ap_du_test_helpers srsran_gateway gtest gtest_main) -target_include_directories(paging_du_high_test PRIVATE ${CMAKE_SOURCE_DIR}) -add_test(paging_du_high_test paging_du_high_test) -set_tests_properties(paging_du_high_test PROPERTIES LABELS "tsan") - add_executable(mac_test_mode_adapter_test mac_test_mode_adapter_test.cpp) target_link_libraries(mac_test_mode_adapter_test srsran_du_high gtest gtest_main) target_include_directories(mac_test_mode_adapter_test PRIVATE ${CMAKE_SOURCE_DIR}) diff --git a/tests/integrationtests/du_high/du_high_many_cells_test.cpp b/tests/integrationtests/du_high/du_high_many_cells_test.cpp index cb03c85918..9987b904ea 100644 --- a/tests/integrationtests/du_high/du_high_many_cells_test.cpp +++ b/tests/integrationtests/du_high/du_high_many_cells_test.cpp @@ -23,10 +23,10 @@ #include "test_utils/du_high_env_simulator.h" #include "tests/test_doubles/f1ap/f1ap_test_message_validators.h" #include "tests/test_doubles/pdcp/pdcp_pdu_generator.h" +#include "tests/test_doubles/scheduler/scheduler_result_test.h" #include "srsran/asn1/f1ap/common.h" #include "srsran/asn1/f1ap/f1ap_pdu_contents.h" #include "srsran/f1ap/common/f1ap_message.h" -#include "srsran/support/test_utils.h" #include using namespace srsran; @@ -50,6 +50,16 @@ class du_high_many_cells_tester : public du_high_env_simulator, public testing:: { public: du_high_many_cells_tester() : du_high_env_simulator(du_high_env_sim_params{GetParam().nof_cells}) {} + + bool run_until_csi(du_cell_index_t cell_idx, rnti_t rnti) + { + return run_until([this, cell_idx, rnti]() { + if (phy.cells[cell_idx].last_ul_res.has_value()) { + return find_ue_uci_with_csi(rnti, *phy.cells[cell_idx].last_ul_res.value().ul_res) != nullptr; + } + return false; + }); + } }; TEST_P(du_high_many_cells_tester, when_du_high_initiated_then_f1_setup_is_sent_with_correct_number_of_cells) @@ -105,8 +115,9 @@ TEST_P(du_high_many_cells_tester, when_ue_created_in_multiple_cells_then_traffic rnti_t rnti = to_rnti(0x4601 + i); ASSERT_TRUE(add_ue(rnti, to_du_cell_index(i))); ASSERT_TRUE(run_rrc_setup(rnti)); - ASSERT_TRUE(force_ue_fallback(rnti)); ASSERT_TRUE(run_ue_context_setup(rnti)); + // Wait for a CSI to be sent, otherwise the TBs will be small. + ASSERT_TRUE(run_until_csi(to_du_cell_index(i), rnti)); // Ensure DU<->CU-UP tunnel was created. ASSERT_EQ(cu_up_sim.last_drb_id.value(), drb_id_t::drb1); @@ -114,29 +125,41 @@ TEST_P(du_high_many_cells_tester, when_ue_created_in_multiple_cells_then_traffic // Forward several DRB PDUs to all UEs. const unsigned nof_pdcp_pdus = 100, pdcp_pdu_size = 128; - for (unsigned c = 0; c != GetParam().nof_cells; ++c) { - for (unsigned i = 0; i < nof_pdcp_pdus; ++i) { + for (unsigned i = 0; i < nof_pdcp_pdus; ++i) { + for (unsigned c = 0; c != GetParam().nof_cells; ++c) { nru_dl_message f1u_pdu{test_helpers::create_pdcp_pdu(pdcp_sn_size::size12bits, i, pdcp_pdu_size, i), i}; cu_up_sim.created_du_notifs[c]->on_new_pdu(f1u_pdu); } } // Ensure DRB is active by verifying that the DRB PDUs are scheduled. - unsigned bytes_sched = 0; - const unsigned expected_bytes_sched = nof_pdcp_pdus * pdcp_pdu_size * GetParam().nof_cells; std::vector bytes_sched_per_cell(GetParam().nof_cells, 0); for (unsigned i = 0; i != GetParam().nof_cells; ++i) { phy.cells[i].last_dl_data.reset(); } - while (bytes_sched < expected_bytes_sched and this->run_until([this]() { + // condition to stop experiment. + auto stop_condition = [this, &bytes_sched_per_cell]() { + for (unsigned c = 0; c != du_high_cfg.cells.size(); ++c) { + if (bytes_sched_per_cell[c] < nof_pdcp_pdus * pdcp_pdu_size) { + // one of the cells still didn't send all the pending traffic. + return false; + } + } + return true; + }; + // check if a PDSCH was scheduled. + auto pdsch_grant_scheduled = [this]() { for (unsigned i = 0; i != du_high_cfg.cells.size(); ++i) { if (phy.cells[i].last_dl_data.has_value() and not phy.cells[i].last_dl_data.value().ue_pdus.empty()) { return true; } } return false; - })) { + }; + + std::vector largest_pdu_per_cell(GetParam().nof_cells, 0); + while (not stop_condition() and this->run_until([pdsch_grant_scheduled]() { return pdsch_grant_scheduled(); })) { for (unsigned c = 0; c != du_high_cfg.cells.size(); ++c) { auto& phy_cell = phy.cells[c]; if (not phy_cell.last_dl_data.has_value()) { @@ -148,23 +171,26 @@ TEST_P(du_high_many_cells_tester, when_ue_created_in_multiple_cells_then_traffic << fmt::format("c-rnti={} scheduled in the wrong cell={}", ue_grant.pdsch_cfg.rnti, c); // Update the total number of bytes scheduled. - if (ue_grant.pdsch_cfg.codewords[0].new_data) { - bytes_sched += phy_cell.last_dl_data.value().ue_pdus[i].pdu.size(); - bytes_sched_per_cell[c] += phy_cell.last_dl_data.value().ue_pdus[i].pdu.size(); + if (ue_grant.pdsch_cfg.codewords[0].new_data and + std::any_of(ue_grant.tb_list[0].lc_chs_to_sched.begin(), + ue_grant.tb_list[0].lc_chs_to_sched.end(), + // is DRB data + [](const dl_msg_lc_info& lc) { return lc.lcid.is_sdu() and lc.lcid.to_lcid() >= LCID_SRB3; })) { + unsigned pdu_size = phy_cell.last_dl_data.value().ue_pdus[i].pdu.size(); + bytes_sched_per_cell[c] += pdu_size; + largest_pdu_per_cell[c] = std::max(largest_pdu_per_cell[c], pdu_size); } } phy.cells[c].last_dl_data.reset(); } } - ASSERT_GE(bytes_sched, expected_bytes_sched) - << fmt::format("Not enough PDSCH grants (bytes={}) were scheduled to meet the enqueued PDCP PDUs (bytes={})", - bytes_sched, - expected_bytes_sched); + ASSERT_TRUE(stop_condition()) << "Experiment did not finish when all cells transmitted pending traffic"; - for (unsigned c = 0; c != du_high_cfg.cells.size(); ++c) { - ASSERT_GE(bytes_sched_per_cell[c], nof_pdcp_pdus * pdcp_pdu_size) << fmt::format( - "In cell={} scheduled bytes {} < expected bytes {}", c, bytes_sched_per_cell[c], nof_pdcp_pdus * pdcp_pdu_size); + // Cells should schedule equally large MAC PDUs + for (unsigned c = 1; c != du_high_cfg.cells.size(); ++c) { + ASSERT_EQ(largest_pdu_per_cell[c], largest_pdu_per_cell[0]) + << fmt::format("cells {} and {} cannot schedule equally large PDUs", 0, c); } } diff --git a/tests/integrationtests/du_high/du_high_test.cpp b/tests/integrationtests/du_high/du_high_test.cpp index 83707d6613..110938cbb6 100644 --- a/tests/integrationtests/du_high/du_high_test.cpp +++ b/tests/integrationtests/du_high/du_high_test.cpp @@ -83,7 +83,6 @@ TEST_F(du_high_tester, when_ue_context_setup_completes_then_drb_is_active) rnti_t rnti = to_rnti(0x4601); ASSERT_TRUE(add_ue(rnti)); ASSERT_TRUE(run_rrc_setup(rnti)); - ASSERT_TRUE(force_ue_fallback(rnti)); ASSERT_TRUE(run_ue_context_setup(rnti)); // Ensure DU<->CU-UP tunnel was created. @@ -206,7 +205,7 @@ TEST_F(du_high_tester, when_ue_context_setup_received_for_inexistent_ue_then_ue_ gnb_cu_ue_f1ap_id_t cu_ue_id = int_to_gnb_cu_ue_f1ap_id(test_rgen::uniform_int(0, (unsigned)gnb_cu_ue_f1ap_id_t::max)); - f1ap_message cu_cp_msg = test_helpers::create_ue_context_setup_request(cu_ue_id, nullopt, {drb_id_t::drb1}); + f1ap_message cu_cp_msg = test_helpers::create_ue_context_setup_request(cu_ue_id, std::nullopt, {drb_id_t::drb1}); this->du_hi->get_f1ap_message_handler().handle_message(cu_cp_msg); ASSERT_TRUE(this->run_until([this]() { return not cu_notifier.last_f1ap_msgs.empty(); })); diff --git a/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp b/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp index f2335dd179..65b4634178 100644 --- a/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp +++ b/tests/integrationtests/du_high/mac_test_mode_adapter_test.cpp @@ -31,12 +31,12 @@ using namespace srsran; struct mac_event_interceptor { - optional next_ul_sched_res; + std::optional next_ul_sched_res; - optional last_ue_created; + std::optional last_ue_created; - optional last_uci; - optional last_crc; + std::optional last_uci; + std::optional last_crc; }; class phy_dummy : public mac_result_notifier, public mac_cell_result_notifier @@ -244,8 +244,8 @@ TEST_P(mac_test_mode_adapter_test, when_test_mode_ue_has_pucch_grants_then_uci_i ASSERT_EQ(mac_events.last_uci->sl_rx, sl_rx); ASSERT_EQ(mac_events.last_uci->ucis.size(), 1); ASSERT_EQ(mac_events.last_uci->ucis[0].rnti, this->params.test_ue_cfg.rnti); - ASSERT_TRUE(variant_holds_alternative(mac_events.last_uci->ucis[0].pdu)); - const auto& f1 = variant_get(mac_events.last_uci->ucis[0].pdu); + ASSERT_TRUE(std::holds_alternative(mac_events.last_uci->ucis[0].pdu)); + const auto& f1 = std::get(mac_events.last_uci->ucis[0].pdu); ASSERT_FALSE(f1.sr_info.has_value()); ASSERT_TRUE(f1.harq_info.has_value()); ASSERT_EQ(f1.harq_info->harqs.size(), 1); @@ -318,8 +318,8 @@ TEST_P(mac_test_mode_adapter_test, when_uci_is_forwarded_to_mac_then_test_mode_c ASSERT_EQ(mac_events.last_uci->sl_rx, sl_rx); ASSERT_EQ(mac_events.last_uci->ucis.size(), 1); ASSERT_EQ(mac_events.last_uci->ucis[0].rnti, this->params.test_ue_cfg.rnti); - ASSERT_TRUE(variant_holds_alternative(mac_events.last_uci->ucis[0].pdu)); - const auto& f2 = variant_get(mac_events.last_uci->ucis[0].pdu); + ASSERT_TRUE(std::holds_alternative(mac_events.last_uci->ucis[0].pdu)); + const auto& f2 = std::get(mac_events.last_uci->ucis[0].pdu); // check SR info. ASSERT_TRUE(f2.sr_info.has_value()); ASSERT_EQ(f2.sr_info->size(), 1); @@ -348,11 +348,11 @@ TEST_P(mac_test_mode_adapter_test, when_uci_is_forwarded_to_mac_then_test_mode_c } else { ASSERT_EQ(*report.ri, params.test_ue_cfg.ri); if (params.nof_ports == 2) { - ASSERT_TRUE(variant_holds_alternative(report.pmi->type)); - ASSERT_EQ(variant_get(report.pmi->type).pmi, params.test_ue_cfg.pmi); + ASSERT_TRUE(std::holds_alternative(report.pmi->type)); + ASSERT_EQ(std::get(report.pmi->type).pmi, params.test_ue_cfg.pmi); } else { - ASSERT_TRUE(variant_holds_alternative(report.pmi->type)); - auto& t = variant_get(report.pmi->type); + ASSERT_TRUE(std::holds_alternative(report.pmi->type)); + auto& t = std::get(report.pmi->type); ASSERT_EQ(t.i_1_1, params.test_ue_cfg.i_1_1); if (t.i_1_3.has_value()) { ASSERT_EQ(*t.i_1_3, params.test_ue_cfg.i_1_3); diff --git a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp index cd64f35464..2e6ba39129 100644 --- a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp +++ b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.cpp @@ -29,6 +29,7 @@ #include "srsran/asn1/f1ap/common.h" #include "srsran/du/du_cell_config_helpers.h" #include "srsran/du_high/du_high_factory.h" +#include "srsran/support/error_handling.h" #include "srsran/support/test_utils.h" using namespace srsran; @@ -151,7 +152,8 @@ static void init_loggers() srslog::fetch_basic_logger("SCHED", true).set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("RLC").set_level(srslog::basic_levels::info); srslog::fetch_basic_logger("DU-MNG").set_level(srslog::basic_levels::debug); - srslog::fetch_basic_logger("DU-F1").set_level(srslog::basic_levels::debug); + srslog::fetch_basic_logger("DU-F1-U").set_level(srslog::basic_levels::warning); + srslog::fetch_basic_logger("DU-F1").set_level(srslog::basic_levels::info); srslog::fetch_basic_logger("ASN1").set_level(srslog::basic_levels::debug); srslog::fetch_basic_logger("TEST").set_level(srslog::basic_levels::debug); srslog::init(); @@ -169,7 +171,7 @@ du_high_env_simulator::du_high_env_simulator(du_high_env_sim_params params) : cfg.f1u_gw = &cu_up_sim; cfg.phy_adapter = &phy; cfg.timers = &timers; - cfg.gnb_du_id = 0; + cfg.gnb_du_id = gnb_du_id_t::min; cfg.gnb_du_name = "srsdu"; cfg.du_bind_addr = transport_layer_address::create_from_string("127.0.0.1"); @@ -229,10 +231,44 @@ bool du_high_env_simulator::add_ue(rnti_t rnti, du_cell_index_t cell_index) // Send UL-CCCH message. du_hi->get_pdu_handler().handle_rx_data_indication(test_helpers::create_ccch_message(next_slot, rnti, cell_index)); - // Wait for Init UL RRC Message to come out of the F1AP. - bool ret = - run_until([this]() { return not cu_notifier.last_f1ap_msgs.empty(); }, 1000 * (next_slot.numerology() + 1)); - if (not ret or not test_helpers::is_init_ul_rrc_msg_transfer_valid(cu_notifier.last_f1ap_msgs.back(), rnti)) { + // Wait for Init UL RRC Message to come out of the F1AP and ConRes CE to be scheduled. + // Note: These events are concurrent. + bool init_ul_rrc_msg_flag = false; + auto init_ul_rrc_msg_sent = [this, rnti, &init_ul_rrc_msg_flag]() { + if (init_ul_rrc_msg_flag) { + return; + } + if (not cu_notifier.last_f1ap_msgs.empty()) { + report_fatal_error_if_not( + test_helpers::is_init_ul_rrc_msg_transfer_valid(cu_notifier.last_f1ap_msgs.back(), rnti), + "Init UL RRC Message is not valid"); + init_ul_rrc_msg_flag = true; + return; + } + }; + bool conres_sent = false; + auto con_res_ce_sent = [this, rnti, cell_index, &conres_sent]() { + if (conres_sent) { + return; + } + phy_cell_test_dummy& phy_cell = phy.cells[cell_index]; + if (phy_cell.last_dl_res.has_value()) { + auto& dl_res = *phy_cell.last_dl_res.value().dl_res; + if (find_ue_pdsch(rnti, dl_res.ue_grants) != nullptr) { + report_fatal_error_if_not(find_ue_pdsch_with_lcid(rnti, lcid_dl_sch_t::UE_CON_RES_ID, dl_res.ue_grants) != + nullptr, + "UE ConRes not scheduled"); + conres_sent = true; + return; + } + } + }; + if (not run_until([&]() { + init_ul_rrc_msg_sent(); + con_res_ce_sent(); + return conres_sent and init_ul_rrc_msg_flag; + })) { + test_logger.error("rnti={}: Unable to add UE. Timeout waiting for Init UL RRC Message or ConRes CE", rnti); return false; } @@ -241,13 +277,14 @@ bool du_high_env_simulator::add_ue(rnti_t rnti, du_cell_index_t cell_index) gnb_cu_ue_f1ap_id_t cu_ue_id = int_to_gnb_cu_ue_f1ap_id(next_cu_ue_id++); EXPECT_TRUE(ues.insert(std::make_pair(rnti, ue_sim_context{rnti, du_ue_id, cu_ue_id, cell_index})).second); - return ret; + return true; } bool du_high_env_simulator::run_rrc_setup(rnti_t rnti) { auto it = ues.find(rnti); if (it == ues.end()) { + test_logger.error("rnti={}: Failed to run RRC Setup procedure. Cause: UE not found", rnti); return false; } const ue_sim_context& u = it->second; @@ -258,22 +295,16 @@ bool du_high_env_simulator::run_rrc_setup(rnti_t rnti) *u.du_ue_id, *u.cu_ue_id, srb_id_t::srb0, byte_buffer::create({0x1, 0x2, 0x3}).value()); du_hi->get_f1ap_message_handler().handle_message(msg); - // Wait for contention resolution to be sent to the PHY. + // Wait for Msg4 to be sent to the PHY. bool ret = run_until([&]() { if (phy_cell.last_dl_res.has_value() and phy_cell.last_dl_res.value().dl_res != nullptr) { auto& dl_res = *phy_cell.last_dl_res.value().dl_res; - for (const dl_msg_alloc& grant : dl_res.ue_grants) { - if (grant.pdsch_cfg.rnti == rnti and - std::any_of(grant.tb_list[0].lc_chs_to_sched.begin(), - grant.tb_list[0].lc_chs_to_sched.end(), - [](const dl_msg_lc_info& lc_grant) { return lc_grant.lcid == lcid_dl_sch_t::UE_CON_RES_ID; })) { - return true; - } - } + return find_ue_pdsch_with_lcid(rnti, LCID_SRB0, dl_res.ue_grants) != nullptr; } return false; }); if (not ret) { + test_logger.error("rnti={}: Msg4 not sent to the PHY", rnti); return false; } @@ -289,6 +320,7 @@ bool du_high_env_simulator::run_rrc_setup(rnti_t rnti) test_helpers::create_pdu_with_sdu(next_slot, rnti, lcid_t::LCID_SRB1)); ret = run_until([this]() { return not cu_notifier.last_f1ap_msgs.empty(); }); if (not ret or not test_helpers::is_ul_rrc_msg_transfer_valid(cu_notifier.last_f1ap_msgs.back(), srb_id_t::srb1)) { + test_logger.error("rnti={}: F1AP UL RRC Message (containing rrcSetupComplete) not sent or is invalid", rnti); return false; } return true; @@ -361,68 +393,10 @@ bool du_high_env_simulator::run_ue_context_setup(rnti_t rnti) return true; } -bool du_high_env_simulator::force_ue_fallback(rnti_t rnti) -{ - auto it = ues.find(rnti); - if (it == ues.end()) { - return false; - } - const ue_sim_context& u = it->second; - const auto& phy_cell = phy.cells[u.pcell_index]; - - // For the UE to transition to non-fallback mode, the GNB needs to receive either an SR or CSI pkus then 2 CRC = OK. - // In the following, we force 2 SRs, which in turn will 2 PUSCH. We also need to force 2 CRC=OK corresponding to each - // of the PUSCH. - for (unsigned crc_cnt = 0; crc_cnt != 2; ++crc_cnt) { - const unsigned max_slot_count = 100; - // Run until the slot the SR PUCCH is scheduled for. - optional slot_sr = nullopt; - for (unsigned count = 0; count != max_slot_count; ++count) { - const bool found_sr = phy_cell.last_ul_res.has_value() and phy_cell.last_ul_res.value().ul_res != nullptr and - find_ue_pucch_with_sr(rnti, phy_cell.last_ul_res.value().ul_res->pucchs) != nullptr; - if (found_sr) { - slot_sr = next_slot; - break; - } - run_slot(); - } - - // Enforce an UCI indication for the SR; this will trigger the SRB1 fallback scheduler to allocate a PUSCH grant. - if (slot_sr.has_value()) { - static_vector pucchs{ - pucch_info{.crnti = rnti, - .format = pucch_format::FORMAT_1, - .format_1 = {.sr_bits = sr_nof_bits::one, .harq_ack_nof_bits = 0}}}; - mac_uci_indication_message uci_ind = test_helpers::create_uci_indication(*slot_sr, pucchs); - } else { - return false; - } - - // Search for the PUSCH grant and force a CRC indication with OK. - for (unsigned count = 0; count != max_slot_count; ++count) { - const ul_sched_info* pusch = nullptr; - if (phy_cell.last_ul_res.has_value() and phy_cell.last_ul_res.value().ul_res != nullptr) { - pusch = find_ue_pusch(rnti, phy_cell.last_ul_res.value().ul_res->puschs); - if (pusch != nullptr) { - du_hi->get_control_info_handler(it->second.pcell_index) - .handle_crc(test_helpers::create_crc_indication( - phy_cell.last_ul_res->slot, rnti, to_harq_id(pusch->pusch_cfg.harq_id))); - if (crc_cnt == 1) { - return true; - } - break; - } - } - run_slot(); - } - } - return false; -} - void du_high_env_simulator::run_slot() { for (unsigned i = 0; i != du_high_cfg.cells.size(); ++i) { - // Signal slot indication to l2. + // Dispatch a slot indication to each cell in the L2. du_hi->get_slot_handler(to_du_cell_index(i)).handle_slot_indication(next_slot); // Wait for slot indication to be processed and the l2 results to be sent back to the l1 (in this case, the test @@ -439,7 +413,7 @@ void du_high_env_simulator::run_slot() << fmt::format("Slot={} failed to be processed (last processed slot={}). Is there a deadlock?", next_slot, phy.cells[i].last_slot_res); - const optional& dl_result = phy.cells[i].last_dl_res; + const std::optional& dl_result = phy.cells[i].last_dl_res; if (dl_result.has_value()) { EXPECT_TRUE(dl_result->slot == next_slot); } @@ -465,5 +439,15 @@ void du_high_env_simulator::handle_slot_results(du_cell_index_t cell_index) mac_uci_indication_message uci_ind = test_helpers::create_uci_indication(sl_rx, ul_res.pucchs); this->du_hi->get_control_info_handler(cell_index).handle_uci(uci_ind); } + + if (not ul_res.puschs.empty()) { + mac_crc_indication_message crc_ind = test_helpers::create_crc_indication(sl_rx, ul_res.puschs); + this->du_hi->get_control_info_handler(cell_index).handle_crc(crc_ind); + + std::optional uci_ind = test_helpers::create_uci_indication(sl_rx, ul_res.puschs); + if (uci_ind.has_value()) { + this->du_hi->get_control_info_handler(cell_index).handle_uci(uci_ind.value()); + } + } } } diff --git a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.h b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.h index 8f5e7160a9..becb565b51 100644 --- a/tests/integrationtests/du_high/test_utils/du_high_env_simulator.h +++ b/tests/integrationtests/du_high/test_utils/du_high_env_simulator.h @@ -71,8 +71,6 @@ class du_high_env_simulator bool run_ue_context_setup(rnti_t rnti); - bool force_ue_fallback(rnti_t rnti); - void run_slot(); bool run_until(unique_function condition, unsigned max_slot_count = 1000); @@ -96,10 +94,10 @@ class du_high_env_simulator private: struct ue_sim_context { - rnti_t rnti = rnti_t::INVALID_RNTI; - optional du_ue_id; - optional cu_ue_id; - du_cell_index_t pcell_index; + rnti_t rnti = rnti_t::INVALID_RNTI; + std::optional du_ue_id; + std::optional cu_ue_id; + du_cell_index_t pcell_index; }; std::unordered_map ues; diff --git a/tests/integrationtests/du_high_cu/cu_du_test.cpp b/tests/integrationtests/du_high_cu/cu_du_test.cpp index 0aa2fb5be2..3a760112c3 100644 --- a/tests/integrationtests/du_high_cu/cu_du_test.cpp +++ b/tests/integrationtests/du_high_cu/cu_du_test.cpp @@ -88,6 +88,7 @@ class cu_du_test : public ::testing::Test srsran::srs_du::du_high_configuration du_cfg{}; du_cfg.exec_mapper = &workers.exec_mapper; du_cfg.f1c_client = &f1c_gw; + du_cfg.f1u_gw = &f1u_gw; du_cfg.phy_adapter = &phy; du_cfg.timers = &timers; du_cfg.cells = {config_helpers::make_default_du_cell_config()}; @@ -106,6 +107,7 @@ class cu_du_test : public ::testing::Test timer_manager timers; srslog::basic_logger& test_logger = srslog::fetch_basic_logger("TEST"); f1c_test_local_gateway f1c_gw{}; + f1u_test_local_gateway f1u_gw{}; std::unique_ptr amf{srs_cu_cp::create_mock_amf()}; std::unique_ptr cu_cp_obj; diff --git a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp index 56d385f74f..b3e7ba23b6 100644 --- a/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp +++ b/tests/integrationtests/du_high_cu/du_high_cu_test_simulator.cpp @@ -168,7 +168,7 @@ void du_high_cu_test_simulator::start_dus() // Instantiate DU-high. srs_du::du_high_configuration& du_hi_cfg = du_ctxt.du_high_cfg; du_hi_cfg.gnb_du_name = fmt::format("srsgnb{}", du_idx + 1); - du_hi_cfg.gnb_du_id = du_idx + 1; + du_hi_cfg.gnb_du_id = (gnb_du_id_t)(du_idx + 1); du_hi_cfg.du_bind_addr = transport_layer_address::create_from_string(fmt::format("127.0.0.{}", du_idx + 1)); du_hi_cfg.exec_mapper = workers.du_hi_exec_mappers[du_idx].get(); du_hi_cfg.f1c_client = &f1c_gw; @@ -196,8 +196,8 @@ void du_high_cu_test_simulator::run_slot() // Wait for slot indication to be processed and the l2 results to be sent back to the l1 (in this case, the test // main thread). - const unsigned MAX_COUNT = 1000; - const optional& dl_result = dus[i]->phy.cells[0].last_dl_res; + const unsigned MAX_COUNT = 1000; + const std::optional& dl_result = dus[i]->phy.cells[0].last_dl_res; for (unsigned count = 0; count < MAX_COUNT and (not dl_result.has_value() or dl_result->slot != dus[i]->next_slot); ++count) { // Process tasks dispatched to the test main thread (e.g. L2 slot result) diff --git a/tests/integrationtests/ofh/ofh_integration_test.cpp b/tests/integrationtests/ofh/ofh_integration_test.cpp index b0e39c0a6a..2425b1d135 100644 --- a/tests/integrationtests/ofh/ofh_integration_test.cpp +++ b/tests/integrationtests/ofh/ofh_integration_test.cpp @@ -276,7 +276,7 @@ namespace { class dummy_frame_notifier : public ether::frame_notifier { // See interface for documentation. - void on_new_frame(span payload) override{}; + void on_new_frame(ether::unique_rx_buffer buffer) override{}; }; dummy_frame_notifier dummy_notifier; @@ -301,6 +301,19 @@ class test_ether_receiver : public ether::receiver std::reference_wrapper notifier; }; +/// Dummy Ethernet receive buffer. +class dummy_eth_rx_buffer : public ether::rx_buffer +{ +public: + /// Constructor receive a view on external data buffer managed by \c dummy_eth_receiver. + explicit dummy_eth_rx_buffer(span data_span_) { data_span = data_span_; } + + span data() const override { return data_span; }; + +private: + span data_span; +}; + /// Dummy Ethernet receiver that receives data from RU emulator and pushes them to the OFH receiver without using real /// Ethernet interface. class dummy_eth_receiver : public test_ether_receiver @@ -335,7 +348,9 @@ class dummy_eth_receiver : public test_ether_receiver std::lock_guard lock(mutex); read_pos = (read_pos + 1) % QUEUE_SIZE; } - notifier.get().on_new_frame(span(queue[read_pos].data(), queue[read_pos].size())); + ether::unique_rx_buffer buffer( + dummy_eth_rx_buffer{span(queue[read_pos].data(), queue[read_pos].size())}); + notifier.get().on_new_frame(std::move(buffer)); })) { std::this_thread::sleep_for(std::chrono::microseconds(10)); } @@ -1047,11 +1062,11 @@ static ru_ofh_dependencies generate_ru_dependencies(srslog::basic_logger& dependencies.error_notifier = &error_notifier; dependencies.sector_dependencies.emplace_back(); - auto& sector_deps = dependencies.sector_dependencies.back(); - sector_deps.logger = &logger; - sector_deps.downlink_executor = workers.ru_dl_exec; - sector_deps.receiver_executor = workers.ru_rx_exec; - sector_deps.transmitter_executor = workers.ru_tx_exec; + auto& sector_deps = dependencies.sector_dependencies.back(); + sector_deps.logger = &logger; + sector_deps.downlink_executor = workers.ru_dl_exec; + sector_deps.uplink_executor = workers.ru_rx_exec; + sector_deps.txrx_executor = workers.ru_tx_exec; // Configure Ethernet gateway. auto gateway = std::make_unique(); diff --git a/tests/integrationtests/ofh/ru_emulator.cpp b/tests/integrationtests/ofh/ru_emulator.cpp index 652154bc7e..01f67e1a5a 100644 --- a/tests/integrationtests/ofh/ru_emulator.cpp +++ b/tests/integrationtests/ofh/ru_emulator.cpp @@ -297,14 +297,10 @@ class ru_emulator : public frame_notifier } // See interface for documentation. - void on_new_frame(span payload) override + void on_new_frame(unique_rx_buffer buffer) override { - if (!executor.execute([this, payload, rx_payload = [payload]() -> std::array { - std::array buffer; - std::memcpy(buffer.data(), payload.data(), payload.size()); - return buffer; - }()]() { process_new_frame(span(rx_payload.data(), payload.size())); })) { - logger.warning("Failed to dispatch uplink task"); + if (!executor.execute([this, b = std::move(buffer)]() mutable { process_new_frame(std::move(b)); })) { + logger.warning("Failed to dispatch receiver task"); } } @@ -313,8 +309,10 @@ class ru_emulator : public frame_notifier unsigned get_statistics() const { return nof_rx_cplane_packets.load(std::memory_order_relaxed); } private: - void process_new_frame(span payload) + void process_new_frame(unique_rx_buffer buffer) { + span payload = buffer.data(); + if (!packet_inspector::is_uplink_cplane(payload, logger)) { return; } diff --git a/tests/integrationtests/ofh/ru_emulator_appconfig.h b/tests/integrationtests/ofh/ru_emulator_appconfig.h index 20b8361323..d91c520f56 100644 --- a/tests/integrationtests/ofh/ru_emulator_appconfig.h +++ b/tests/integrationtests/ofh/ru_emulator_appconfig.h @@ -70,7 +70,7 @@ struct ru_emulator_appconfig { /// Individual RU emulators configurations. std::vector ru_cfg = {{}}; /// DPDK configuration. - optional dpdk_config; + std::optional dpdk_config; }; } // namespace srsran diff --git a/tests/integrationtests/ofh/ru_emulator_cli11_schema.cpp b/tests/integrationtests/ofh/ru_emulator_cli11_schema.cpp index a99fe4643b..9346f06c71 100644 --- a/tests/integrationtests/ofh/ru_emulator_cli11_schema.cpp +++ b/tests/integrationtests/ofh/ru_emulator_cli11_schema.cpp @@ -41,7 +41,7 @@ static void configure_cli11_log_args(CLI::App& app, ru_emulator_log_appconfig& l app.add_option("--level", log_params.level, "Log level")->capture_default_str()->check(level_check); } -static void configure_cli11_ru_emu_dpdk_args(CLI::App& app, optional& config) +static void configure_cli11_ru_emu_dpdk_args(CLI::App& app, std::optional& config) { config.emplace(); diff --git a/tests/integrationtests/ofh/ru_emulator_dpdk_transceiver.cpp b/tests/integrationtests/ofh/ru_emulator_dpdk_transceiver.cpp index 9172f5bfd0..8650049af1 100644 --- a/tests/integrationtests/ofh/ru_emulator_dpdk_transceiver.cpp +++ b/tests/integrationtests/ofh/ru_emulator_dpdk_transceiver.cpp @@ -20,6 +20,7 @@ * */ +#include "../../../lib/ofh/ethernet/dpdk/dpdk_ethernet_rx_buffer_impl.h" #include "ru_emulator_transceiver.h" #include #include @@ -90,12 +91,8 @@ void dpdk_transceiver::receive() for (auto mbuf : span<::rte_mbuf*>(mbufs.data(), num_frames)) { ::rte_vlan_strip(mbuf); - - uint8_t* data = rte_pktmbuf_mtod(mbuf, uint8_t*); - unsigned length = mbuf->pkt_len; - notifier->on_new_frame(span(data, length)); - - ::rte_pktmbuf_free(mbuf); + dpdk_rx_buffer_impl buffer(mbuf); + notifier->on_new_frame(std::move(buffer)); } } diff --git a/tests/integrationtests/rlc/rlc_stress_test.cpp b/tests/integrationtests/rlc/rlc_stress_test.cpp index bd63383f61..d7bebd0a7c 100644 --- a/tests/integrationtests/rlc/rlc_stress_test.cpp +++ b/tests/integrationtests/rlc/rlc_stress_test.cpp @@ -34,7 +34,7 @@ stress_stack::stress_stack(const stress_test_args& args_, uint32_t id, rb_id_t r pcell_name("PCell-Worker-" + std::to_string(id)), ue_worker{ue_name, task_worker_queue_size}, pcell_worker{pcell_name, task_worker_queue_size}, - logger("STACK", {0, id, rb_id, "DL/UL"}) + logger("STACK", {gnb_du_id_t::min, id, rb_id, "DL/UL"}) { ue_executor = make_task_executor_ptr(ue_worker); pcell_executor = make_task_executor_ptr(pcell_worker); diff --git a/tests/integrationtests/rlc/rlc_stress_test_f1.h b/tests/integrationtests/rlc/rlc_stress_test_f1.h index c5f083267d..3325caef75 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_f1.h +++ b/tests/integrationtests/rlc/rlc_stress_test_f1.h @@ -41,7 +41,7 @@ class f1ap_dummy : public pdcp_tx_lower_notifier, pdcp_rx_lower_interface* pdcp_rx_lower = nullptr; public: - f1ap_dummy(uint32_t id) : logger("F1AP", {0, id, drb_id_t::drb1, "DL"}) {} + f1ap_dummy(uint32_t id) : logger("F1AP", {gnb_du_id_t::min, id, drb_id_t::drb1, "DL"}) {} // PDCP -> F1 -> RLC void on_new_pdu(pdcp_tx_pdu pdu) final diff --git a/tests/integrationtests/rlc/rlc_stress_test_mac.h b/tests/integrationtests/rlc/rlc_stress_test_mac.h index 711fa74ae2..1ecda7136b 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_mac.h +++ b/tests/integrationtests/rlc/rlc_stress_test_mac.h @@ -44,7 +44,7 @@ class mac_dummy : public rlc_tx_lower_layer_notifier public: mac_dummy(const stress_test_args& args_, uint32_t ue_id, rb_id_t rb_id) : - args(args_), logger("MAC", {0, ue_id, rb_id, "DL"}), rgen(args_.seed), bsr(0) + args(args_), logger("MAC", {(gnb_du_id_t)0, ue_id, rb_id, "DL"}), rgen(args_.seed), bsr(0) { } diff --git a/tests/integrationtests/rlc/rlc_stress_test_rrc.h b/tests/integrationtests/rlc/rlc_stress_test_rrc.h index 2d53777925..0966a88ae6 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_rrc.h +++ b/tests/integrationtests/rlc/rlc_stress_test_rrc.h @@ -32,7 +32,7 @@ class rrc_dummy : public pdcp_rx_upper_control_notifier, public pdcp_tx_upper_co rlc_bearer_logger logger; public: - explicit rrc_dummy(uint32_t id) : logger("RRC", {0, id, drb_id_t::drb1, "DL/UL"}) {} + explicit rrc_dummy(uint32_t id) : logger("RRC", {gnb_du_id_t::min, id, drb_id_t::drb1, "DL/UL"}) {} // PDCP -> RRC void on_integrity_failure() final {} diff --git a/tests/integrationtests/rlc/rlc_stress_test_traffic.h b/tests/integrationtests/rlc/rlc_stress_test_traffic.h index 3c39090f22..c0a40e228f 100644 --- a/tests/integrationtests/rlc/rlc_stress_test_traffic.h +++ b/tests/integrationtests/rlc/rlc_stress_test_traffic.h @@ -36,7 +36,7 @@ class stress_traffic_sink : public pdcp_rx_upper_data_notifier rlc_bearer_logger logger; public: - stress_traffic_sink(uint32_t ue_id, rb_id_t rb_id) : logger("TRAFF", {0, ue_id, rb_id, "UL"}) {} + stress_traffic_sink(uint32_t ue_id, rb_id_t rb_id) : logger("TRAFF", {(gnb_du_id_t)0, ue_id, rb_id, "UL"}) {} // pdcp_rx_upper_data_notifier interface void on_new_sdu(byte_buffer pdu) final; @@ -60,7 +60,7 @@ class stress_traffic_source args(args_), rgen(args_.seed), int_dist(args_.min_sdu_size, args_.max_sdu_size), - logger("TRAFF", {0, ue_id, rb_id, "DL"}) + logger("TRAFF", {(gnb_du_id_t)0, ue_id, rb_id, "DL"}) { } diff --git a/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp b/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp index e83783dcec..c95d6e60c1 100644 --- a/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp +++ b/tests/test_doubles/f1ap/f1ap_test_message_validators.cpp @@ -41,9 +41,9 @@ static bool is_packable(const f1ap_message& msg) return msg.pdu.pack(bref) == asn1::SRSASN_SUCCESS; } -bool srsran::test_helpers::is_init_ul_rrc_msg_transfer_valid(const f1ap_message& msg, - rnti_t rnti, - optional nci) +bool srsran::test_helpers::is_init_ul_rrc_msg_transfer_valid(const f1ap_message& msg, + rnti_t rnti, + const std::optional& nci) { TRUE_OR_RETURN(msg.pdu.type() == asn1::f1ap::f1ap_pdu_c::types_opts::init_msg); TRUE_OR_RETURN(msg.pdu.init_msg().proc_code == ASN1_F1AP_ID_INIT_UL_RRC_MSG_TRANSFER); diff --git a/tests/test_doubles/f1ap/f1ap_test_message_validators.h b/tests/test_doubles/f1ap/f1ap_test_message_validators.h index a17c6b91d5..26e78ac3af 100644 --- a/tests/test_doubles/f1ap/f1ap_test_message_validators.h +++ b/tests/test_doubles/f1ap/f1ap_test_message_validators.h @@ -36,7 +36,9 @@ class byte_buffer; namespace test_helpers { /// \brief Check if an F1AP message is a valid Initial UL RRC Message Transfer message. -bool is_init_ul_rrc_msg_transfer_valid(const f1ap_message& msg, rnti_t rnti, optional nci = {}); +bool is_init_ul_rrc_msg_transfer_valid(const f1ap_message& msg, + rnti_t rnti, + const std::optional& nci = {}); bool is_valid_dl_rrc_message_transfer(const f1ap_message& msg); @@ -53,4 +55,4 @@ bool is_valid_ue_context_modification_request(const f1ap_message& msg); bool is_valid_ue_context_release_command(const f1ap_message& msg); } // namespace test_helpers -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/tests/test_doubles/f1ap/f1ap_test_messages.cpp b/tests/test_doubles/f1ap/f1ap_test_messages.cpp index 0719d7561c..969f2913a2 100644 --- a/tests/test_doubles/f1ap/f1ap_test_messages.cpp +++ b/tests/test_doubles/f1ap/f1ap_test_messages.cpp @@ -175,9 +175,9 @@ static drbs_to_be_setup_item_s generate_drb_am_setup_item(drb_id_t drbid) return drb; } -f1ap_message srsran::test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t cu_ue_id, - optional du_ue_id, - const std::vector& drbs_to_setup) +f1ap_message srsran::test_helpers::create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t cu_ue_id, + std::optional du_ue_id, + const std::vector& drbs_to_setup) { using namespace asn1::f1ap; f1ap_message msg; diff --git a/tests/test_doubles/f1ap/f1ap_test_messages.h b/tests/test_doubles/f1ap/f1ap_test_messages.h index 639a47b392..59a8944016 100644 --- a/tests/test_doubles/f1ap/f1ap_test_messages.h +++ b/tests/test_doubles/f1ap/f1ap_test_messages.h @@ -59,9 +59,9 @@ f1ap_message generate_f1_removal_request(unsigned transaction_id); f1ap_message generate_f1_removal_response(const f1ap_message& f1_removal_request); /// \brief Generates dummy F1AP UE CONTEXT SETUP REQUEST message. -f1ap_message create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t cu_ue_id, - optional du_ue_id, - const std::vector& drbs_to_setup); +f1ap_message create_ue_context_setup_request(gnb_cu_ue_f1ap_id_t cu_ue_id, + std::optional du_ue_id, + const std::vector& drbs_to_setup); /// \brief Generates F1AP Initial UL RRC TRANSFER message. f1ap_message create_init_ul_rrc_message_transfer(gnb_du_ue_f1ap_id_t du_ue_id, @@ -97,4 +97,4 @@ generate_ue_context_modification_response(gnb_du_ue_f1ap_id_t du_ue_id, gnb_cu_u byte_buffer extract_dl_dcch_msg(const byte_buffer& rrc_container); } // namespace test_helpers -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/tests/test_doubles/f1ap/f1c_test_local_gateway.h b/tests/test_doubles/f1ap/f1c_test_local_gateway.h index 905165a390..ac907583db 100644 --- a/tests/test_doubles/f1ap/f1c_test_local_gateway.h +++ b/tests/test_doubles/f1ap/f1c_test_local_gateway.h @@ -25,6 +25,7 @@ #include "srsran/cu_cp/cu_cp_f1c_handler.h" #include "srsran/f1ap/common/f1ap_message.h" #include "srsran/f1ap/gateways/f1c_connection_client.h" +#include "srsran/f1u/du/f1u_gateway.h" namespace srsran { @@ -117,4 +118,23 @@ class f1c_test_local_gateway : public srs_du::f1c_connection_client std::vector> connections; }; +class f1u_test_local_gateway : public srs_du::f1u_du_gateway +{ + std::unique_ptr create_du_bearer(uint32_t ue_index, + drb_id_t drb_id, + srs_du::f1u_config config, + const up_transport_layer_info& dl_up_tnl_info, + const up_transport_layer_info& ul_up_tnl_info, + srs_du::f1u_du_gateway_bearer_rx_notifier& du_rx, + timer_factory timers, + task_executor& ue_executor) override + { + return nullptr; + } + + void remove_du_bearer(const up_transport_layer_info& dl_up_tnl_info) override {} + + expected get_du_bind_address(gnb_du_id_t du_index) override { return std::string("127.0.0.1"); } +}; + } // namespace srsran diff --git a/tests/test_doubles/f1u/dummy_f1u_du_gateway.h b/tests/test_doubles/f1u/dummy_f1u_du_gateway.h index 6f47aed960..c2cfded7cc 100644 --- a/tests/test_doubles/f1u/dummy_f1u_du_gateway.h +++ b/tests/test_doubles/f1u/dummy_f1u_du_gateway.h @@ -50,8 +50,8 @@ class cu_up_simulator : public f1u_du_gateway std::vector created_du_notifs; std::vector registered_dl_tnls; - optional last_ue_idx; - optional last_drb_id; + std::optional last_ue_idx; + std::optional last_drb_id; std::unique_ptr create_du_bearer(uint32_t ue_index, drb_id_t drb_id, @@ -80,6 +80,8 @@ class cu_up_simulator : public f1u_du_gateway } } } + + expected get_du_bind_address(gnb_du_id_t du_index) override { return std::string("127.0.0.1"); } }; } // namespace srs_du diff --git a/tests/test_doubles/mac/dummy_mac_result_notifier.h b/tests/test_doubles/mac/dummy_mac_result_notifier.h index ed7d8f9cd2..f4c6bc3b2f 100644 --- a/tests/test_doubles/mac/dummy_mac_result_notifier.h +++ b/tests/test_doubles/mac/dummy_mac_result_notifier.h @@ -35,19 +35,19 @@ struct phy_cell_test_dummy : public mac_cell_result_notifier { void on_new_uplink_scheduler_results(const mac_ul_sched_result& ul_res) override; void on_cell_results_completion(slot_point slot) override; - slot_point last_slot_res; - optional last_dl_res; - optional last_dl_data; - optional last_ul_res; + slot_point last_slot_res; + std::optional last_dl_res; + std::optional last_dl_data; + std::optional last_ul_res; private: task_executor& test_exec; dl_sched_result last_dl_sched_res; ul_sched_result last_ul_sched_res; - optional cached_dl_res; - optional cached_dl_data; - optional cached_ul_res; + std::optional cached_dl_res; + std::optional cached_dl_data; + std::optional cached_ul_res; }; /// \brief Dummy implementation of mac_cell_result_notifier that stores the last results in the class. The user can @@ -61,4 +61,4 @@ struct phy_test_dummy : public mac_result_notifier { std::vector cells; }; -} // namespace srsran \ No newline at end of file +} // namespace srsran diff --git a/tests/test_doubles/mac/mac_test_messages.cpp b/tests/test_doubles/mac/mac_test_messages.cpp index 81b0f48a98..b337e3cf36 100644 --- a/tests/test_doubles/mac/mac_test_messages.cpp +++ b/tests/test_doubles/mac/mac_test_messages.cpp @@ -58,6 +58,20 @@ mac_crc_indication_message srsran::test_helpers::create_crc_indication(slot_poin .crcs = {mac_crc_pdu{.rnti = rnti, .harq_id = h_id, .tb_crc_success = true}}}; } +mac_crc_indication_message srsran::test_helpers::create_crc_indication(slot_point sl_rx, + span puschs) +{ + mac_crc_indication_message crc_ind; + crc_ind.sl_rx = sl_rx; + crc_ind.crcs.resize(puschs.size()); + for (unsigned i = 0; i != puschs.size(); ++i) { + crc_ind.crcs[i].rnti = puschs[i].pusch_cfg.rnti; + crc_ind.crcs[i].harq_id = puschs[i].pusch_cfg.harq_id; + crc_ind.crcs[i].tb_crc_success = true; + } + return crc_ind; +} + mac_uci_pdu srsran::test_helpers::create_uci_pdu(const pucch_info& pucch) { mac_uci_pdu pdu{}; @@ -109,6 +123,30 @@ mac_uci_pdu srsran::test_helpers::create_uci_pdu(const pucch_info& pucch) return pdu; } +mac_uci_pdu srsran::test_helpers::create_uci_pdu(rnti_t rnti, const uci_info& pusch_uci) +{ + mac_uci_pdu pdu{}; + pdu.rnti = rnti; + auto& pusch_ind = pdu.pdu.emplace(); + pusch_ind.ul_sinr_dB = 100; + + if (pusch_uci.harq.has_value() and pusch_uci.harq->harq_ack_nof_bits > 0) { + auto& harq = pusch_uci.harq.value(); + pusch_ind.harq_info.emplace(); + pusch_ind.harq_info->is_valid = true; + pusch_ind.harq_info->payload.resize(harq.harq_ack_nof_bits); + pusch_ind.harq_info->payload.fill(true); + } + if (pusch_uci.csi.has_value() and pusch_uci.csi->csi_part1_nof_bits > 0) { + pusch_ind.csi_part1_info.emplace(); + pusch_ind.csi_part1_info->is_valid = true; + pusch_ind.csi_part1_info->payload.resize(pusch_uci.csi->csi_part1_nof_bits); + pusch_ind.csi_part1_info->payload.fill(true); + } + + return pdu; +} + mac_uci_indication_message srsran::test_helpers::create_uci_indication(slot_point sl_rx, span pucchs) { mac_uci_indication_message uci_ind; @@ -118,3 +156,16 @@ mac_uci_indication_message srsran::test_helpers::create_uci_indication(slot_poin } return uci_ind; } + +std::optional srsran::test_helpers::create_uci_indication(slot_point sl_rx, + span puschs) +{ + mac_uci_indication_message uci_ind; + uci_ind.sl_rx = sl_rx; + for (const auto& pusch : puschs) { + if (pusch.uci.has_value()) { + uci_ind.ucis.push_back(create_uci_pdu(pusch.pusch_cfg.rnti, pusch.uci.value())); + } + } + return uci_ind.ucis.empty() ? std::nullopt : std::make_optional(uci_ind); +} diff --git a/tests/test_doubles/mac/mac_test_messages.h b/tests/test_doubles/mac/mac_test_messages.h index 382b623243..9090fd0c7d 100644 --- a/tests/test_doubles/mac/mac_test_messages.h +++ b/tests/test_doubles/mac/mac_test_messages.h @@ -25,6 +25,7 @@ #include "srsran/mac/mac_cell_control_information_handler.h" #include "srsran/mac/mac_pdu_handler.h" #include "srsran/scheduler/harq_id.h" +#include "srsran/scheduler/scheduler_slot_handler.h" namespace srsran { @@ -42,9 +43,16 @@ mac_rx_data_indication create_pdu_with_sdu(slot_point sl_rx, rnti_t rnti, lcid_t /// set as 1s. mac_uci_pdu create_uci_pdu(const pucch_info& pucch); +/// Generate MAC UCI PDU out of a PUSCH UCI PDU indication, with all HARQ-ACKs set to ACK and CSI set as 1s. +mac_uci_pdu create_uci_pdu(rnti_t rnti, const uci_info& pusch_uci); + mac_uci_indication_message create_uci_indication(slot_point sl_rx, span pucchs); +std::optional create_uci_indication(slot_point sl_rx, span puschs); + mac_crc_indication_message create_crc_indication(slot_point sl_rx, rnti_t rnti, harq_id_t h_id); +mac_crc_indication_message create_crc_indication(slot_point sl_rx, span puschs); + } // namespace test_helpers } // namespace srsran diff --git a/tests/test_doubles/scheduler/CMakeLists.txt b/tests/test_doubles/scheduler/CMakeLists.txt index 3beb380a9b..6772d23255 100644 --- a/tests/test_doubles/scheduler/CMakeLists.txt +++ b/tests/test_doubles/scheduler/CMakeLists.txt @@ -18,6 +18,6 @@ # and at http://www.gnu.org/licenses/. # -add_library(sched_test_doubles pucch_res_test_builder_helper.cpp) +add_library(sched_test_doubles pucch_res_test_builder_helper.cpp scheduler_result_test.cpp) set_target_properties(sched_test_doubles PROPERTIES UNITY_BUILD ON) target_link_libraries(sched_test_doubles srsran_sched srslog) \ No newline at end of file diff --git a/tests/test_doubles/scheduler/scheduler_result_test.cpp b/tests/test_doubles/scheduler/scheduler_result_test.cpp new file mode 100644 index 0000000000..cbfe40dd51 --- /dev/null +++ b/tests/test_doubles/scheduler/scheduler_result_test.cpp @@ -0,0 +1,57 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "scheduler_result_test.h" + +using namespace srsran; + +const dl_msg_alloc* srsran::find_ue_pdsch_with_lcid(rnti_t rnti, lcid_dl_sch_t lcid, span dlgrants) +{ + auto it = std::find_if(dlgrants.begin(), dlgrants.end(), [rnti, lcid](const dl_msg_alloc& pdsch) { + return pdsch.pdsch_cfg.rnti == rnti and std::any_of(pdsch.tb_list[0].lc_chs_to_sched.begin(), + pdsch.tb_list[0].lc_chs_to_sched.end(), + [lcid](const dl_msg_lc_info& t) { return t.lcid == lcid; }); + }); + return it != dlgrants.end() ? &*it : nullptr; +} + +const dl_msg_alloc* srsran::find_ue_pdsch_with_lcid(rnti_t rnti, lcid_t lcid, span dlgrants) +{ + return find_ue_pdsch_with_lcid(rnti, lcid_dl_sch_t{lcid}, dlgrants); +} + +const csi_report_configuration* srsran::find_ue_uci_with_csi(rnti_t rnti, const ul_sched_result& ul_res) +{ + if (ul_res.nof_ul_symbols == 0) { + return nullptr; + } + const pucch_info* pucch = find_ue_pucch_with_csi(rnti, ul_res.pucchs); + if (pucch != nullptr) { + return &pucch->csi_rep_cfg.value(); + } + for (const ul_sched_info& ul : ul_res.puschs) { + if (ul.pusch_cfg.rnti == rnti and ul.uci.has_value() and ul.uci.value().csi.has_value()) { + return &ul.uci.value().csi.value().csi_rep_cfg; + } + } + return nullptr; +} diff --git a/tests/test_doubles/scheduler/scheduler_result_test.h b/tests/test_doubles/scheduler/scheduler_result_test.h index 315adf6c96..9bb51f9a03 100644 --- a/tests/test_doubles/scheduler/scheduler_result_test.h +++ b/tests/test_doubles/scheduler/scheduler_result_test.h @@ -49,15 +49,8 @@ inline const dl_msg_alloc* find_ue_pdsch(rnti_t rnti, span d return it != dlgrants.end() ? &*it : nullptr; } -inline const dl_msg_alloc* find_ue_pdsch_with_lcid(rnti_t rnti, lcid_t lcid, span dlgrants) -{ - auto it = std::find_if(dlgrants.begin(), dlgrants.end(), [rnti, lcid](const dl_msg_alloc& pdsch) { - return pdsch.pdsch_cfg.rnti == rnti and std::any_of(pdsch.tb_list[0].lc_chs_to_sched.begin(), - pdsch.tb_list[0].lc_chs_to_sched.end(), - [lcid](const dl_msg_lc_info& t) { return t.lcid == lcid; }); - }); - return it != dlgrants.end() ? &*it : nullptr; -} +const dl_msg_alloc* find_ue_pdsch_with_lcid(rnti_t rnti, lcid_dl_sch_t lcid, span dlgrants); +const dl_msg_alloc* find_ue_pdsch_with_lcid(rnti_t rnti, lcid_t lcid, span dlgrants); inline const ul_sched_info* find_ue_pusch(rnti_t rnti, span ulgrants) { @@ -116,4 +109,7 @@ inline const pucch_info* find_ue_pucch_with_csi(rnti_t rnti, span bytes{0x1, 0x2, 0x3, 0x4, 0x5, 0xff}; - byte_buffer pdu = make_byte_buffer("0102030405FF"); + byte_buffer pdu = make_byte_buffer("0102030405FF").value(); ASSERT_EQ(pdu, bytes); } diff --git a/tests/unittests/adt/optional_test.cpp b/tests/unittests/adt/optional_test.cpp deleted file mode 100644 index 6199da68b6..0000000000 --- a/tests/unittests/adt/optional_test.cpp +++ /dev/null @@ -1,177 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "srsran/adt/optional.h" -#include "srsran/support/test_utils.h" -#include -#include - -// Disable GCC 5's -Wsuggest-override warnings in gtest. -#ifdef __clang__ -#pragma GCC diagnostic ignored "-Wall" -#else // __clang__ -#pragma GCC diagnostic ignored "-Wsuggest-override" -#endif // __clang__ - -using namespace srsran; - -std::random_device rd; -std::mt19937 g(rd()); - -unsigned get_random_int() -{ - return std::uniform_int_distribution{std::numeric_limits::min(), std::numeric_limits::max()}(g); -} - -template -class any_type_optional_tester : public ::testing::Test -{ -public: - using value_type = T; - - ~any_type_optional_tester() { TESTASSERT(moveonly_test_object::object_count() == 0); } -}; - -using any_types = ::testing::Types; -TYPED_TEST_SUITE(any_type_optional_tester, any_types); - -TYPED_TEST(any_type_optional_tester, optional_has_same_triviality_traits_as_value_type) -{ - using T = typename TestFixture::value_type; - - static_assert(std::is_trivially_destructible>::value == std::is_trivially_destructible::value, - "optional trait is incorrect"); - static_assert(std::is_trivially_copy_constructible>::value == - std::is_trivially_copy_constructible::value, - "optional trait is incorrect"); - static_assert(std::is_trivially_move_constructible>::value == - std::is_trivially_move_constructible::value, - "optional trait is incorrect"); - static_assert(std::is_trivially_copy_assignable>::value == std::is_trivially_copy_assignable::value, - "optional trait is incorrect"); - static_assert(std::is_trivially_move_assignable>::value == std::is_trivially_move_assignable::value, - "optional trait is incorrect"); -} - -TYPED_TEST(any_type_optional_tester, default_ctor_creates_empty_optional) -{ - using T = typename TestFixture::value_type; - optional val; - ASSERT_FALSE(val.has_value()); -} - -TYPED_TEST(any_type_optional_tester, value_ctor_sets_optional_value) -{ - using T = typename TestFixture::value_type; - int v = get_random_int(); - optional val(v); - ASSERT_TRUE(val.has_value()); - ASSERT_TRUE(static_cast(val)); - ASSERT_EQ(val.value(), v); - ASSERT_EQ(*val, v); -} - -TYPED_TEST(any_type_optional_tester, value_assign_sets_optional_value) -{ - using T = typename TestFixture::value_type; - optional val; - - int v = get_random_int(); - val = v; - ASSERT_TRUE(val.has_value()); - ASSERT_EQ(val.value(), v); -} - -TYPED_TEST(any_type_optional_tester, optional_operator_eq_compares_internal_values) -{ - using T = typename TestFixture::value_type; - optional opt1, opt2; - - // two empty optionals - ASSERT_EQ(opt1, opt2); - - // one empty and the other not empty. - int v = get_random_int(); - T val(v); - opt1 = v; - ASSERT_EQ(opt1, val); - ASSERT_EQ(val, opt1); - ASSERT_NE(opt2, val); - ASSERT_NE(val, opt2); - ASSERT_NE(opt1, opt2); - ASSERT_NE(opt2, opt1); - - // two equal optionals with values. - opt2 = v; - ASSERT_EQ(opt1, opt2); - ASSERT_EQ(opt2, opt1); - - // two unequal optional with values. - opt2 = v == std::numeric_limits::max() ? 0 : v + 1; - ASSERT_NE(opt1, opt2); - ASSERT_NE(opt2, opt1); - ASSERT_NE(opt2, val); - ASSERT_NE(val, opt2); -} - -TYPED_TEST(any_type_optional_tester, optional_move_calls_value_move) -{ - using T = typename TestFixture::value_type; - int v = get_random_int(); - T val(v); - optional opt1(v), opt2; - - opt2 = std::move(opt1); - ASSERT_TRUE(opt1.has_value()) << "Moving from non-empty optional does not delete optional value"; - ASSERT_TRUE(opt2.has_value()); - ASSERT_EQ(opt2, val); -} - -TYPED_TEST(any_type_optional_tester, optional_reset_sets_optional_empty) -{ - using T = typename TestFixture::value_type; - optional opt(get_random_int()); - - opt.reset(); - ASSERT_FALSE(opt.has_value()); - - opt.reset(); - ASSERT_FALSE(opt.has_value()) << "Failed to reset empty optional"; -} - -TYPED_TEST(any_type_optional_tester, optional_emplace_value) -{ - using T = typename TestFixture::value_type; - optional opt; - int v = get_random_int(), v2 = get_random_int(); - T val(v), val2(v2); - - // write on empty optional. - opt.emplace(v); - ASSERT_TRUE(opt.has_value()); - ASSERT_EQ(opt, val); - - // overwrite. - opt.emplace(v2); - ASSERT_TRUE(opt.has_value()); - ASSERT_EQ(opt, val2); -} diff --git a/tests/unittests/asn1/asn1_cause_conversion_test.cpp b/tests/unittests/asn1/asn1_cause_conversion_test.cpp index 6918bd585c..90e2d51bc8 100644 --- a/tests/unittests/asn1/asn1_cause_conversion_test.cpp +++ b/tests/unittests/asn1/asn1_cause_conversion_test.cpp @@ -64,54 +64,54 @@ TEST_F(asn1_cause_conversion_test, when_ngap_cause_received_then_conversion_to_c asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::unspecified; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::unspecified); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_radio_network_t::unspecified); asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::cell_not_available; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::cell_not_available); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_radio_network_t::cell_not_available); asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::up_confidentiality_protection_not_possible; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_radio_network_t::up_confidentiality_protection_not_possible); asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::ho_target_not_allowed; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::ho_target_not_allowed); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_radio_network_t::ho_target_not_allowed); asn1_cause.set_radio_network() = asn1::ngap::cause_radio_network_opts::user_inactivity; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_radio_network_t::user_inactivity); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_radio_network_t::user_inactivity); asn1_cause.set_transport() = asn1::ngap::cause_transport_opts::unspecified; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_transport_t::unspecified); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_transport_t::unspecified); asn1_cause.set_nas() = asn1::ngap::cause_nas_opts::deregister; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), cause_nas_t::deregister); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), cause_nas_t::deregister); asn1_cause.set_protocol() = asn1::ngap::cause_protocol_opts::msg_not_compatible_with_receiver_state; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), cause_protocol_t::msg_not_compatible_with_receiver_state); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), cause_protocol_t::msg_not_compatible_with_receiver_state); asn1_cause.set_misc() = asn1::ngap::cause_misc_opts::unspecified; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_misc_t::unspecified); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_misc_t::unspecified); asn1_cause.set_misc() = asn1::ngap::cause_misc_opts::unknown_plmn_or_sn_pn; ngap_cause = asn1_to_cause(asn1_cause); - ASSERT_TRUE(variant_holds_alternative(ngap_cause)); - ASSERT_EQ(variant_get(ngap_cause), ngap_cause_misc_t::unknown_plmn_or_sn_pn); + ASSERT_TRUE(std::holds_alternative(ngap_cause)); + ASSERT_EQ(std::get(ngap_cause), ngap_cause_misc_t::unknown_plmn_or_sn_pn); } // test conversion to ngap asn1 @@ -181,58 +181,58 @@ TEST_F(asn1_cause_conversion_test, when_f1ap_cause_received_then_conversion_to_c f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::unspecified; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_radio_network_t::unspecified); f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::cell_not_available; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::cell_not_available); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_radio_network_t::cell_not_available); f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::normal_release; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::normal_release); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_radio_network_t::normal_release); f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::gnb_cu_cell_capacity_exceeded; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::gnb_cu_cell_capacity_exceeded); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_radio_network_t::gnb_cu_cell_capacity_exceeded); f1ap_cause.set_radio_network() = asn1::f1ap::cause_radio_network_opts::rl_fail_others; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_radio_network_t::rl_fail_others); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_radio_network_t::rl_fail_others); f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unspecified; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_transport_t::unspecified); f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unknown_tnl_address_for_iab; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unknown_tnl_address_for_iab); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_transport_t::unknown_tnl_address_for_iab); f1ap_cause.set_transport() = asn1::f1ap::cause_transport_opts::unknown_up_tnl_info_for_iab; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), f1ap_cause_transport_t::unknown_up_tnl_info_for_iab); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), f1ap_cause_transport_t::unknown_up_tnl_info_for_iab); f1ap_cause.set_protocol() = asn1::f1ap::cause_protocol_opts::semantic_error; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_protocol_t::semantic_error); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_protocol_t::semantic_error); f1ap_cause.set_misc() = asn1::f1ap::cause_misc_opts::hardware_fail; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_misc_t::hardware_fail); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_misc_t::hardware_fail); f1ap_cause.set_misc() = asn1::f1ap::cause_misc_opts::unspecified; cause = asn1_to_cause(f1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_misc_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_misc_t::unspecified); } // test conversion to f1ap asn1 @@ -310,54 +310,54 @@ TEST_F(asn1_cause_conversion_test, when_e1ap_cause_received_then_conversion_to_c e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::unspecified; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_radio_network_t::unspecified); e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::unknown_or_inconsistent_pair_of_ue_e1ap_id; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_radio_network_t::unknown_or_inconsistent_pair_of_ue_e1ap_id); e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::normal_release; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::normal_release); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_radio_network_t::normal_release); e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::pdcp_cfg_not_supported; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::pdcp_cfg_not_supported); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_radio_network_t::pdcp_cfg_not_supported); e1ap_cause.set_radio_network() = asn1::e1ap::cause_radio_network_opts::report_characteristic_empty; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_radio_network_t::report_characteristic_empty); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_radio_network_t::report_characteristic_empty); e1ap_cause.set_transport() = asn1::e1ap::cause_transport_opts::unspecified; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_transport_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_transport_t::unspecified); e1ap_cause.set_transport() = asn1::e1ap::cause_transport_opts::unknown_tnl_address_for_iab; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), e1ap_cause_transport_t::unknown_tnl_address_for_iab); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), e1ap_cause_transport_t::unknown_tnl_address_for_iab); e1ap_cause.set_protocol() = asn1::e1ap::cause_protocol_opts::semantic_error; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_protocol_t::semantic_error); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_protocol_t::semantic_error); e1ap_cause.set_misc() = asn1::e1ap::cause_misc_opts::hardware_fail; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_misc_t::hardware_fail); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_misc_t::hardware_fail); e1ap_cause.set_misc() = asn1::e1ap::cause_misc_opts::unspecified; cause = asn1_to_cause(e1ap_cause); - ASSERT_TRUE(variant_holds_alternative(cause)); - ASSERT_EQ(variant_get(cause), cause_misc_t::unspecified); + ASSERT_TRUE(std::holds_alternative(cause)); + ASSERT_EQ(std::get(cause), cause_misc_t::unspecified); } // test conversion to e1ap asn1 diff --git a/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp b/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp index 2530da9b14..a11771f25a 100644 --- a/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_reestablishment_test.cpp @@ -309,16 +309,13 @@ TEST_F(cu_cp_reestablishment_test, EXPECT_TRUE(attach_ue(du_idx, old_du_ue_id, old_crnti, uint_to_amf_ue_id(0))); // Send RRC Reestablishment Request and DU receives RRC Reestablishment. - gnb_du_ue_f1ap_id_t new_du_ue_id = int_to_gnb_du_ue_f1ap_id(1); - rnti_t new_crnti = to_rnti(0x4602); - ASSERT_TRUE(reestablish_ue(du_idx, new_du_ue_id, new_crnti, old_crnti, old_pci)) << "Reestablishment failed"; - - // old UE should not be removed at this stage. - auto report = this->get_cu_cp().get_metrics_handler().request_metrics_report(); - ASSERT_EQ(report.ues.size(), 1) << "Old UE should not be removed yet"; + gnb_du_ue_f1ap_id_t du_ue_id2 = int_to_gnb_du_ue_f1ap_id(1); + rnti_t crnti2 = to_rnti(0x4602); + ASSERT_TRUE(send_rrc_reest_request_and_wait_response(du_ue_id2, crnti2, old_crnti, old_pci)) + << "RRC Reestablishment should have been sent"; // Run second Reestablishment. This should fail. - new_du_ue_id = int_to_gnb_du_ue_f1ap_id(2); - new_crnti = to_rnti(0x4603); - ASSERT_FALSE(reestablish_ue(du_idx, new_du_ue_id, new_crnti, old_crnti, old_pci)) << "Reestablishment failed"; + auto du_ue_id3 = int_to_gnb_du_ue_f1ap_id(2); + auto crnti3 = to_rnti(0x4603); + ASSERT_FALSE(reestablish_ue(du_idx, du_ue_id3, crnti3, old_crnti, old_pci)) << "Fallback should have occurred"; } diff --git a/tests/unittests/cu_cp/cu_cp_test.cpp b/tests/unittests/cu_cp/cu_cp_test.cpp index ec6cec9f72..8cc0a826fd 100644 --- a/tests/unittests/cu_cp/cu_cp_test.cpp +++ b/tests/unittests/cu_cp/cu_cp_test.cpp @@ -520,8 +520,10 @@ TEST_F(cu_cp_test, when_handover_request_received_then_handover_notify_is_sent) amf_ue_id_to_uint(amf_ue_id)); // Inject RRC Reconfiguration Complete with transaction_id=0 - 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("800008004e17dae3")); + 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("800008004e17dae3").value()); f1c_gw.get_du(du_index).on_new_message(rrc_recfg_complete); // Check that the Handover Notify was sent to the AMF diff --git a/tests/unittests/cu_cp/cu_cp_test_environment.cpp b/tests/unittests/cu_cp/cu_cp_test_environment.cpp index 77c70bf6f3..6e4aa4e0f9 100644 --- a/tests/unittests/cu_cp/cu_cp_test_environment.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_environment.cpp @@ -131,6 +131,7 @@ bool cu_cp_test_environment::tick_until(std::chrono::milliseconds timeout, const } // Push to CU-CP worker task taht checks the state of the condition. + done = false; cu_cp_workers->worker.push_task_blocking([&]() { // Need to tick the clock. tick(); @@ -341,7 +342,7 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ *ue_ctx.cu_ue_id, du_ue_id, srb_id_t::srb1, - make_byte_buffer("00013a0abf002b96882dac46355c4f34464ddaf7b43fde37ae8000000000")); + make_byte_buffer("00013a0abf002b96882dac46355c4f34464ddaf7b43fde37ae8000000000").value()); get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); // Wait for UL NAS Message (containing authentication response) @@ -367,7 +368,8 @@ bool cu_cp_test_environment::authenticate_ue(unsigned du_idx, gnb_du_ue_f1ap_id_ du_ue_id, srb_id_t::srb1, make_byte_buffer("00023a1cbf0243241cb5003f002f3b80048290a1b283800000f8b880103f0020bc800680807888787f800008192a3b4" - "c080080170170700c0080a980808000000000")); + "c080080170170700c0080a980808000000000") + .value()); get_du(du_idx).push_ul_pdu(ul_rrc_msg_transfer); // Wait for UL NAS Message (containing ue security mode complete) @@ -405,7 +407,7 @@ bool cu_cp_test_environment::setup_ue_security(unsigned du_idx, gnb_du_ue_f1ap_i // Inject RRC 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")); + 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 Initial Context Setup Response. @@ -447,7 +449,7 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, // 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("00043a053f015362c51680bf00218003fe6db7"))); + du_ue_id, *ue_ctx.cu_ue_id, srb_id_t::srb1, make_byte_buffer("00043a053f015362c51680bf00218003fe6db7").value())); bool result = this->wait_for_ngap_tx_pdu(ngap_pdu); report_fatal_error_if_not(result, "Failed to receive Registration Complete"); @@ -457,7 +459,8 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, *ue_ctx.cu_ue_id, srb_id_t::srb1, make_byte_buffer("00053a253f011ffa9203013f0033808018970080e0ffffc9d8bd8013404010880080000840830000000041830000000" - "00000800001800005000006000006800008800900c092838339b939b0b837002c98dcab"))); + "00000800001800005000006000006800008800900c092838339b939b0b837002c98dcab") + .value())); result = this->wait_for_ngap_tx_pdu(ngap_pdu); report_fatal_error_if_not(result, "Failed to receive Registration Complete"); @@ -465,7 +468,7 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, ngap_message dl_nas_transport_msg = generate_downlink_nas_transport_message( amf_ue_id, *ue_ctx.ran_ue_id, - make_byte_buffer("7e0205545bfc027e0054430f90004f00700065006e00350047005346004732800131235200490100")); + 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); report_fatal_error_if_not(result, "Failed to receive NAS Configuration Update Command"); @@ -488,7 +491,8 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, srb_id_t::srb1, make_byte_buffer("00064c821930680ce811d1968097e340e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" "a071e439f0000240400e0300000000100186c0000700809df0000000000000103a0002000012cb2800281c50f000700" - "0f00000004008010240a00126cc3c6"))); + "0f00000004008010240a00126cc3c6") + .value())); result = this->wait_for_e1ap_tx_pdu(0, e1ap_pdu); report_fatal_error_if_not(result, "Failed to receive E1AP Bearer Context Setup"); @@ -516,7 +520,7 @@ bool cu_cp_test_environment::attach_ue(unsigned du_idx, // 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("00070e00cc6fcda5"))); + du_ue_id, *ue_ctx.cu_ue_id, 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"); diff --git a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp index 924f56b58f..17b2bbaa23 100644 --- a/tests/unittests/cu_cp/cu_cp_test_helpers.cpp +++ b/tests/unittests/cu_cp/cu_cp_test_helpers.cpp @@ -236,7 +236,7 @@ void cu_cp_test::authenticate_ue(amf_ue_id_t amf_ue_id, cu_ue_id, du_ue_id, srb_id_t::srb1, - make_byte_buffer("00013a0abf002b96882dac46355c4f34464ddaf7b43fde37ae8000000000")); + make_byte_buffer("00013a0abf002b96882dac46355c4f34464ddaf7b43fde37ae8000000000").value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); // Inject DL NAS Transport message (ue security mode command) @@ -249,7 +249,8 @@ void cu_cp_test::authenticate_ue(amf_ue_id_t amf_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00023a1cbf0243241cb5003f002f3b80048290a1b283800000f8b880103f0020bc800680807888787f800008192a3b4" - "c080080170170700c0080a980808000000000")); + "c080080170170700c0080a980808000000000") + .value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); } @@ -264,8 +265,8 @@ void cu_cp_test::setup_security(amf_ue_id_t amf_ue_id, cu_cp_obj->get_ngap_message_handler().handle_message(init_ctxt_setup_req); // Inject Security Mode Complete - f1ap_message ul_rrc_msg_transfer = - generate_ul_rrc_message_transfer(cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00032a00fd5ec7ff")); + f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( + 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); } @@ -344,7 +345,7 @@ void cu_cp_test::test_preamble_ue_full_attach(du_index_t du_index, // Inject Registration Complete f1ap_message ul_rrc_msg_transfer = generate_ul_rrc_message_transfer( - cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00043a053f015362c51680bf00218003fe6db7")); + cu_ue_id, du_ue_id, srb_id_t::srb1, make_byte_buffer("00043a053f015362c51680bf00218003fe6db7").value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); // Inject PDU Session Establishment Request @@ -353,14 +354,15 @@ void cu_cp_test::test_preamble_ue_full_attach(du_index_t du_index, du_ue_id, srb_id_t::srb1, make_byte_buffer("00053a253f011ffa9203013f0033808018970080e0ffffc9d8bd8013404010880080000840830000000041830000000" - "00000800001800005000006000006800008800900c092838339b939b0b837002c98dcab")); + "00000800001800005000006000006800008800900c092838339b939b0b837002c98dcab") + .value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); // Inject Configuration Update Command ngap_message dl_nas_transport_msg = generate_downlink_nas_transport_message( amf_ue_id, ran_ue_id, - make_byte_buffer("7e0205545bfc027e0054430f90004f00700065006e00350047005346004732800131235200490100")); + make_byte_buffer("7e0205545bfc027e0054430f90004f00700065006e00350047005346004732800131235200490100").value()); cu_cp_obj->get_ngap_message_handler().handle_message(dl_nas_transport_msg); // Inject PDU Session Resource Setup Request @@ -380,7 +382,8 @@ void cu_cp_test::test_preamble_ue_full_attach(du_index_t du_index, srb_id_t::srb1, make_byte_buffer("00064c821930680ce811d1968097e340e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007" "a071e439f0000240400e0300000000100186c0000700809df0000000000000103a0002000012cb2800281c50f000700" - "0f00000004008010240a00126cc3c6")); + "0f00000004008010240a00126cc3c6") + .value()); f1c_gw.get_du(du_index).on_new_message(ul_rrc_msg_transfer); // check that the Bearer Context Setup was sent to the CU-UP @@ -423,8 +426,8 @@ void cu_cp_test::test_preamble_ue_full_attach(du_index_t du_index, 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("00070e00cc6fcda5")); + 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/mobility/inter_cu_handover_routine_test.cpp b/tests/unittests/cu_cp/mobility/inter_cu_handover_routine_test.cpp index f039b60458..ef12595a27 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 @@ -66,7 +66,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("000800400004015d3c18c0806bae872c411e548b")); + make_byte_buffer("000800400004015d3c18c0806bae872c411e548b").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 dc3152628a..610962e5bd 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 @@ -69,7 +69,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("000800410004015f741fe0804bf183fcaa6e9699")); + 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); } @@ -104,7 +104,8 @@ class inter_du_handover_routine_test : public mobility_test "0b8c8b5040c00504032014120d00505036014160e0050603a0141a120c506a0496302a72fd159e26f2681d2083c5df81821c000000" "38ffd294a5294f28160000219760000000000005000001456aa28023800c00041000710804e20070101084000e21009c200e040220" "8001c420138401c0c042100038840270c038200882000710804e18004000000410c04080c100e0d0000e388000000400800100c001" - "0120044014c00004620090e3800c")); + "0120044014c00004620090e3800c") + .value()); f1c_gw.get_du(target_du_index).on_new_message(ue_context_setup_resp); } @@ -152,8 +153,10 @@ class inter_du_handover_routine_test : public mobility_test /// \brief Inject RRC Reconfiguration Complete. void inject_rrc_reconfig_complete(std::optional transaction_id = {}) { - 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("8000080035c41efd")); + 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("8000080035c41efd").value()); f1c_gw.get_du(target_du_index).on_new_message(rrc_recfg_complete); } diff --git a/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp b/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp index f719a62d3b..b2ed06443e 100644 --- a/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/pdu_session_resource_modification_routine_test.cpp @@ -181,10 +181,10 @@ TEST_F(pdu_session_resource_modification_test, when_bearer_ctxt_modification_fai // Verify content of initial bearer modification request. ASSERT_TRUE(e1ap_bearer_ctxt_mng.first_e1ap_request.has_value()); - ASSERT_TRUE(variant_holds_alternative( + ASSERT_TRUE(std::holds_alternative( e1ap_bearer_ctxt_mng.first_e1ap_request.value())); const auto& bearer_ctxt_mod_req = - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); + std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); ASSERT_TRUE(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.has_value()); ASSERT_EQ(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.value().pdu_session_res_to_modify_list.size(), 1); ASSERT_EQ(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.value() @@ -362,10 +362,10 @@ TEST_F(pdu_session_resource_modification_test, // Verify content of initial bearer modification request. ASSERT_TRUE(e1ap_bearer_ctxt_mng.first_e1ap_request.has_value()); - ASSERT_TRUE(variant_holds_alternative( + ASSERT_TRUE(std::holds_alternative( e1ap_bearer_ctxt_mng.first_e1ap_request.value())); const auto& bearer_ctxt_mod_req = - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); + std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); ASSERT_TRUE(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.has_value()); ASSERT_EQ(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.value().pdu_session_res_to_modify_list.size(), 1); ASSERT_EQ(bearer_ctxt_mod_req.ng_ran_bearer_context_mod_request.value() diff --git a/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp b/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp index 78d5ca9008..d2520131aa 100644 --- a/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp +++ b/tests/unittests/cu_cp/routines/pdu_session_resource_setup_routine_test.cpp @@ -224,8 +224,7 @@ TEST_F(pdu_session_resource_setup_test, when_rrc_reconfiguration_succeeds_then_s start_procedure(request); // Verify validity of Bearer context setup. - is_valid_e1ap_message( - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); + is_valid_e1ap_message(std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); // Verify content of UE context modification which should include the setup of DRB1. const auto& ue_ctxt_mod_req = f1ap_ue_ctxt_mng.get_ctxt_mod_request(); @@ -330,9 +329,9 @@ TEST_F(pdu_session_resource_setup_test, when_setup_for_pdu_sessions_with_two_qos // Verify Bearer Context Setup request for two DRBs with one QoS flow each. ASSERT_TRUE( - variant_holds_alternative(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); + std::holds_alternative(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); const auto& context_setup_req = - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); + std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()); ASSERT_EQ(context_setup_req.pdu_session_res_to_setup_list.size(), 1); ASSERT_EQ(context_setup_req.pdu_session_res_to_setup_list.begin()->drb_to_setup_list_ng_ran.size(), 2); ASSERT_EQ(context_setup_req.pdu_session_res_to_setup_list.begin() @@ -431,8 +430,7 @@ TEST_F(pdu_session_resource_setup_test, when_two_consecutive_setups_arrive_beare VERIFY_EQUAL(rrc_srbs_to_add_mod_in_reconf(), {2}); // Verify generated messages can be packed into valid ASN.1 encoded messages - is_valid_e1ap_message( - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); + is_valid_e1ap_message(std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); is_valid_e1ap_message(e1ap_bearer_ctxt_mng.second_e1ap_request.value()); } @@ -465,10 +463,10 @@ TEST_F(pdu_session_resource_setup_test, when_two_consecutive_setups_arrive_beare ASSERT_TRUE(procedure_ready()); // Verify content of first bearer context modifications which should be the setup of a new PDU session. - ASSERT_TRUE(variant_holds_alternative( + ASSERT_TRUE(std::holds_alternative( e1ap_bearer_ctxt_mng.first_e1ap_request.value())); const auto& context_mod_req = - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()) + std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value()) .ng_ran_bearer_context_mod_request; ASSERT_TRUE(context_mod_req.has_value()); ASSERT_EQ(context_mod_req.value().pdu_session_res_to_setup_mod_list.size(), 1); @@ -476,7 +474,7 @@ TEST_F(pdu_session_resource_setup_test, when_two_consecutive_setups_arrive_beare // Verify generated messages can be packed into valid ASN.1 encoded messages is_valid_e1ap_message( - variant_get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); + std::get(e1ap_bearer_ctxt_mng.first_e1ap_request.value())); // Verify content of UE context modification which should include the setup of DRB2. const auto& ue_ctxt_mod_req = f1ap_ue_ctxt_mng.get_ctxt_mod_request(); diff --git a/tests/unittests/cu_cp/test_helpers.h b/tests/unittests/cu_cp/test_helpers.h index 85e00fbdcf..cc666b2606 100644 --- a/tests/unittests/cu_cp/test_helpers.h +++ b/tests/unittests/cu_cp/test_helpers.h @@ -30,7 +30,6 @@ #include "lib/cu_cp/du_processor/du_processor.h" #include "lib/cu_cp/ue_manager/ue_manager_impl.h" #include "tests/unittests/ngap/ngap_test_helpers.h" -#include "srsran/adt/variant.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/support/async/async_task.h" #include "srsran/support/async/async_test_utils.h" @@ -39,6 +38,7 @@ #include #include #include +#include namespace srsran { namespace srs_cu_cp { @@ -86,7 +86,7 @@ struct dummy_du_processor_cu_cp_notifier : public du_processor_cu_cp_notifier { byte_buffer on_target_cell_sib1_required(du_index_t du_index, nr_cell_global_id_t cgi) override { - return make_byte_buffer("deadbeef"); + return make_byte_buffer("deadbeef").value(); } async_task on_ue_removal_required(ue_index_t ue_index) override @@ -400,7 +400,7 @@ struct dummy_e1ap_bearer_context_manager : public e1ap_bearer_context_manager { second_e1ap_request.reset(); } - std::optional> + std::optional> first_e1ap_request; std::optional second_e1ap_request; @@ -472,7 +472,7 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { CORO_BEGIN(ctx); res.success = ue_context_setup_outcome; - res.du_to_cu_rrc_info.cell_group_cfg = make_byte_buffer("5800b24223c853a0120c7c080408c008"); + res.du_to_cu_rrc_info.cell_group_cfg = make_byte_buffer("5800b24223c853a0120c7c080408c008").value(); CORO_RETURN(res); }); @@ -497,7 +497,7 @@ struct dummy_f1ap_ue_context_manager : public f1ap_ue_context_manager { drb_item.drb_id = uint_to_drb_id(drb_id); // set ID res.drbs_setup_mod_list.emplace(drb_item.drb_id, drb_item); } - res.du_to_cu_rrc_info.cell_group_cfg = make_byte_buffer("5800b24223c853a0120c7c080408c008"); + res.du_to_cu_rrc_info.cell_group_cfg = make_byte_buffer("5800b24223c853a0120c7c080408c008").value(); // TODO: add failed list and other fields here .. CORO_RETURN(res); diff --git a/tests/unittests/cu_up/cu_up_test.cpp b/tests/unittests/cu_up/cu_up_test.cpp index 212f37a5f9..b93219b0ca 100644 --- a/tests/unittests/cu_up/cu_up_test.cpp +++ b/tests/unittests/cu_up/cu_up_test.cpp @@ -40,7 +40,7 @@ using namespace asn1::e1ap; /// This implementation returns back to the E1 interface a dummy CU-UP E1 Setup Response message upon the receival of /// the CU-UP E1 Setup Request message. -class dummy_cu_cp_handler : public e1ap_connection_client +class dummy_cu_cp_handler : public e1_connection_client { public: std::unique_ptr @@ -121,7 +121,7 @@ class cu_up_test : public ::testing::Test cfg.ctrl_executor = executor.get(); cfg.ue_exec_pool = exec_pool.get(); cfg.io_ul_executor = executor.get(); - cfg.e1ap.e1ap_conn_client = &e1ap_client; + cfg.e1ap.e1_conn_client = &e1ap_client; cfg.f1u_gateway = f1u_gw.get(); cfg.ngu_gw = ngu_gw.get(); cfg.timers = app_timers.get(); diff --git a/tests/unittests/cu_up/cu_up_test_helpers.h b/tests/unittests/cu_up/cu_up_test_helpers.h index f322f33618..136081cebb 100644 --- a/tests/unittests/cu_up/cu_up_test_helpers.h +++ b/tests/unittests/cu_up/cu_up_test_helpers.h @@ -116,7 +116,7 @@ class dummy_gtpu_teid_pool final : public gtpu_teid_pool bool full() const override { return true; }; - uint32_t get_max_teids() override { return UINT32_MAX; } + uint32_t get_max_nof_teids() override { return UINT32_MAX; } uint32_t next_teid = 0; }; @@ -235,9 +235,7 @@ class dummy_f1u_gateway final : public f1u_cu_up_gateway const srs_cu_up::f1u_config& config, const up_transport_layer_info& ul_up_tnl_info, f1u_cu_up_gateway_bearer_rx_notifier& rx_notifier, - task_executor& ul_exec, - timer_factory ue_dl_timer_factory, - unique_timer& ue_inactivity_timer) override + task_executor& ul_exec) override { created_ul_teid_list.push_back(ul_up_tnl_info.gtp_teid); bearer.connect_f1u_rx_sdu_notifier(rx_notifier); diff --git a/tests/unittests/du_manager/du_manager_test_helpers.cpp b/tests/unittests/du_manager/du_manager_test_helpers.cpp index 9550fcb440..239142ea02 100644 --- a/tests/unittests/du_manager/du_manager_test_helpers.cpp +++ b/tests/unittests/du_manager/du_manager_test_helpers.cpp @@ -115,7 +115,7 @@ du_manager_test_bench::du_manager_test_bench(span cells) : du_mng_exec(worker), ue_exec_mapper(worker), cell_exec_mapper(worker), - params{{"srsgnb", 1, 1, transport_layer_address::create_from_string("127.0.0.1"), du_cells}, + params{{"srsgnb", (gnb_du_id_t)1, 1, transport_layer_address::create_from_string("127.0.0.1"), du_cells}, {timers, du_mng_exec, ue_exec_mapper, cell_exec_mapper}, {f1ap, f1ap}, {f1u_gw}, diff --git a/tests/unittests/du_manager/du_manager_test_helpers.h b/tests/unittests/du_manager/du_manager_test_helpers.h index a73fa812f0..b4f5f32b1e 100644 --- a/tests/unittests/du_manager/du_manager_test_helpers.h +++ b/tests/unittests/du_manager/du_manager_test_helpers.h @@ -46,7 +46,7 @@ class dummy_teid_pool final : public gtpu_teid_pool bool full() const override { return false; } - uint32_t get_max_teids() override { return std::numeric_limits::max(); } + uint32_t get_max_nof_teids() override { return std::numeric_limits::max(); } private: uint32_t next_gtpu_teid = 0; @@ -208,6 +208,8 @@ class f1u_gateway_dummy : public f1u_du_gateway f1u_bearers.erase(bearer_it); } + expected get_du_bind_address(gnb_du_id_t du_index) override { return std::string("127.0.0.1"); } + std::map> f1u_bearers; }; 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 cb8eb4db3b..b09ea17910 100644 --- a/tests/unittests/du_manager/du_ran_resource_manager_test.cpp +++ b/tests/unittests/du_manager/du_ran_resource_manager_test.cpp @@ -47,7 +47,7 @@ class du_ran_resource_manager_tester_base qos_cfg_list)) { if (params.csi_rs_enabled) { - default_csi_pucch_res_cfg = srsran::variant_get( + default_csi_pucch_res_cfg = std::get( default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); } } @@ -81,7 +81,7 @@ class du_ran_resource_manager_tester_base { return serv_cell_cfg.csi_meas_cfg.has_value() and not serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and - srsran::variant_holds_alternative( + std::holds_alternative( serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); } @@ -90,7 +90,7 @@ class du_ran_resource_manager_tester_base { srsran_assert(has_ue_csi_cfg(serv_cell_cfg), "CSI configuration not found"); - return srsran::variant_get( + return std::get( serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); } @@ -172,7 +172,7 @@ class du_ran_resource_manager_tester : public du_ran_resource_manager_tester_bas { srsran_assert(default_ue_cell_cfg.csi_meas_cfg.has_value() and not default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and - srsran::variant_holds_alternative( + std::holds_alternative( default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type), "CSI report configuration is required for this unittest;"); } @@ -233,13 +233,12 @@ TEST_P(du_ran_resource_manager_tester, when_multiple_ues_are_created_then_they_u // Check if PUCCH config is correctly updated. const serving_cell_config serving_cell_cfg = ue_res->cells[0].serv_cell_cfg; std::optional csi_pucch_res{}; - const bool has_csi_cfg = - serving_cell_cfg.csi_meas_cfg.has_value() and - not serving_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and - srsran::variant_holds_alternative( - serving_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); + const bool has_csi_cfg = serving_cell_cfg.csi_meas_cfg.has_value() and + not serving_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and + std::holds_alternative( + serving_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); if (has_csi_cfg) { - csi_pucch_res.emplace(srsran::variant_get( + csi_pucch_res.emplace(std::get( serving_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type) .pucch_csi_res_list.front() .pucch_res_id.cell_res_id); @@ -344,7 +343,7 @@ class du_ran_res_mng_multiple_cfg_tester : public du_ran_resource_manager_tester { srsran_assert(default_ue_cell_cfg.csi_meas_cfg.has_value() and not default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and - srsran::variant_holds_alternative( + std::holds_alternative( default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type), "CSI report configuration is required for this unittest;"); } @@ -573,7 +572,7 @@ make_custom_du_cell_config_for_pucch_cnt(const pucch_cnt_builder_params& pucch_p 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()) { - variant_get( + std::get( du_cfg.ue_ded_serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type) .report_slot_period = pucch_params_.csi_period; } @@ -592,7 +591,7 @@ class du_ran_res_mng_pucch_cnt_tester : public du_ran_resource_manager_tester_ba { srsran_assert(default_ue_cell_cfg.csi_meas_cfg.has_value() and not default_ue_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty() and - srsran::variant_holds_alternative( + std::holds_alternative( 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 = diff --git a/tests/unittests/du_manager/du_ue/ue_manager_test.cpp b/tests/unittests/du_manager/du_ue/ue_manager_test.cpp index 7b14162073..dcb7d75a20 100644 --- a/tests/unittests/du_manager/du_ue/ue_manager_test.cpp +++ b/tests/unittests/du_manager/du_ue/ue_manager_test.cpp @@ -108,12 +108,13 @@ class du_ue_manager_tester : public ::testing::Test null_rlc_pcap rlc_pcap; dummy_ue_resource_configurator_factory cell_res_alloc; - du_manager_params params{{"srsgnb", 1, 1, transport_layer_address::create_from_string("127.0.0.1"), cells}, - {timers, worker, ue_execs, cell_execs}, - {f1ap_dummy, f1ap_dummy}, - {f1u_dummy}, - {mac_dummy, f1ap_dummy, f1ap_dummy, rlc_pcap}, - {mac_dummy, mac_dummy}}; + du_manager_params params{ + {"srsgnb", (gnb_du_id_t)1, 1, transport_layer_address::create_from_string("127.0.0.1"), cells}, + {timers, worker, ue_execs, cell_execs}, + {f1ap_dummy, f1ap_dummy}, + {f1u_dummy}, + {mac_dummy, f1ap_dummy, f1ap_dummy, rlc_pcap}, + {mac_dummy, mac_dummy}}; du_ue_manager ue_mng{params, cell_res_alloc}; }; diff --git a/tests/unittests/du_manager/pucch_resource_generator_test.cpp b/tests/unittests/du_manager/pucch_resource_generator_test.cpp index 952d313cfc..c4e065cb63 100644 --- a/tests/unittests/du_manager/pucch_resource_generator_test.cpp +++ b/tests/unittests/du_manager/pucch_resource_generator_test.cpp @@ -107,8 +107,8 @@ class pucch_grid void add_resource(const pucch_resource& res) { if (res.format == pucch_format::FORMAT_1) { - srsran_assert(variant_holds_alternative(res.format_params), "Expected PUCCH Format 1"); - const auto& res_f1 = variant_get(res.format_params); + srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 1"); + const auto& res_f1 = std::get(res.format_params); if (res.second_hop_prb.has_value()) { // First hop. @@ -148,8 +148,8 @@ class pucch_grid } } else if (res.format == srsran::pucch_format::FORMAT_2) { - srsran_assert(variant_holds_alternative(res.format_params), "Expected PUCCH Format 2"); - const auto& res_f2 = variant_get(res.format_params); + srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 2"); + const auto& res_f2 = std::get(res.format_params); if (res.second_hop_prb.has_value()) { // First hop. @@ -189,8 +189,8 @@ class pucch_grid bool verify_collision(const pucch_resource& res) const { if (res.format == pucch_format::FORMAT_1) { - srsran_assert(variant_holds_alternative(res.format_params), "Expected PUCCH Format 1"); - const auto& res_f1 = variant_get(res.format_params); + 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. if (res.second_hop_prb.has_value()) { // First hop. @@ -247,8 +247,8 @@ class pucch_grid } } } else if (res.format == srsran::pucch_format::FORMAT_2) { - srsran_assert(variant_holds_alternative(res.format_params), "Expected PUCCH Format 2"); - const auto& res_f2 = variant_get(res.format_params); + srsran_assert(std::holds_alternative(res.format_params), "Expected PUCCH Format 2"); + const auto& res_f2 = std::get(res.format_params); // Intra-slot frequency hopping. if (res.second_hop_prb.has_value()) { @@ -675,14 +675,14 @@ class test_ue_pucch_config_builder : public ::testing::TestWithParam( + std::holds_alternative( csi_cfg.csi_report_cfg_list.front().report_cfg_type) and - not variant_get( + not std::get( csi_cfg.csi_report_cfg_list.front().report_cfg_type) .pucch_csi_res_list.empty(), "PUCCH-CSI-ResourceList has not been configured in the CSI-reportConfig"); - const pucch_res_id_t csi_res_id = variant_get( + const pucch_res_id_t csi_res_id = std::get( csi_cfg.csi_report_cfg_list.front().report_cfg_type) .pucch_csi_res_list.front() .pucch_res_id; diff --git a/tests/unittests/du_manager/sib_test.cpp b/tests/unittests/du_manager/sib_test.cpp index 7729d0f850..9bd11f925e 100644 --- a/tests/unittests/du_manager/sib_test.cpp +++ b/tests/unittests/du_manager/sib_test.cpp @@ -23,7 +23,6 @@ #include "lib/du_manager/converters/f1ap_configuration_helpers.h" #include "srsran/asn1/rrc_nr/sys_info.h" #include "srsran/ran/sib/system_info_config.h" -#include #include #include @@ -35,12 +34,12 @@ TEST(srs_sib19_test, make_asn1_rrc_cell_sib19_buffer) sib19.cell_specific_koffset.emplace(); sib19.cell_specific_koffset.value() = 260; sib19.ephemeris_info.emplace(); - variant_get(sib19.ephemeris_info.value()).position_x = 1; - variant_get(sib19.ephemeris_info.value()).position_y = 2; - variant_get(sib19.ephemeris_info.value()).position_z = 3; - variant_get(sib19.ephemeris_info.value()).velocity_vx = 4; - variant_get(sib19.ephemeris_info.value()).velocity_vy = 5; - variant_get(sib19.ephemeris_info.value()).velocity_vz = 6; + std::get(sib19.ephemeris_info.value()).position_x = 1; + std::get(sib19.ephemeris_info.value()).position_y = 2; + std::get(sib19.ephemeris_info.value()).position_z = 3; + std::get(sib19.ephemeris_info.value()).velocity_vx = 4; + std::get(sib19.ephemeris_info.value()).velocity_vy = 5; + std::get(sib19.ephemeris_info.value()).velocity_vz = 6; // Call the function being tested std::string js_str; auto buf = srsran::srs_du::make_asn1_rrc_cell_sib19_buffer(sib19, &js_str); @@ -62,5 +61,5 @@ TEST(srs_sib19_test, make_asn1_rrc_cell_sib19_buffer) EXPECT_EQ(sib19_decoded.ntn_cfg_r17.cell_specific_koffset_r17, sib19.cell_specific_koffset); EXPECT_TRUE(sib19_decoded.ntn_cfg_r17.ephemeris_info_r17_present); EXPECT_EQ(sib19_decoded.ntn_cfg_r17.ephemeris_info_r17.position_velocity_r17().position_x_r17, - variant_get(sib19.ephemeris_info.value()).position_x); -} \ No newline at end of file + std::get(sib19.ephemeris_info.value()).position_x); +} diff --git a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp index 148d452cd1..7694f0578a 100644 --- a/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp +++ b/tests/unittests/e1ap/common/e1ap_cu_cp_test_messages.cpp @@ -92,10 +92,10 @@ e1ap_bearer_context_setup_request srsran::srs_cu_cp::generate_bearer_context_set request.ue_index = ue_index; request.security_info.security_algorithm.ciphering_algo = srsran::security::ciphering_algorithm::nea0; - request.security_info.up_security_key.encryption_key = make_byte_buffer("9950ab8083ed034257d900e9a6a06236"); - request.ue_dl_aggregate_maximum_bit_rate = 300000000; - request.serving_plmn = "00101"; - request.activity_notif_level = "ue"; + request.security_info.up_security_key.encryption_key = make_byte_buffer("9950ab8083ed034257d900e9a6a06236").value(); + request.ue_dl_aggregate_maximum_bit_rate = 300000000; + request.serving_plmn = "00101"; + request.activity_notif_level = "ue"; e1ap_pdu_session_res_to_setup_item res_to_setup_item; res_to_setup_item.pdu_session_id = uint_to_pdu_session_id(0); diff --git a/tests/unittests/e1ap/cu_cp/e1ap_test_local_gateway.h b/tests/unittests/e1ap/cu_cp/e1_test_local_gateway.h similarity index 93% rename from tests/unittests/e1ap/cu_cp/e1ap_test_local_gateway.h rename to tests/unittests/e1ap/cu_cp/e1_test_local_gateway.h index ee780eca18..3e54411493 100644 --- a/tests/unittests/e1ap/cu_cp/e1ap_test_local_gateway.h +++ b/tests/unittests/e1ap/cu_cp/e1_test_local_gateway.h @@ -24,7 +24,7 @@ #include "srsran/cu_cp/cu_cp_e1_handler.h" #include "srsran/e1ap/common/e1ap_message.h" -#include "srsran/e1ap/cu_up/e1ap_connection_client.h" +#include "srsran/e1ap/gateways/e1_connection_client.h" namespace srsran { @@ -63,13 +63,11 @@ class test_e1ap_message_notifier : public e1ap_message_notifier /// \brief Test helper class that creates an E1AP gateway for co-located setups (CU-CP and CU-UP in the same process), /// and stores the messages received by the CU-CP and CU-UP for testing purposes. -class e1ap_test_local_gateway : public srs_cu_up::e1ap_connection_client +class e1_test_local_gateway : public srs_cu_up::e1_connection_client { public: - e1ap_test_local_gateway() = default; - explicit e1ap_test_local_gateway(srs_cu_cp::cu_cp_e1_handler& cu_cp_cu_up_mng_) : cu_cp_cu_up_mng(&cu_cp_cu_up_mng_) - { - } + e1_test_local_gateway() = default; + explicit e1_test_local_gateway(srs_cu_cp::cu_cp_e1_handler& cu_cp_cu_up_mng_) : cu_cp_cu_up_mng(&cu_cp_cu_up_mng_) {} void attach_cu_cp_cu_up_repo(srs_cu_cp::cu_cp_e1_handler& cu_cp_cu_up_mng_) { cu_cp_cu_up_mng = &cu_cp_cu_up_mng_; } diff --git a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h index dc34cbfc22..f4185d25fb 100644 --- a/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h +++ b/tests/unittests/e1ap/cu_cp/e1ap_cu_cp_test_helpers.h @@ -24,7 +24,7 @@ #include "../common/e1ap_cu_cp_test_messages.h" #include "../common/test_helpers.h" -#include "e1ap_test_local_gateway.h" +#include "e1_test_local_gateway.h" #include "lib/cu_cp/ue_manager/ue_manager_impl.h" #include "srsran/cu_cp/cu_cp_types.h" #include "srsran/e1ap/common/e1ap_common.h" @@ -86,8 +86,8 @@ class dummy_cu_cp_e1ap_gateway size_t nof_connections() const { return cu_up_tx_notifiers.size(); } private: - srslog::basic_logger& logger; - e1ap_test_local_gateway local_e1ap_gw; + srslog::basic_logger& logger; + e1_test_local_gateway local_e1ap_gw; std::vector> cu_up_tx_notifiers; }; diff --git a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.cpp b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.cpp index 78fd5f76c9..c4583408bc 100644 --- a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.cpp +++ b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.cpp @@ -41,7 +41,7 @@ class dummy_e1ap_tx_pdu_notifier : public e1ap_message_notifier } // namespace -std::unique_ptr dummy_e1ap_connection_client::handle_cu_up_connection_request( +std::unique_ptr dummy_e1_connection_client::handle_cu_up_connection_request( std::unique_ptr cu_up_rx_pdu_notifier_) { cu_up_rx_pdu_notifier = std::move(cu_up_rx_pdu_notifier_); diff --git a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.h b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.h index 82ab12a2ab..fa4de2188d 100644 --- a/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.h +++ b/tests/unittests/e1ap/cu_up/e1ap_cu_up_test_helpers.h @@ -33,7 +33,7 @@ namespace srsran { namespace srs_cu_up { -class dummy_e1ap_connection_client : public srs_cu_up::e1ap_connection_client +class dummy_e1_connection_client : public srs_cu_up::e1_connection_client { public: e1ap_message last_tx_e1ap_pdu; @@ -59,7 +59,7 @@ class e1ap_cu_up_test : public ::testing::Test void setup_bearer(unsigned int cu_cp_ue_e1ap_id); /// Dummy E1AP gateway to connect to CU-CP and send E1AP PDUs. - dummy_e1ap_connection_client e1ap_gw; + dummy_e1_connection_client e1ap_gw; timer_manager timers; diff --git a/tests/unittests/e2/common/e2_test_helpers.h b/tests/unittests/e2/common/e2_test_helpers.h index fbfcb72bf9..0cc75166eb 100644 --- a/tests/unittests/e2/common/e2_test_helpers.h +++ b/tests/unittests/e2/common/e2_test_helpers.h @@ -709,7 +709,7 @@ class dummy_e2sm_handler : public e2sm_handler e2sm_action_definition action_def; action_def.service_model = e2sm_service_model_t::KPM; e2sm_kpm_action_definition_s& e2sm_kpm_action_definition = - variant_get(action_def.action_definition); + std::get(action_def.action_definition); e2sm_kpm_action_definition.ric_style_type = 3; e2sm_kpm_action_definition.action_definition_formats.set_action_definition_format3(); e2sm_kpm_action_definition.action_definition_formats.action_definition_format3().meas_cond_list.resize(1); @@ -761,7 +761,7 @@ class dummy_e2_connection_client final : public e2_connection_client dummy_e2_pdu_notifier* get_e2_msg_notifier() { return msg_notifier; } - void close() override{}; + void close() override {} private: srslog::basic_logger& logger; diff --git a/tests/unittests/e2/e2ap_network_adapter_test.cpp b/tests/unittests/e2/e2ap_network_adapter_test.cpp index 1ee8725620..a10316de08 100644 --- a/tests/unittests/e2/e2ap_network_adapter_test.cpp +++ b/tests/unittests/e2/e2ap_network_adapter_test.cpp @@ -34,94 +34,6 @@ using namespace srsran; -class dummy_ric_e2 : public e2_message_handler, public e2_event_handler -{ -public: - dummy_ric_e2(e2_message_notifier& e2_pdu_notifier_) : - logger(srslog::fetch_basic_logger("E2-RIC")), pdu_notifier(e2_pdu_notifier_){}; - - /// E2_event_ handler functions. - void handle_connection_loss() override{}; - - /// E2 message handler functions. - void handle_message(const e2_message& msg) override - { - logger.info("RIC received msg."); - last_e2_msg = msg; - }; - - // RIC sends msg to E2 Agent - void send_msg(const e2_message& msg) - { - logger.info("RIC sends msg."); - pdu_notifier.on_new_message(msg); - }; - - void send_setup_response() - { - logger.info("RIC sends Setup Response msg."); - e2_message e2_setup_response = {}; - e2_setup_response.pdu.set_successful_outcome(); - e2_setup_response.pdu.successful_outcome().load_info_obj(ASN1_E2AP_ID_E2SETUP); - - auto& setup = e2_setup_response.pdu.successful_outcome().value.e2setup_resp(); - setup->transaction_id = last_e2_msg.pdu.init_msg().value.e2setup_request()->transaction_id; - setup->ran_functions_accepted_present = true; - asn1::protocol_ie_single_container_s ran_func_item; - ran_func_item.value().ran_function_id_item().ran_function_id = e2sm_kpm_asn1_packer::ran_func_id; - ran_func_item.value().ran_function_id_item().ran_function_revision = 0; - setup->ran_functions_accepted.push_back(ran_func_item); - setup->global_ric_id.plmn_id.from_number(1); - setup->global_ric_id.ric_id.from_number(1); - - // fill the required part with dummy data - setup->e2node_component_cfg_addition_ack.resize(1); - asn1::e2ap::e2node_component_cfg_addition_ack_item_s& e2node_component_cfg_addition_ack_item = - setup->e2node_component_cfg_addition_ack[0].value().e2node_component_cfg_addition_ack_item(); - e2node_component_cfg_addition_ack_item.e2node_component_interface_type = - asn1::e2ap::e2node_component_interface_type_e::e2node_component_interface_type_opts::e1; - e2node_component_cfg_addition_ack_item.e2node_component_id.set_e2node_component_interface_type_e1().gnb_cu_up_id = - 123; - e2node_component_cfg_addition_ack_item.e2node_component_cfg_ack.upd_outcome = - asn1::e2ap::e2node_component_cfg_ack_s::upd_outcome_opts::success; - - send_msg(e2_setup_response); - }; - - e2_message last_e2_msg; - -private: - srslog::basic_logger& logger; - e2_message_notifier& pdu_notifier; -}; - -class e2_decorator : public e2_message_handler, public e2_event_handler -{ -public: - e2_decorator(e2_interface& decorated_iface_) : - logger(srslog::fetch_basic_logger("E2")), - decorated_e2_mgs_handler(decorated_iface_), - decorated_e2_event_handler(decorated_iface_){}; - - /// E2_event_ handler functions. - void handle_connection_loss() override { decorated_e2_event_handler.handle_connection_loss(); }; - - /// E2 message handler functions. - void handle_message(const e2_message& msg) override - { - logger.info("E2 received msg."); - last_e2_msg = msg; - decorated_e2_mgs_handler.handle_message(msg); - }; - - e2_message last_e2_msg; - -private: - srslog::basic_logger& logger; - e2_message_handler& decorated_e2_mgs_handler; - e2_event_handler& decorated_e2_event_handler; -}; - class e2ap_network_adapter_test : public ::testing::Test { protected: @@ -148,7 +60,7 @@ class e2ap_network_adapter_test : public ::testing::Test ric_net_adapter = std::make_unique(*epoll_broker, *ric_pcap); auto ric_gw = create_sctp_network_gateway({ric_config, *ric_net_adapter, *ric_net_adapter}); ric_net_adapter->bind_and_listen(std::move(ric_gw)); - ric_e2_iface = std::make_unique(*ric_net_adapter); + ric_e2_iface = std::make_unique(*ric_net_adapter, *this); ric_net_adapter->connect_e2ap(ric_e2_iface.get(), ric_e2_iface.get()); std::optional ric_gw_port = ric_net_adapter->get_listen_port(); @@ -178,7 +90,7 @@ class e2ap_network_adapter_test : public ::testing::Test e2_subscription_mngr = std::make_unique(*net_adapter, *e2sm_mngr); factory = timer_factory{timers, task_exec}; e2ap = create_e2_with_task_exec(cfg, factory, *net_adapter, *e2_subscription_mngr, *e2sm_mngr, task_exec); - e2ap_rx_probe = std::make_unique(*e2ap); + e2ap_rx_probe = std::make_unique(*e2ap, *this); net_adapter->connect_e2ap(e2ap_rx_probe.get(), e2ap_rx_probe.get()); } @@ -186,11 +98,110 @@ class e2ap_network_adapter_test : public ::testing::Test { // flush logger after each test srslog::flush(); - - net_adapter->disconnect_gateway(); - ric_net_adapter->disconnect_gateway(); } + class dummy_ric_e2 : public e2_message_handler, public e2_event_handler + { + public: + dummy_ric_e2(e2_message_notifier& e2_pdu_notifier_, e2ap_network_adapter_test& parent_) : + logger(srslog::fetch_basic_logger("E2-RIC")), pdu_notifier(e2_pdu_notifier_), parent(parent_){}; + + /// E2_event_ handler functions. + void handle_connection_loss() override{}; + + /// E2 message handler functions. + void handle_message(const e2_message& msg) override + { + logger.info("RIC received msg."); + last_e2_msg = msg; + std::unique_lock lock(parent.mutex); + msg_received = true; + parent.cvar.notify_one(); + }; + + // RIC sends msg to E2 Agent + void send_msg(const e2_message& msg) + { + logger.info("RIC sends msg."); + pdu_notifier.on_new_message(msg); + }; + + void send_setup_response() + { + logger.info("RIC sends Setup Response msg."); + e2_message e2_setup_response = {}; + e2_setup_response.pdu.set_successful_outcome(); + e2_setup_response.pdu.successful_outcome().load_info_obj(ASN1_E2AP_ID_E2SETUP); + + auto& setup = e2_setup_response.pdu.successful_outcome().value.e2setup_resp(); + setup->transaction_id = last_e2_msg.pdu.init_msg().value.e2setup_request()->transaction_id; + setup->ran_functions_accepted_present = true; + asn1::protocol_ie_single_container_s ran_func_item; + ran_func_item.value().ran_function_id_item().ran_function_id = e2sm_kpm_asn1_packer::ran_func_id; + ran_func_item.value().ran_function_id_item().ran_function_revision = 0; + setup->ran_functions_accepted.push_back(ran_func_item); + setup->global_ric_id.plmn_id.from_number(1); + setup->global_ric_id.ric_id.from_number(1); + + // fill the required part with dummy data + setup->e2node_component_cfg_addition_ack.resize(1); + asn1::e2ap::e2node_component_cfg_addition_ack_item_s& e2node_component_cfg_addition_ack_item = + setup->e2node_component_cfg_addition_ack[0].value().e2node_component_cfg_addition_ack_item(); + e2node_component_cfg_addition_ack_item.e2node_component_interface_type = + asn1::e2ap::e2node_component_interface_type_e::e2node_component_interface_type_opts::e1; + e2node_component_cfg_addition_ack_item.e2node_component_id.set_e2node_component_interface_type_e1().gnb_cu_up_id = + 123; + e2node_component_cfg_addition_ack_item.e2node_component_cfg_ack.upd_outcome = + asn1::e2ap::e2node_component_cfg_ack_s::upd_outcome_opts::success; + + send_msg(e2_setup_response); + }; + + bool msg_received = false; + e2_message last_e2_msg; + + private: + srslog::basic_logger& logger; + e2_message_notifier& pdu_notifier; + e2ap_network_adapter_test& parent; + }; + + class e2_decorator : public e2_message_handler, public e2_event_handler + { + public: + e2_decorator(e2_interface& decorated_iface_, e2ap_network_adapter_test& parent_) : + logger(srslog::fetch_basic_logger("E2")), + decorated_e2_mgs_handler(decorated_iface_), + decorated_e2_event_handler(decorated_iface_), + parent(parent_){}; + + /// E2_event_ handler functions. + void handle_connection_loss() override { decorated_e2_event_handler.handle_connection_loss(); }; + + /// E2 message handler functions. + void handle_message(const e2_message& msg) override + { + logger.info("E2 received msg."); + last_e2_msg = msg; + decorated_e2_mgs_handler.handle_message(msg); + std::unique_lock lock(parent.mutex); + msg_received = true; + parent.cvar.notify_one(); + }; + + bool msg_received = false; + e2_message last_e2_msg; + + private: + srslog::basic_logger& logger; + e2_message_handler& decorated_e2_mgs_handler; + e2_event_handler& decorated_e2_event_handler; + e2ap_network_adapter_test& parent; + }; + + std::mutex mutex; + std::condition_variable cvar; + std::unique_ptr epoll_broker; // dummy RIC std::unique_ptr ric_e2_iface; @@ -224,7 +235,11 @@ TEST_F(e2ap_network_adapter_test, when_e2_setup_response_received_then_ric_conne test_logger.info("Launching E2 setup procedure..."); e2ap->start(); - std::this_thread::sleep_for(std::chrono::seconds(1)); + { + std::unique_lock lock(mutex); + cvar.wait(lock, [this]() { return ric_e2_iface->msg_received; }); + } + // Status: RIC received E2 Setup Request. ASSERT_EQ(ric_e2_iface->last_e2_msg.pdu.type().value, asn1::e2ap::e2ap_pdu_c::types_opts::init_msg); ASSERT_EQ(ric_e2_iface->last_e2_msg.pdu.init_msg().value.type().value, @@ -233,15 +248,14 @@ TEST_F(e2ap_network_adapter_test, when_e2_setup_response_received_then_ric_conne // Action 2: RIC sends E2 Setup Request Response. test_logger.info("Injecting E2SetupResponse"); ric_e2_iface->send_setup_response(); - - tick(); - std::this_thread::sleep_for(std::chrono::seconds(1)); + { + std::unique_lock lock(mutex); + cvar.wait(lock, [this]() { return e2ap_rx_probe->msg_received; }); + } // Status: E2 Agent received E2 Setup Request Response. ASSERT_EQ(e2ap_rx_probe->last_e2_msg.pdu.type().value, asn1::e2ap::e2ap_pdu_c::types_opts::successful_outcome); ASSERT_EQ(e2ap_rx_probe->last_e2_msg.pdu.successful_outcome().value.type().value, asn1::e2ap::e2ap_elem_procs_o::successful_outcome_c::types_opts::e2setup_resp); - - tick(); test_logger.info("Test finished."); } diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp index 61a2d84aa0..2f550c4205 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.cpp @@ -194,22 +194,22 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi // cu to du to rrc info f1ap_cu_to_du_rrc_info cu_to_du_rrc_info; - cu_to_du_rrc_info.cg_cfg_info = make_byte_buffer("deadbeef"); - cu_to_du_rrc_info.ue_cap_rat_container_list = make_byte_buffer("deadbeef"); - cu_to_du_rrc_info.meas_cfg = make_byte_buffer("deadbeef"); + cu_to_du_rrc_info.cg_cfg_info = make_byte_buffer("deadbeef").value(); + cu_to_du_rrc_info.ue_cap_rat_container_list = make_byte_buffer("deadbeef").value(); + cu_to_du_rrc_info.meas_cfg = make_byte_buffer("deadbeef").value(); msg.cu_to_du_rrc_info = cu_to_du_rrc_info; // tx action ind msg.tx_action_ind = f1ap_tx_action_ind::stop; // res coordination transfer container - msg.res_coordination_transfer_container = make_byte_buffer("deadbeef"); + msg.res_coordination_transfer_container = make_byte_buffer("deadbeef").value(); // rrc recfg complete ind msg.rrc_recfg_complete_ind = f1ap_rrc_recfg_complete_ind::true_value; // rrc container - msg.rrc_container = make_byte_buffer("deadbeef"); + msg.rrc_container = make_byte_buffer("deadbeef").value(); // scell to be setup mod list f1ap_scell_to_be_setup_mod_item scell_to_be_setup_mod_item; @@ -376,7 +376,7 @@ f1ap_ue_context_modification_request srsran::srs_cu_cp::generate_ue_context_modi msg.rlc_fail_ind = rlc_fail_ind; // ul tx direct current list info - msg.ul_tx_direct_current_list_info = make_byte_buffer("deadbeef"); + msg.ul_tx_direct_current_list_info = make_byte_buffer("deadbeef").value(); // gnb du cfg query msg.gnb_du_cfg_query = true; @@ -471,7 +471,7 @@ cu_cp_paging_message srsran::srs_cu_cp::generate_paging_message() // add ue radio cap for paging cu_cp_ue_radio_cap_for_paging ue_radio_cap_for_paging; - ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = make_byte_buffer("deadbeef"); + ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = make_byte_buffer("deadbeef").value(); paging_msg.ue_radio_cap_for_paging = ue_radio_cap_for_paging; // add paging origin diff --git a/tests/unittests/f1ap/common/f1ap_cu_test_messages.h b/tests/unittests/f1ap/common/f1ap_cu_test_messages.h index a748992e7c..da3420c683 100644 --- a/tests/unittests/f1ap/common/f1ap_cu_test_messages.h +++ b/tests/unittests/f1ap/common/f1ap_cu_test_messages.h @@ -52,12 +52,12 @@ f1ap_message generate_ue_context_release_complete(gnb_cu_ue_f1ap_id_t cu_ue_id, f1ap_message generate_ue_context_setup_request(gnb_cu_ue_f1ap_id_t cu_ue_id, gnb_du_ue_f1ap_id_t du_ue_id); /// \brief Generates dummy F1AP UE CONTEXT SETUP RESPONSE message. -f1ap_message -generate_ue_context_setup_response(gnb_cu_ue_f1ap_id_t cu_ue_id, - gnb_du_ue_f1ap_id_t du_ue_id, - rnti_t crnti = to_rnti(0x4601), - byte_buffer cell_group_config = make_byte_buffer( - "5c02b091117aec701061e000b1c03544cde4a20c7c080408c008241000100000")); +f1ap_message generate_ue_context_setup_response( + gnb_cu_ue_f1ap_id_t cu_ue_id, + gnb_du_ue_f1ap_id_t du_ue_id, + rnti_t crnti = to_rnti(0x4601), + byte_buffer cell_group_config = + make_byte_buffer("5c02b091117aec701061e000b1c03544cde4a20c7c080408c008241000100000").value()); /// \brief Generates dummy F1AP UE CONTEXT SETUP FAILURE message. f1ap_message generate_ue_context_setup_failure(gnb_cu_ue_f1ap_id_t cu_ue_id, gnb_du_ue_f1ap_id_t du_ue_id); diff --git a/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp b/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp index 1867b00944..5a72851610 100644 --- a/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp +++ b/tests/unittests/f1ap/cu_cp/f1ap_cu_test_helpers.cpp @@ -119,9 +119,9 @@ srsran::srs_cu_cp::create_ue_context_setup_request(const std::initializer_list(); - timers = timer_factory{timer_mng, ue_worker}; - - ue_inactivity_timer = timers.create_timer(); - // prepare F1-U DU bearer config f1u_du_config.t_notify = 10; f1u_du_config.warn_on_drop = true; @@ -241,7 +237,7 @@ TEST_F(f1u_connector_test, ul_dl_flow) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -252,11 +248,11 @@ TEST_F(f1u_connector_test, ul_dl_flow) cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("dead").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("dbee").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer.get(), cu_rx); } @@ -273,7 +269,7 @@ TEST_F(f1u_connector_test, destroy_bearer_cu_up) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -284,18 +280,18 @@ TEST_F(f1u_connector_test, destroy_bearer_cu_up) cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("dead").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("dbee").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer.get(), cu_rx); // Deleting CU bearer will disconnect from connector cu_bearer.reset(); // Check DU-> CU-UP path is properly detached - byte_buffer du_buf2 = make_byte_buffer("BEEF"); + byte_buffer du_buf2 = make_byte_buffer("fdea").value(); check_ul_path_disconnected(du_buf2.deep_copy().value(), du_bearer.get(), cu_rx); } @@ -312,7 +308,7 @@ TEST_F(f1u_connector_test, disconnect_bearer_cu_up) dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -323,22 +319,22 @@ TEST_F(f1u_connector_test, disconnect_bearer_cu_up) cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("dddd").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("bbbb").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer.get(), cu_rx); // Disconnect CU bearer without destryoing it from connector cu_gw->disconnect_cu_bearer(ul_tnl); // Check CU-UP -> DU path is properly detached - byte_buffer cu_buf2 = make_byte_buffer("DEAD"); + byte_buffer cu_buf2 = make_byte_buffer("DEAD").value(); check_dl_path_disconnected(cu_buf2.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path is properly detached - byte_buffer du_buf2 = make_byte_buffer("BEEF"); + byte_buffer du_buf2 = make_byte_buffer("BEEF").value(); check_ul_path_disconnected(du_buf2.deep_copy().value(), du_bearer.get(), cu_rx); } @@ -354,7 +350,7 @@ TEST_F(f1u_connector_test, destroy_bearer_du) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -365,18 +361,18 @@ TEST_F(f1u_connector_test, destroy_bearer_du) cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("dead").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("dbee").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer.get(), cu_rx); // Delete DU bearer du_bearer.reset(); // Check CU-UP -> DU path is properly detached - byte_buffer cu_buf2 = make_byte_buffer("DEAD"); + byte_buffer cu_buf2 = make_byte_buffer("DEAD").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); } @@ -392,7 +388,7 @@ TEST_F(f1u_connector_test, disconnect_bearer_du) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx; @@ -403,22 +399,22 @@ TEST_F(f1u_connector_test, disconnect_bearer_du) cu_gw->attach_dl_teid(ul_tnl, dl_tnl); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("DDDD").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("BBBB").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer.get(), cu_rx); // Disconnect DU bearer without destryoing it from connector du_gw->remove_du_bearer(dl_tnl); // Check CU-UP -> DU path is properly detached - byte_buffer cu_buf2 = make_byte_buffer("DEAD"); + byte_buffer cu_buf2 = make_byte_buffer("DEAD").value(); check_dl_path_disconnected(cu_buf2.deep_copy().value(), cu_bearer.get(), du_rx); // Check DU-> CU-UP path is properly detached - byte_buffer du_buf2 = make_byte_buffer("BEEF"); + byte_buffer du_buf2 = make_byte_buffer("BEEF").value(); check_ul_path_disconnected(du_buf2.deep_copy().value(), du_bearer.get(), cu_rx); } @@ -435,7 +431,7 @@ TEST_F(f1u_connector_test, update_du_f1u) // Create CU TX notifier adapter dummy_f1u_cu_up_rx_notifier cu_rx; std::unique_ptr cu_bearer = - cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker, timers, ue_inactivity_timer); + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); // Create DU TX notifier adapter and RX handler dummy_f1u_du_gateway_bearer_rx_notifier du_rx1; @@ -446,11 +442,11 @@ TEST_F(f1u_connector_test, update_du_f1u) cu_gw->attach_dl_teid(ul_tnl, dl_tnl1); // Check CU-UP -> DU path - byte_buffer cu_buf = make_byte_buffer("ABCD"); + byte_buffer cu_buf = make_byte_buffer("dead").value(); check_dl_path_connected(cu_buf.deep_copy().value(), cu_bearer.get(), du_rx1); // Check DU-> CU-UP path - byte_buffer du_buf = make_byte_buffer("DCBA"); + byte_buffer du_buf = make_byte_buffer("beef").value(); check_ul_path_connected(du_buf.deep_copy().value(), du_bearer1.get(), cu_rx); logger.info("Attach new DU bearer"); @@ -467,11 +463,11 @@ TEST_F(f1u_connector_test, update_du_f1u) du_bearer1.reset(); // Check CU-UP -> DU path - byte_buffer cu_buf2 = make_byte_buffer("ABCD"); + byte_buffer cu_buf2 = make_byte_buffer("dead").value(); check_dl_path_connected(cu_buf2.deep_copy().value(), cu_bearer.get(), du_rx2); // Check DU-> CU-UP path - byte_buffer du_buf2 = make_byte_buffer("DCBA"); + byte_buffer du_buf2 = make_byte_buffer("dbee").value(); check_ul_path_connected(du_buf2.deep_copy().value(), du_bearer2.get(), cu_rx); } diff --git a/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp new file mode 100644 index 0000000000..8de32cbf27 --- /dev/null +++ b/tests/unittests/f1u/common/f1u_cu_split_connector_test.cpp @@ -0,0 +1,464 @@ +/* + * + * 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 "tests/unittests/gateways/test_helpers.h" +#include "srsran/f1u/cu_up/split_connector/f1u_split_connector.h" +#include "srsran/gateways/udp_network_gateway_factory.h" +#include "srsran/gtpu/gtpu_demux_factory.h" +#include "srsran/srslog/srslog.h" +#include "srsran/support/executors/manual_task_worker.h" +#include "srsran/support/io/io_broker_factory.h" +#include + +using namespace srsran; +using namespace srs_cu_up; + +namespace { + +// dummy CU-UP RX bearer interface +class dummy_f1u_cu_up_rx_notifier : public f1u_cu_up_gateway_bearer_rx_notifier +{ +public: + dummy_f1u_cu_up_rx_notifier() = default; + + void on_new_pdu(nru_ul_message msg) override + { + std::lock_guard lock(rx_mutex); + if (msg.t_pdu.has_value()) { + rx_bytes += msg.t_pdu->length(); + } + msg_queue.push(std::move(msg)); + rx_cvar.notify_one(); + } + + unsigned get_rx_bytes() + { + std::lock_guard lock(rx_mutex); + return rx_bytes; + } + + expected get_rx_pdu_blocking(manual_task_worker& ue_worker, + std::chrono::milliseconds timeout_ms = std::chrono::milliseconds(10)) + { + // wait until at least one PDU is received + std::unique_lock lock(rx_mutex); + for (int i = 0; i < 100; i++) { + if (!rx_cvar.wait_for(lock, timeout_ms, [this]() { return !msg_queue.empty(); })) { + if (not msg_queue.empty()) { + break; + } + rx_mutex.unlock(); + ue_worker.run_pending_tasks(); // unlock so we can push into the message queue + rx_mutex.lock(); + } + } + + if (msg_queue.empty()) { + return default_error_t{}; + } + + nru_ul_message pdu = std::move(msg_queue.front()); + msg_queue.pop(); + return pdu; + } + + bool empty() + { + std::lock_guard lock(rx_mutex); + return msg_queue.empty(); + } + +private: + std::queue msg_queue; + std::mutex rx_mutex; + std::condition_variable rx_cvar; + + unsigned rx_bytes = 0; +}; + +/// Fixture class for F1-U connector tests. +/// It requires TEST_F() to create/spawn tests +class f1u_cu_split_connector_test : public ::testing::Test +{ +public: + f1u_cu_split_connector_test() { epoll_broker = create_io_broker(io_broker_type::epoll); } + +protected: + void SetUp() override + { + // init test's logger + srslog::init(); + logger.set_level(srslog::basic_levels::debug); + + // init logger + f1u_logger_cu.set_level(srslog::basic_levels::debug); + f1u_logger_cu.set_hex_dump_max_size(100); + gtpu_logger_cu.set_level(srslog::basic_levels::debug); + gtpu_logger_cu.set_hex_dump_max_size(100); + udp_logger_cu.set_level(srslog::basic_levels::debug); + udp_logger_cu.set_hex_dump_max_size(100); + + logger.info("Creating F1-U connector"); + + // Create UDP tester + udp_network_gateway_config tester_config; + tester_config.bind_address = "127.0.0.2"; + tester_config.bind_port = 0; + tester_config.non_blocking_mode = true; + udp_tester = create_udp_network_gateway({tester_config, server_data_notifier, io_tx_executor}); + ASSERT_TRUE(udp_tester->create_and_bind()); + std::optional tester_bind_port = udp_tester->get_bind_port(); + ASSERT_TRUE(tester_bind_port.has_value()); + + // create GTP-U dmux + gtpu_demux_creation_request msg = {}; + msg.cfg.warn_on_drop = true; + msg.gtpu_pcap = &dummy_pcap; + demux = create_gtpu_demux(msg); + + // create f1-u connector + udp_network_gateway_config nru_gw_config = {}; + nru_gw_config.bind_address = "127.0.0.1"; + nru_gw_config.bind_port = 0; + nru_gw_config.reuse_addr = true; + udp_gw = srs_cu_up::create_udp_ngu_gateway(nru_gw_config, *epoll_broker, io_tx_executor); + cu_gw = std::make_unique(udp_gw.get(), demux.get(), dummy_pcap, tester_bind_port.value()); + cu_gw_bind_port = cu_gw->get_bind_port(); + ASSERT_TRUE(cu_gw_bind_port.has_value()); + + // prepare F1-U CU-UP bearer config + f1u_cu_up_cfg.warn_on_drop = false; + } + + void TearDown() override + { + // flush logger after each test + srslog::flush(); + + stop_token.store(true, std::memory_order_relaxed); + if (rx_thread.joinable()) { + rx_thread.join(); + } + cu_gw.reset(); + udp_gw.reset(); + udp_tester.reset(); + } + + // spawn a thread to receive data + void start_receive_thread() + { + rx_thread = std::thread([this]() { + stop_token.store(false); + while (not stop_token.load(std::memory_order_relaxed)) { + // call receive() on socket + udp_tester->receive(); + + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + }); + } + + void send_to_server(byte_buffer pdu, const std::string& dest_addr, uint16_t port) + { + in_addr inaddr_v4 = {}; + in6_addr inaddr_v6 = {}; + sockaddr_storage addr_storage = {}; + + if (inet_pton(AF_INET, dest_addr.c_str(), &inaddr_v4) == 1) { + sockaddr_in* tmp = (sockaddr_in*)&addr_storage; + tmp->sin_family = AF_INET; + tmp->sin_addr = inaddr_v4; + tmp->sin_port = htons(port); + } else if (inet_pton(AF_INET6, dest_addr.c_str(), &inaddr_v6) == 1) { + sockaddr_in6* tmp = (sockaddr_in6*)&addr_storage; + tmp->sin6_family = AF_INET6; + tmp->sin6_addr = inaddr_v6; + tmp->sin6_port = htons(port); + } else { + FAIL(); + } + udp_tester->handle_pdu(std::move(pdu), addr_storage); + } + + manual_task_worker ue_worker{128}; + std::unique_ptr epoll_broker; + manual_task_worker io_tx_executor{128}; + std::unique_ptr demux; + std::unique_ptr udp_gw; + null_dlt_pcap dummy_pcap = {}; + + // Tester UDP gw to TX/RX PDUs to F1-U CU GW + std::unique_ptr udp_tester; + std::thread rx_thread; // thread for test RX + std::atomic stop_token = {false}; + dummy_network_gateway_data_notifier_with_src_addr server_data_notifier; + + srs_cu_up::f1u_config f1u_cu_up_cfg; + std::unique_ptr cu_gw; + std::optional cu_gw_bind_port = 0; + + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); + srslog::basic_logger& f1u_logger_cu = srslog::fetch_basic_logger("CU-F1-U", false); + srslog::basic_logger& gtpu_logger_cu = srslog::fetch_basic_logger("GTPU", false); + srslog::basic_logger& udp_logger_cu = srslog::fetch_basic_logger("UDP-GW", false); +}; +} // namespace + +/// Test the instantiation of a new entity +TEST_F(f1u_cu_split_connector_test, create_new_connector) +{ + EXPECT_NE(cu_gw, nullptr); +} + +TEST_F(f1u_cu_split_connector_test, send_sdu_with_dl_teid_attached) +{ + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->attach_dl_teid(ul_tnl, dl_tnl); + + ASSERT_NE(udp_tester, nullptr); + start_receive_thread(); + + expected cu_buf = make_byte_buffer("abcd"); + ASSERT_TRUE(cu_buf.has_value()); + + nru_dl_message sdu = {}; + sdu.pdcp_sn = 0; + sdu.t_pdu = cu_buf.value().deep_copy().value(); + + cu_bearer->on_new_pdu(std::move(sdu)); + io_tx_executor.run_pending_tasks(); + + expected exp_buf = make_byte_buffer("34ff000e00000002000000840200000000000000abcd"); + ASSERT_TRUE(exp_buf.has_value()); + + expected du_rx_pdu = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_TRUE(du_rx_pdu.has_value()); + ASSERT_EQ(du_rx_pdu.value().length(), exp_buf.value().length()); + ASSERT_EQ(du_rx_pdu.value(), exp_buf.value()); +} + +TEST_F(f1u_cu_split_connector_test, send_sdu_without_dl_teid_attached) +{ + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + // Not attaching DL TEID + + ASSERT_NE(udp_tester, nullptr); + start_receive_thread(); + + expected cu_buf = make_byte_buffer("abcd"); + ASSERT_TRUE(cu_buf.has_value()); + + nru_dl_message sdu = {}; + sdu.pdcp_sn = 0; + sdu.t_pdu = cu_buf.value().deep_copy().value(); + + cu_bearer->on_new_pdu(std::move(sdu)); + io_tx_executor.run_pending_tasks(); + + // No PDU expected + expected du_rx_pdu = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_FALSE(du_rx_pdu.has_value()); +} + +TEST_F(f1u_cu_split_connector_test, recv_sdu_with_dl_teid_attached) +{ + ASSERT_NE(udp_tester, nullptr); + ASSERT_TRUE(cu_gw_bind_port.has_value()); + + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->attach_dl_teid(ul_tnl, dl_tnl); + + // Send SDU + expected du_buf = make_byte_buffer("34ff000e00000001000000840210000000000000abcd"); + ASSERT_TRUE(du_buf.has_value()); + send_to_server(std::move(du_buf.value()), "127.0.0.1", cu_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu = cu_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_FALSE(rx_sdu.is_error()); + ASSERT_TRUE(rx_sdu.value().t_pdu.has_value()); + + expected exp_buf = make_byte_buffer("abcd"); + ASSERT_FALSE(exp_buf.is_error()); + ASSERT_EQ(rx_sdu.value().t_pdu->length(), exp_buf.value().length()); + ASSERT_EQ(rx_sdu.value().t_pdu.value(), exp_buf.value()); +} + +TEST_F(f1u_cu_split_connector_test, recv_sdu_without_dl_teid_attached) +{ + ASSERT_NE(udp_tester, nullptr); + ASSERT_TRUE(cu_gw_bind_port.has_value()); + + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + // Not attaching DL TEID + + // Send SDU + expected du_buf = make_byte_buffer("34ff000e00000001000000840210000000000000abcd"); + ASSERT_TRUE(du_buf.has_value()); + send_to_server(std::move(du_buf.value()), "127.0.0.1", cu_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu = cu_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_FALSE(rx_sdu.is_error()); + ASSERT_TRUE(rx_sdu.value().t_pdu.has_value()); + + expected exp_buf = make_byte_buffer("abcd"); + ASSERT_FALSE(exp_buf.is_error()); + ASSERT_EQ(rx_sdu.value().t_pdu->length(), exp_buf.value().length()); + ASSERT_EQ(rx_sdu.value().t_pdu.value(), exp_buf.value()); +} + +TEST_F(f1u_cu_split_connector_test, disconnect_stops_tx) +{ + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->attach_dl_teid(ul_tnl, dl_tnl); + + ASSERT_NE(udp_tester, nullptr); + start_receive_thread(); + + // Disconnect incorrect tunnel (no effect expected) + cu_gw->disconnect_cu_bearer( + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{7}}); + + expected cu_buf1 = make_byte_buffer("abcd"); + ASSERT_TRUE(cu_buf1.has_value()); + + nru_dl_message sdu1 = {}; + sdu1.pdcp_sn = 0; + sdu1.t_pdu = cu_buf1.value().deep_copy().value(); + + cu_bearer->on_new_pdu(std::move(sdu1)); + io_tx_executor.run_pending_tasks(); + + expected exp_buf = make_byte_buffer("34ff000e00000002000000840200000000000000abcd"); + ASSERT_TRUE(exp_buf.has_value()); + + expected du_rx_pdu = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_TRUE(du_rx_pdu.has_value()); + ASSERT_EQ(du_rx_pdu.value().length(), exp_buf.value().length()); + ASSERT_EQ(du_rx_pdu.value(), exp_buf.value()); + + // Disconnect correct tunnel explicitly (not via deleting cu_bearer because we need it to handle sdu2) + cu_gw->disconnect_cu_bearer(ul_tnl); + + // Send another SDU + expected cu_buf2 = make_byte_buffer("bcde"); + ASSERT_TRUE(cu_buf2.has_value()); + + nru_dl_message sdu2 = {}; + sdu2.pdcp_sn = 0; + sdu2.t_pdu = cu_buf2.value().deep_copy().value(); + + cu_bearer->on_new_pdu(std::move(sdu2)); + io_tx_executor.run_pending_tasks(); + + // No PDU expected + expected du_rx_pdu2 = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_FALSE(du_rx_pdu2.has_value()); + + // Destructor of cu_bearer tries to disconnect tunnel again, hence we see a warning. +} + +TEST_F(f1u_cu_split_connector_test, destroy_bearer_disconnects_and_stops_rx) +{ + ASSERT_NE(udp_tester, nullptr); + ASSERT_TRUE(cu_gw_bind_port.has_value()); + + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + dummy_f1u_cu_up_rx_notifier cu_rx; + + std::unique_ptr cu_bearer = + cu_gw->create_cu_bearer(0, drb_id_t::drb1, f1u_cu_up_cfg, ul_tnl, cu_rx, ue_worker); + cu_gw->attach_dl_teid(ul_tnl, dl_tnl); + + // Disconnect incorrect tunnel (no effect expected) + cu_gw->disconnect_cu_bearer( + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{7}}); + + // Send SDU + expected du_buf1 = make_byte_buffer("34ff000e00000001000000840210000000000000abcd"); + ASSERT_TRUE(du_buf1.has_value()); + send_to_server(std::move(du_buf1.value()), "127.0.0.1", cu_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu1 = cu_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_FALSE(rx_sdu1.is_error()); + ASSERT_TRUE(rx_sdu1.value().t_pdu.has_value()); + + expected exp_buf1 = make_byte_buffer("abcd"); + ASSERT_FALSE(exp_buf1.is_error()); + ASSERT_EQ(rx_sdu1.value().t_pdu->length(), exp_buf1.value().length()); + ASSERT_EQ(rx_sdu1.value().t_pdu.value(), exp_buf1.value()); + + // Disconnect correct tunnel by deleting the cu_bearer (which should deregister from connector upon destruction) + cu_bearer.reset(); + + // Send another SDU + expected du_buf2 = make_byte_buffer("34ff000e00000001000000840210000000000000bcde"); + ASSERT_TRUE(du_buf2.has_value()); + send_to_server(std::move(du_buf2.value()), "127.0.0.1", cu_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu2 = cu_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_TRUE(rx_sdu2.is_error()); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp b/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp new file mode 100644 index 0000000000..ae94311415 --- /dev/null +++ b/tests/unittests/f1u/common/f1u_du_split_connector_test.cpp @@ -0,0 +1,383 @@ +/* + * + * 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 "../..//gateways/test_helpers.h" +#include "srsran/f1u/du/split_connector/f1u_split_connector.h" +#include "srsran/gateways/udp_network_gateway_factory.h" +#include "srsran/gtpu/gtpu_demux_factory.h" +#include "srsran/srslog/srslog.h" +#include "srsran/support/executors/manual_task_worker.h" +#include "srsran/support/io/io_broker_factory.h" +#include + +using namespace srsran; +using namespace srs_du; + +namespace { + +struct dummy_f1u_du_gateway_bearer_rx_notifier final : srsran::srs_du::f1u_du_gateway_bearer_rx_notifier { + void on_new_pdu(nru_dl_message msg) override + { + logger.info(msg.t_pdu.begin(), msg.t_pdu.end(), "DU received SDU. pdcp_sn={}", msg.pdcp_sn); + std::lock_guard lock(rx_mutex); + rx_bytes += msg.t_pdu.length(); + msg_queue.push(std::move(msg)); + rx_cvar.notify_one(); + } + + expected get_rx_pdu_blocking(manual_task_worker& ue_worker, + std::chrono::milliseconds timeout_ms = std::chrono::milliseconds(10)) + { + // wait until at least one PDU is received + std::unique_lock lock(rx_mutex); + for (int i = 0; i < 100; i++) { + if (!rx_cvar.wait_for(lock, timeout_ms, [this]() { return !msg_queue.empty(); })) { + if (not msg_queue.empty()) { + break; + } + rx_mutex.unlock(); + ue_worker.run_pending_tasks(); // unlock so we can push into the message queue + rx_mutex.lock(); + } + } + if (msg_queue.empty()) { + return default_error_t{}; + } + nru_dl_message pdu = std::move(msg_queue.front()); + msg_queue.pop(); + return pdu; + } + +private: + srslog::basic_logger& logger = srslog::fetch_basic_logger("CU-F1-U", false); + std::queue msg_queue; + std::mutex rx_mutex; + std::condition_variable rx_cvar; + + unsigned rx_bytes = 0; +}; + +/// Fixture class for F1-U DU connector tests. +class f1u_du_split_connector_test : public ::testing::Test +{ +public: + f1u_du_split_connector_test() { epoll_broker = create_io_broker(io_broker_type::epoll); } + +protected: + void SetUp() override + { + // init test's logger + srslog::init(); + logger.set_level(srslog::basic_levels::debug); + + // init logger + f1u_logger_du.set_level(srslog::basic_levels::debug); + f1u_logger_du.set_hex_dump_max_size(100); + gtpu_logger_du.set_level(srslog::basic_levels::debug); + gtpu_logger_du.set_hex_dump_max_size(100); + udp_logger_du.set_level(srslog::basic_levels::debug); + udp_logger_du.set_hex_dump_max_size(100); + + logger.info("Creating F1-U connector"); + + // Create UDP tester + udp_network_gateway_config tester_config; + tester_config.bind_address = "127.0.0.1"; + tester_config.bind_port = 0; + tester_config.non_blocking_mode = true; + udp_tester = create_udp_network_gateway({tester_config, server_data_notifier, io_tx_executor}); + ASSERT_TRUE(udp_tester->create_and_bind()); + std::optional tester_bind_port = udp_tester->get_bind_port(); + ASSERT_TRUE(tester_bind_port.has_value()); + + // create GTP-U dmux + gtpu_demux_creation_request msg = {}; + msg.cfg.warn_on_drop = true; + msg.gtpu_pcap = &dummy_pcap; + demux = create_gtpu_demux(msg); + + // create f1-u connector + udp_network_gateway_config nru_gw_config = {}; + nru_gw_config.bind_address = "127.0.0.2"; + nru_gw_config.bind_port = 0; + nru_gw_config.reuse_addr = true; + udp_gw = srs_cu_up::create_udp_ngu_gateway(nru_gw_config, *epoll_broker, io_tx_executor); + du_gw = std::make_unique(udp_gw.get(), demux.get(), dummy_pcap, tester_bind_port.value()); + du_gw_bind_port = du_gw->get_bind_port(); + ASSERT_TRUE(du_gw_bind_port.has_value()); + + timers = timer_factory{timer_mng, ue_worker}; + + ue_inactivity_timer = timers.create_timer(); + + // prepare F1-U CU-UP bearer config + f1u_du_cfg.warn_on_drop = false; + } + + void TearDown() override + { + // flush logger after each test + srslog::flush(); + + stop_token.store(true, std::memory_order_relaxed); + if (rx_thread.joinable()) { + rx_thread.join(); + } + + du_gw.reset(); + udp_gw.reset(); + udp_tester.reset(); + } + + void send_to_server(byte_buffer pdu, const std::string& dest_addr, uint16_t port) + { + in_addr inaddr_v4 = {}; + in6_addr inaddr_v6 = {}; + sockaddr_storage addr_storage = {}; + + if (inet_pton(AF_INET, dest_addr.c_str(), &inaddr_v4) == 1) { + sockaddr_in* tmp = (sockaddr_in*)&addr_storage; + tmp->sin_family = AF_INET; + tmp->sin_addr = inaddr_v4; + tmp->sin_port = htons(port); + } else if (inet_pton(AF_INET6, dest_addr.c_str(), &inaddr_v6) == 1) { + sockaddr_in6* tmp = (sockaddr_in6*)&addr_storage; + tmp->sin6_family = AF_INET6; + tmp->sin6_addr = inaddr_v6; + tmp->sin6_port = htons(port); + } else { + FAIL(); + } + udp_tester->handle_pdu(std::move(pdu), addr_storage); + } + + // spawn a thread to receive data + void start_receive_thread() + { + rx_thread = std::thread([this]() { + stop_token.store(false); + while (not stop_token.load(std::memory_order_relaxed)) { + // call receive() on socket + udp_tester->receive(); + + std::this_thread::sleep_for(std::chrono::microseconds(100)); + } + }); + } + + timer_manager timer_mng; + manual_task_worker ue_worker{128}; + timer_factory timers; + unique_timer ue_inactivity_timer; + std::unique_ptr epoll_broker; + manual_task_worker io_tx_executor{128}; + std::unique_ptr demux; + std::unique_ptr udp_gw; + null_dlt_pcap dummy_pcap = {}; + + // Tester UDP gw to TX/RX PDUs to F1-U CU GW + std::unique_ptr udp_tester; + std::thread rx_thread; // thread for test RX + std::atomic stop_token = {false}; + dummy_network_gateway_data_notifier_with_src_addr server_data_notifier; + + f1u_config f1u_du_cfg; + std::unique_ptr du_gw; + std::optional du_gw_bind_port = 0; + + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); + srslog::basic_logger& f1u_logger_du = srslog::fetch_basic_logger("CU-F1-U", false); + srslog::basic_logger& gtpu_logger_du = srslog::fetch_basic_logger("GTPU", false); + srslog::basic_logger& udp_logger_du = srslog::fetch_basic_logger("UDP-GW", false); +}; +} // namespace + +/// Test the instantiation of a new entity +TEST_F(f1u_du_split_connector_test, create_new_connector) +{ + EXPECT_NE(du_gw, nullptr); +} + +TEST_F(f1u_du_split_connector_test, send_sdu) +{ + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + + dummy_f1u_du_gateway_bearer_rx_notifier du_rx; + + auto du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, f1u_du_cfg, dl_tnl, ul_tnl, du_rx, timers, ue_worker); + ASSERT_NE(udp_tester, nullptr); + start_receive_thread(); + + expected du_buf = make_byte_buffer("abcd"); + ASSERT_TRUE(du_buf.has_value()); + nru_ul_message sdu = {}; + auto du_chain_buf = byte_buffer_chain::create(du_buf.value().deep_copy().value()); + sdu.t_pdu = std::move(du_chain_buf.value()); + nru_dl_data_delivery_status deliv_status = {}; + sdu.data_delivery_status.emplace(deliv_status); + + du_bearer->on_new_pdu(std::move(sdu)); + io_tx_executor.run_pending_tasks(); + + expected exp_buf = make_byte_buffer("34ff000e00000001000000840210000000000000abcd"); + ASSERT_TRUE(exp_buf.has_value()); + + expected cu_rx_pdu = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_TRUE(cu_rx_pdu.has_value()); + ASSERT_EQ(cu_rx_pdu.value().length(), exp_buf.value().length()); + ASSERT_EQ(cu_rx_pdu.value(), exp_buf.value()); +} + +TEST_F(f1u_du_split_connector_test, recv_sdu) +{ + ASSERT_NE(udp_tester, nullptr); + ASSERT_TRUE(du_gw_bind_port.has_value()); + + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + dummy_f1u_du_gateway_bearer_rx_notifier du_rx; + + auto du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, f1u_du_cfg, dl_tnl, ul_tnl, du_rx, timers, ue_worker); + + // Send SDU + expected du_buf = make_byte_buffer("34ff000e00000002000000840200000000000000abcd"); + ASSERT_TRUE(du_buf.has_value()); + send_to_server(std::move(du_buf.value()), "127.0.0.2", du_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu = du_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_FALSE(rx_sdu.is_error()); + + expected exp_buf = make_byte_buffer("abcd"); + ASSERT_TRUE(exp_buf.has_value()); + ASSERT_EQ(rx_sdu.value().t_pdu.length(), exp_buf.value().length()); + ASSERT_EQ(rx_sdu.value().t_pdu, exp_buf.value()); +} + +TEST_F(f1u_du_split_connector_test, disconnect_stops_tx) +{ + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + + dummy_f1u_du_gateway_bearer_rx_notifier du_rx; + + auto du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, f1u_du_cfg, dl_tnl, ul_tnl, du_rx, timers, ue_worker); + ASSERT_NE(udp_tester, nullptr); + start_receive_thread(); + + // Disconnect incorrect tunnel (no effect expected) + du_gw->remove_du_bearer( + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{7}}); + + expected du_buf1 = make_byte_buffer("abcd"); + ASSERT_TRUE(du_buf1.has_value()); + nru_ul_message sdu1 = {}; + auto du_chain_buf1 = byte_buffer_chain::create(du_buf1.value().deep_copy().value()); + sdu1.t_pdu = std::move(du_chain_buf1.value()); + nru_dl_data_delivery_status deliv_status1 = {}; + sdu1.data_delivery_status.emplace(deliv_status1); + + du_bearer->on_new_pdu(std::move(sdu1)); + io_tx_executor.run_pending_tasks(); + + expected exp_buf = make_byte_buffer("34ff000e00000001000000840210000000000000abcd"); + ASSERT_TRUE(exp_buf.has_value()); + + expected cu_rx_pdu = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_TRUE(cu_rx_pdu.has_value()); + ASSERT_EQ(cu_rx_pdu.value().length(), exp_buf.value().length()); + ASSERT_EQ(cu_rx_pdu.value(), exp_buf.value()); + + // Disconnect correct tunnel explicitly (not via deleting du_bearer because we need it to handle sdu2) + du_gw->remove_du_bearer(dl_tnl); + + // Send another SDU + expected du_buf2 = make_byte_buffer("bcde"); + ASSERT_TRUE(du_buf2.has_value()); + nru_ul_message sdu2 = {}; + auto du_chain_buf2 = byte_buffer_chain::create(du_buf2.value().deep_copy().value()); + sdu2.t_pdu = std::move(du_chain_buf2.value()); + nru_dl_data_delivery_status deliv_status2 = {}; + sdu2.data_delivery_status.emplace(deliv_status2); + + du_bearer->on_new_pdu(std::move(sdu2)); + io_tx_executor.run_pending_tasks(); + + // No PDU expected + expected cu_rx_pdu2 = server_data_notifier.get_rx_pdu_blocking(); + ASSERT_FALSE(cu_rx_pdu2.has_value()); + + // Destructor of du_bearer tries to disconnect tunnel again, hence we see a warning. +} + +TEST_F(f1u_du_split_connector_test, destroy_bearer_disconnects_and_stops_rx) +{ + ASSERT_NE(udp_tester, nullptr); + ASSERT_TRUE(du_gw_bind_port.has_value()); + + // Setup GTP-U tunnel + up_transport_layer_info ul_tnl{transport_layer_address::create_from_string("127.0.0.1"), gtpu_teid_t{1}}; + up_transport_layer_info dl_tnl{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{2}}; + dummy_f1u_du_gateway_bearer_rx_notifier du_rx; + + auto du_bearer = du_gw->create_du_bearer(0, drb_id_t::drb1, f1u_du_cfg, dl_tnl, ul_tnl, du_rx, timers, ue_worker); + + // Disconnect incorrect tunnel (no effect expected) + du_gw->remove_du_bearer( + up_transport_layer_info{transport_layer_address::create_from_string("127.0.0.2"), gtpu_teid_t{7}}); + + // Send SDU + expected du_buf1 = make_byte_buffer("34ff000e00000002000000840200000000000000abcd"); + ASSERT_TRUE(du_buf1.has_value()); + send_to_server(std::move(du_buf1.value()), "127.0.0.2", du_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu1 = du_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_FALSE(rx_sdu1.is_error()); + + expected exp_buf1 = make_byte_buffer("abcd"); + ASSERT_TRUE(exp_buf1.has_value()); + ASSERT_EQ(rx_sdu1.value().t_pdu.length(), exp_buf1.value().length()); + ASSERT_EQ(rx_sdu1.value().t_pdu, exp_buf1.value()); + + // Disconnect correct tunnel by deleting the du_bearer (which should deregister from connector upon destruction) + du_bearer.reset(); + + // Send another SDU + expected du_buf2 = make_byte_buffer("34ff000e00000002000000840200000000000000bcde"); + ASSERT_TRUE(du_buf2.has_value()); + send_to_server(std::move(du_buf2.value()), "127.0.0.2", du_gw_bind_port.value()); + + // Blocking waiting for RX + expected rx_sdu = du_rx.get_rx_pdu_blocking(ue_worker); + ASSERT_TRUE(rx_sdu.is_error()); +} + +int main(int argc, char** argv) +{ + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/tests/unittests/fapi_adaptor/adaptor_performance_dl_tti_request.cpp b/tests/unittests/fapi_adaptor/adaptor_performance_dl_tti_request.cpp index 20f75a6938..fa783a7345 100644 --- a/tests/unittests/fapi_adaptor/adaptor_performance_dl_tti_request.cpp +++ b/tests/unittests/fapi_adaptor/adaptor_performance_dl_tti_request.cpp @@ -284,8 +284,8 @@ static void pdsch_conversion_benchmark() builder_pdsch.set_pdsch_allocation_in_time_parameters(start_symbol_index, nr_of_symbols); - optional profile_nr; - optional dmrs_profile; + std::optional profile_nr; + std::optional dmrs_profile; if (power_nr != -9) { profile_nr.emplace(power_nr); } else { @@ -300,7 +300,7 @@ static void pdsch_conversion_benchmark() builder_pdsch.set_maintenance_v3_codeword_parameters( ldpc_graph, tb_size_lbrm_bytes, pdu_bitmap & 1U, (pdu_bitmap >> 1) & 1U); - optional data_profile; + std::optional data_profile; if (power != -33) { data_profile.emplace(power); } diff --git a/tests/unittests/fapi_adaptor/phy/CMakeLists.txt b/tests/unittests/fapi_adaptor/phy/CMakeLists.txt index 74384c5437..f8ae1e5de1 100644 --- a/tests/unittests/fapi_adaptor/phy/CMakeLists.txt +++ b/tests/unittests/fapi_adaptor/phy/CMakeLists.txt @@ -21,5 +21,7 @@ add_subdirectory(messages) add_executable(fapi_to_phy_translator_test fapi_to_phy_translator_test.cpp) -target_link_libraries(fapi_to_phy_translator_test fapi_to_phy_translator srsran_fapi_precoding_matrix_tools srsran_fapi_uci_part2_tools srslog srsran_fapi_message_builder_test_helpers srsran_upper_phy_support gtest gtest_main) +target_link_libraries(fapi_to_phy_translator_test srsran_fapi_to_phy_translator srsran_fapi_precoding_matrix_tools + srsran_fapi_uci_part2_tools srslog srsran_fapi_message_builder_test_helpers + srsran_upper_phy_support gtest gtest_main) gtest_discover_tests(fapi_to_phy_translator_test) diff --git a/tests/unittests/gtpu/gtpu_test_allocator.cpp b/tests/unittests/gtpu/gtpu_test_allocator.cpp index de7b30c732..23a25bcd80 100644 --- a/tests/unittests/gtpu/gtpu_test_allocator.cpp +++ b/tests/unittests/gtpu/gtpu_test_allocator.cpp @@ -32,7 +32,7 @@ TEST(gtpu_pool_test, normal_request_succeeds) { gtpu_teid_pool_impl pool(MAX_TEIDS); - for (unsigned n = 0; n < 3; ++n) { + for (unsigned n = GTPU_TEID_MIN.value(); n < 3 + GTPU_TEID_MIN.value(); ++n) { expected teid = pool.request_teid(); ASSERT_EQ(true, teid.has_value()); ASSERT_EQ(teid.value(), gtpu_teid_t{n}); @@ -43,7 +43,7 @@ TEST(gtpu_pool_test, full_pool_request_fails) { gtpu_teid_pool_impl pool(MAX_TEIDS); - for (unsigned n = 0; n < MAX_TEIDS; ++n) { + for (unsigned n = GTPU_TEID_MIN.value(); n < MAX_TEIDS + GTPU_TEID_MIN.value(); ++n) { expected teid = pool.request_teid(); ASSERT_EQ(true, teid.has_value()); ASSERT_EQ(teid.value(), gtpu_teid_t{n}); @@ -56,7 +56,7 @@ TEST(gtpu_pool_test, request_after_all_release_succeeds) { gtpu_teid_pool_impl pool(MAX_TEIDS); - for (unsigned n = 0; n < MAX_TEIDS; ++n) { + for (unsigned n = GTPU_TEID_MIN.value(); n < MAX_TEIDS + GTPU_TEID_MIN.value(); ++n) { expected teid = pool.request_teid(); ASSERT_EQ(true, teid.has_value()); ASSERT_EQ(teid.value(), gtpu_teid_t{n}); @@ -70,7 +70,7 @@ TEST(gtpu_pool_test, request_after_few_free_succeeds) { gtpu_teid_pool_impl pool(MAX_TEIDS); - for (unsigned n = 0; n < MAX_TEIDS; ++n) { + for (unsigned n = GTPU_TEID_MIN.value(); n < MAX_TEIDS + GTPU_TEID_MIN.value(); ++n) { expected teid = pool.request_teid(); ASSERT_EQ(true, teid.has_value()); ASSERT_EQ(teid.value(), gtpu_teid_t{n}); @@ -91,6 +91,11 @@ TEST(gtpu_pool_test, request_after_few_free_succeeds) ASSERT_EQ(true, teid.has_value()); ASSERT_EQ(teid.value(), gtpu_teid_t{8}); } + // full again + { + expected teid = pool.request_teid(); + ASSERT_EQ(false, teid.has_value()); + } } int main(int argc, char** argv) diff --git a/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp b/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp index 18daa088ad..e80cdffa1a 100644 --- a/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp +++ b/tests/unittests/gtpu/gtpu_tunnel_ngu_test.cpp @@ -151,8 +151,8 @@ TEST_F(gtpu_tunnel_ngu_test, rx_sdu) gtpu = create_gtpu_tunnel_ngu(msg); sockaddr_storage orig_addr = {}; - byte_buffer orig_vec = make_byte_buffer(gtpu_ping_vec_teid_2_qfi_1_dl); - byte_buffer strip_vec = make_byte_buffer(gtpu_ping_vec_teid_2_qfi_1_dl); + byte_buffer orig_vec = make_byte_buffer(gtpu_ping_vec_teid_2_qfi_1_dl).value(); + byte_buffer strip_vec = make_byte_buffer(gtpu_ping_vec_teid_2_qfi_1_dl).value(); gtpu_dissected_pdu dissected_pdu; bool read_ok = gtpu_dissect_pdu(dissected_pdu, strip_vec.deep_copy().value(), gtpu_rx_logger); ASSERT_EQ(read_ok, true); diff --git a/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp b/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp index 381fc4519c..33a563aff0 100644 --- a/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp +++ b/tests/unittests/gtpu/gtpu_tunnel_nru_test.cpp @@ -250,7 +250,8 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_dl_msg_with_t_pdu) ASSERT_FALSE(tx_msg_copy.is_error()); tx->handle_sdu(std::move(tx_msg_copy.value())); - byte_buffer exp_pdu = make_byte_buffer("34ff001c00000002000000840404001122330244556677aabbccdd00800028b7e0c51048"); + byte_buffer exp_pdu = + make_byte_buffer("34ff001c00000002000000840404001122330244556677aabbccdd00800028b7e0c51048").value(); ASSERT_EQ(exp_pdu, gtpu_tx.last_tx); // RX @@ -286,7 +287,8 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_ul_msg) ASSERT_FALSE(tx_msg_copy.is_error()); tx->handle_sdu(std::move(tx_msg_copy.value())); - byte_buffer exp_pdu = make_byte_buffer("34ff001c000000020000008406110000ab4130021e84811e84894c4ba44c4c8100000000"); + byte_buffer exp_pdu = + make_byte_buffer("34ff001c000000020000008406110000ab4130021e84811e84894c4ba44c4c8100000000").value(); ASSERT_EQ(exp_pdu, gtpu_tx.last_tx); // RX @@ -323,7 +325,8 @@ TEST_F(gtpu_tunnel_nru_test, tx_rx_nru_ul_msg_with_t_pdu) ASSERT_FALSE(tx_msg_copy.is_error()); tx->handle_sdu(std::move(tx_msg_copy.value())); - byte_buffer exp_pdu = make_byte_buffer("34ff001c00000002000000840411000154cbf7011e84811e84890000800028b7e0c51048"); + byte_buffer exp_pdu = + make_byte_buffer("34ff001c00000002000000840411000154cbf7011e84811e84890000800028b7e0c51048").value(); ASSERT_EQ(exp_pdu, gtpu_tx.last_tx); // RX diff --git a/tests/unittests/ngap/ng_setup_procedure_test.cpp b/tests/unittests/ngap/ng_setup_procedure_test.cpp index d29cabbde2..cf496f62bc 100644 --- a/tests/unittests/ngap/ng_setup_procedure_test.cpp +++ b/tests/unittests/ngap/ng_setup_procedure_test.cpp @@ -51,8 +51,8 @@ TEST_F(ngap_test, when_ng_setup_response_received_then_amf_connected) ngap->handle_message(ng_setup_response); ASSERT_TRUE(t.ready()); - ASSERT_TRUE(variant_holds_alternative(t.get())); - ASSERT_EQ(variant_get(t.get()).amf_name, "open5gs-amf0"); + ASSERT_TRUE(std::holds_alternative(t.get())); + ASSERT_EQ(std::get(t.get()).amf_name, "open5gs-amf0"); } /// Test unsuccessful ng setup procedure with time to wait and successful retry @@ -94,8 +94,8 @@ TEST_F(ngap_test, when_ng_setup_failure_with_time_to_wait_received_then_retry_wi ngap->handle_message(ng_setup_response); ASSERT_TRUE(t.ready()); - ASSERT_TRUE(variant_holds_alternative(t.get())); - ASSERT_EQ(variant_get(t.get()).amf_name, "open5gs-amf0"); + ASSERT_TRUE(std::holds_alternative(t.get())); + ASSERT_EQ(std::get(t.get()).amf_name, "open5gs-amf0"); } /// Test unsuccessful ng setup procedure with time to wait and unsuccessful retry @@ -137,7 +137,7 @@ TEST_F(ngap_test, when_ng_setup_failure_with_time_to_wait_received_then_retry_wi ngap->handle_message(ng_setup_failure); ASSERT_TRUE(t.ready()); - ASSERT_TRUE(variant_holds_alternative(t.get())); + ASSERT_TRUE(std::holds_alternative(t.get())); } /// Test the ng setup procedure @@ -168,5 +168,5 @@ TEST_F(ngap_test, when_retry_limit_reached_then_amf_not_connected) } ASSERT_TRUE(t.ready()); - ASSERT_TRUE(variant_holds_alternative(t.get())); + ASSERT_TRUE(std::holds_alternative(t.get())); } diff --git a/tests/unittests/ngap/ngap_asn1_packer_test.cpp b/tests/unittests/ngap/ngap_asn1_packer_test.cpp index a8b8bffc2b..057d6781a5 100644 --- a/tests/unittests/ngap/ngap_asn1_packer_test.cpp +++ b/tests/unittests/ngap/ngap_asn1_packer_test.cpp @@ -35,7 +35,7 @@ using namespace srs_cu_cp; security::sec_key make_sec_key(std::string hex_str) { - byte_buffer key_buf = make_byte_buffer(hex_str); + byte_buffer key_buf = make_byte_buffer(hex_str).value(); security::sec_key key = {}; std::copy(key_buf.begin(), key_buf.end(), key.begin()); return key; @@ -147,7 +147,7 @@ TEST_F(ngap_asn1_packer_test, when_unpack_init_ctx_extract_sec_params_correctly) const char* security_key_cstr = "50636e38151d62356d9a1a0c9f2391885177307ad494be15281dfe5fdac06302"; security::sec_key security_key = make_sec_key(security_key_cstr); - byte_buffer buf = make_byte_buffer(ngap_init_ctx_req); + byte_buffer buf = make_byte_buffer(ngap_init_ctx_req).value(); asn1::cbit_ref bref(buf); srs_cu_cp::ngap_message msg = {}; @@ -221,7 +221,7 @@ TEST_F(ngap_asn1_packer_test, when_ul_nas_message_packing_successful_then_unpack // test unsuccessful unpacking TEST_F(ngap_asn1_packer_test, when_unpack_unsuccessful_then_error_indication_is_send) { - byte_buffer ngap_pdu = make_byte_buffer("deadbeef"); + byte_buffer ngap_pdu = make_byte_buffer("deadbeef").value(); // Unpack message and forward to ngap packer->handle_packed_pdu(ngap_pdu); diff --git a/tests/unittests/ngap/ngap_paging_test.cpp b/tests/unittests/ngap/ngap_paging_test.cpp index 7800a997cc..89a20ff777 100644 --- a/tests/unittests/ngap/ngap_paging_test.cpp +++ b/tests/unittests/ngap/ngap_paging_test.cpp @@ -93,10 +93,10 @@ class ngap_paging_test : public ngap_test return false; } if (cu_cp_paging_notifier.last_msg.ue_radio_cap_for_paging.value().ue_radio_cap_for_paging_of_nr != - make_byte_buffer("deadbeef")) { + make_byte_buffer("deadbeef").value()) { test_logger.error("UE radio cap for paging mismatch {} != {}", cu_cp_paging_notifier.last_msg.ue_radio_cap_for_paging.value().ue_radio_cap_for_paging_of_nr, - make_byte_buffer("deadbeef")); + make_byte_buffer("deadbeef").value()); return false; } diff --git a/tests/unittests/ngap/ngap_test_messages.cpp b/tests/unittests/ngap/ngap_test_messages.cpp index 144cee4bdf..606fd5e2e9 100644 --- a/tests/unittests/ngap/ngap_test_messages.cpp +++ b/tests/unittests/ngap/ngap_test_messages.cpp @@ -294,8 +294,9 @@ srsran::srs_cu_cp::generate_valid_initial_context_setup_request_message_with_pdu asn1::ngap::pdu_session_res_setup_item_cxt_req_s pdu_session_item; pdu_session_item.pdu_session_id = 1U; pdu_session_item.s_nssai.sst.from_number(1U); - pdu_session_item.pdu_session_res_setup_request_transfer = make_byte_buffer( - "0000040082000a0c400000003040000000008b000a01f00a3213020000049a00860001000088000700010000091c00"); + pdu_session_item.pdu_session_res_setup_request_transfer = + make_byte_buffer("0000040082000a0c400000003040000000008b000a01f00a3213020000049a00860001000088000700010000091c00") + .value(); init_context_setup_req->pdu_session_res_setup_list_cxt_req.push_back(pdu_session_item); @@ -362,8 +363,9 @@ srsran::srs_cu_cp::generate_invalid_initial_context_setup_request_message_with_p asn1::ngap::pdu_session_res_setup_item_cxt_req_s pdu_session_item; pdu_session_item.pdu_session_id = 1U; pdu_session_item.s_nssai.sst.from_number(1U); - pdu_session_item.pdu_session_res_setup_request_transfer = make_byte_buffer( - "0000040082000a0c400000003040000000008b000a01f00a3213020000049a00860001000088000700010000091c00"); + pdu_session_item.pdu_session_res_setup_request_transfer = + make_byte_buffer("0000040082000a0c400000003040000000008b000a01f00a3213020000049a00860001000088000700010000091c00") + .value(); init_context_setup_req->pdu_session_res_setup_list_cxt_req.push_back(pdu_session_item); @@ -505,12 +507,12 @@ ngap_message srsran::srs_cu_cp::generate_valid_pdu_session_resource_release_comm // Add PDU Session NAS PDU pdu_session_res_release_cmd->nas_pdu_present = true; - pdu_session_res_release_cmd->nas_pdu = make_byte_buffer("7e02bcb47dc1137e00680100052e01b3d3241201"); + pdu_session_res_release_cmd->nas_pdu = make_byte_buffer("7e02bcb47dc1137e00680100052e01b3d3241201").value(); // Add PDU session resource to release list asn1::ngap::pdu_session_res_to_release_item_rel_cmd_s pdu_session_res_to_release_item_rel_cmd; pdu_session_res_to_release_item_rel_cmd.pdu_session_id = pdu_session_id_to_uint(pdu_session_id); - pdu_session_res_to_release_item_rel_cmd.pdu_session_res_release_cmd_transfer = make_byte_buffer("10"); + pdu_session_res_to_release_item_rel_cmd.pdu_session_res_release_cmd_transfer = make_byte_buffer("10").value(); pdu_session_res_release_cmd->pdu_session_res_to_release_list_rel_cmd.push_back( pdu_session_res_to_release_item_rel_cmd); @@ -701,7 +703,7 @@ ngap_message srsran::srs_cu_cp::generate_valid_paging_message() // add ue radio cap for paging paging->ue_radio_cap_for_paging_present = true; - paging->ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = make_byte_buffer("deadbeef"); + paging->ue_radio_cap_for_paging.ue_radio_cap_for_paging_of_nr = make_byte_buffer("deadbeef").value(); // add paging origin paging->paging_origin_present = true; @@ -798,8 +800,10 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ asn1::ngap::pdu_session_res_setup_item_ho_req_s setup_item; setup_item.pdu_session_id = 1; setup_item.s_nssai.sst.from_number(1); - setup_item.ho_request_transfer = make_byte_buffer( - "0000050082000a0c400000003040000000008b000a01f00a32130200001b13007f00010000860001000088000700010000091c00"); + setup_item.ho_request_transfer = + make_byte_buffer( + "0000050082000a0c400000003040000000008b000a01f00a32130200001b13007f00010000860001000088000700010000091c00") + .value(); ho_request->pdu_session_res_setup_list_ho_req.push_back(setup_item); // allowed nssai asn1::ngap::allowed_nssai_item_s allowed_nssai; @@ -811,9 +815,11 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_request(amf_ue_id_t amf_ // source to target transparent container asn1::ngap::source_ngran_node_to_target_ngran_node_transparent_container_s transparent_container; // rrc container - transparent_container.rrc_container = make_byte_buffer( - "0021930680ce811d1968097e340e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007a071e439f0000240400e03" - "00000000100186c0000700809df0000000000000103a0002000012cb2800281c50f0007000f00000004008010240a0"); + transparent_container.rrc_container = + make_byte_buffer("0021930680ce811d1968097e340e1480005824c5c00060fc2c00637fe002e00131401a0000000880058d006007a071e" + "439f0000240400e03" + "00000000100186c0000700809df0000000000000103a0002000012cb2800281c50f0007000f00000004008010240a0") + .value(); // pdu session res info list asn1::ngap::pdu_session_res_info_item_s session_item; session_item.pdu_session_id = 1; @@ -869,21 +875,28 @@ ngap_message srsran::srs_cu_cp::generate_valid_handover_command(amf_ue_id_t amf_ ho_cmd->pdu_session_res_ho_list_present = true; asn1::ngap::pdu_session_res_ho_item_s ho_item; ho_item.pdu_session_id = 1; - ho_item.ho_cmd_transfer = make_byte_buffer("00"); + ho_item.ho_cmd_transfer = make_byte_buffer("00").value(); ho_cmd->pdu_session_res_ho_list.push_back(ho_item); // target to source transparent container asn1::ngap::target_ngran_node_to_source_ngran_node_transparent_container_s transparent_container; // rrc container - transparent_container.rrc_container = make_byte_buffer( - "08190115200204d00f00102f1f852020605701ac00445ebb1c041878002c00445ebb1c041878002c24445ebb1c041878002c700d3133b414" - "831f0203e0102341e0400024a771002900000000c000140000034ec00187c8a000000697386589000401833251870024e1106fbf56c70eb0" - "04162301620981950001ffff8000000306e10840000702ca0041904000040d31a01100102002a28908900081001514488500040800a8a246" - "30002040054514060088681aab2420e2048a163068e1e4a78fa0428918f04000850404800b50405000850505800b50506000850606800b50" - "6071a48500079a4b5000b9a4b5040f0050703e68410101a10484268414111a10584668418129a10720496302645c24d03a41078bbf030438" - "00000071ffa5294a529e502c0000432ec000000000000a0000018ad5450047001800082002a210054401c040421000a88401560070201104" - "002a210055801c0c0421000a88401568070401104002a210055a00100000010430102030403834000a8a2000000200400080600080900220" - "0a600002298094e3800c00"); + transparent_container.rrc_container = make_byte_buffer("08190115200204d00f00102f1f852020605701ac00445ebb1c041878002c0" + "0445ebb1c041878002c24445ebb1c041878002c700d3133b414" + "831f0203e0102341e0400024a771002900000000c000140000034ec00187c" + "8a000000697386589000401833251870024e1106fbf56c70eb0" + "04162301620981950001ffff8000000306e10840000702ca0041904000040" + "d31a01100102002a28908900081001514488500040800a8a246" + "30002040054514060088681aab2420e2048a163068e1e4a78fa0428918f04" + "000850404800b50405000850505800b50506000850606800b50" + "6071a48500079a4b5000b9a4b5040f0050703e68410101a10484268414111" + "a10584668418129a10720496302645c24d03a41078bbf030438" + "00000071ffa5294a529e502c0000432ec000000000000a0000018ad545004" + "7001800082002a210054401c040421000a88401560070201104" + "002a210055801c0c0421000a88401568070401104002a210055a001000000" + "10430102030403834000a8a2000000200400080600080900220" + "0a600002298094e3800c00") + .value(); ho_cmd->target_to_source_transparent_container = pack_into_pdu(transparent_container); return ngap_msg; diff --git a/tests/unittests/ofh/compression/ofh_compression_test.cpp b/tests/unittests/ofh/compression/ofh_compression_test.cpp index b3f8b4c5d4..dc552ed473 100644 --- a/tests/unittests/ofh/compression/ofh_compression_test.cpp +++ b/tests/unittests/ofh/compression/ofh_compression_test.cpp @@ -186,18 +186,18 @@ TEST(ru_compression_test, bpsk_input_compression_is_correct) INSTANTIATE_TEST_SUITE_P(OFHCompressionTestSuite, OFHCompressionFixture, ::testing::Combine(::testing::Values("generic" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ , "avx2" -#endif // HAVE_AVX2 -#ifdef HAVE_AVX512 +#endif // __AVX2__ +#ifdef __AVX512F__ , "avx512" -#endif // HAVE_AVX512 -#ifdef HAVE_NEON +#endif // __AVX512F__ +#ifdef __ARM_NEON , "neon" -#endif // HAVE_NEON +#endif // __ARM_NEON ), ::testing::ValuesIn(ofh_compression_test_data))); diff --git a/tests/unittests/ofh/receiver/helpers.h b/tests/unittests/ofh/receiver/helpers.h index b84efa2547..07fecd3c55 100644 --- a/tests/unittests/ofh/receiver/helpers.h +++ b/tests/unittests/ofh/receiver/helpers.h @@ -82,14 +82,6 @@ class resource_grid_writer_bool_spy : public resource_grid_writer unsigned get_nof_subc() const override { return 51 * NOF_SUBCARRIERS_PER_RB; }; unsigned get_nof_symbols() const override { return MAX_NSYMB_PER_SLOT; }; - span - put(unsigned port, unsigned l, unsigned k_init, span mask, span symbols) override - { - grid_written = true; - nof_prbs_written += symbols.size() / NOF_SUBCARRIERS_PER_RB; - return {}; - } - span put(unsigned port, unsigned l, unsigned k_init, diff --git a/tests/unittests/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl_test.cpp b/tests/unittests/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl_test.cpp index 1dd0e7e59c..3b4fc33765 100644 --- a/tests/unittests/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_data_flow_uplane_uplink_data_impl_test.cpp @@ -80,7 +80,7 @@ class data_flow_uplane_uplink_data_impl_fixture : public ::testing::Test // Fill the contexts ul_cplane_context_repo_ptr->add(slot, eaxc, context); - ul_context_repo->add({slot, sector}, grid); + ul_context_repo->add({slot, sector}, grid, {context.radio_hdr.start_symbol, context.nof_symbols}); } data_flow_uplane_uplink_data_impl_config get_config() diff --git a/tests/unittests/ofh/receiver/ofh_message_receiver_test.cpp b/tests/unittests/ofh/receiver/ofh_message_receiver_test.cpp index 5d3dd08e3a..d090540dd4 100644 --- a/tests/unittests/ofh/receiver/ofh_message_receiver_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_message_receiver_test.cpp @@ -24,6 +24,7 @@ #include "../../../../lib/ofh/receiver/ofh_rx_window_checker.h" #include "../../../../lib/ofh/receiver/ofh_sequence_id_checker_dummy_impl.h" #include "../compression/ofh_iq_decompressor_test_doubles.h" +#include "srsran/ofh/ethernet/ethernet_unique_buffer.h" #include "srsran/ofh/ofh_factories.h" #include @@ -32,6 +33,17 @@ using namespace ofh; using namespace srsran::ofh::testing; namespace { +/// Dummy Ethernet receive buffer. +class dummy_eth_rx_buffer : public ether::rx_buffer +{ +public: + explicit dummy_eth_rx_buffer(std::vector&& init_values) { buffer = init_values; } + + span data() const override { return buffer; }; + +private: + std::vector buffer; +}; /// Data flow User-Plane uplink PRACH spy. class data_flow_uplane_uplink_prach_spy : public data_flow_uplane_uplink_prach { @@ -108,9 +120,9 @@ class ecpri_packet_decoder_spy : public ecpri::packet_decoder /// Dummy Ethernet receiver class. class dummy_eth_receiver : public ether::receiver { - void start(ether::frame_notifier& notifier) override{}; + void start(ether::frame_notifier& notifier) override {} - void stop() override{}; + void stop() override {} }; } // namespace @@ -130,7 +142,7 @@ class ofh_message_receiver_fixture : public ::testing::Test data_flow_uplane_uplink_prach_spy* df_prach; ecpri_packet_decoder_spy* ecpri_decoder; vlan_frame_decoder_spy* vlan_decoder; - message_receiver ul_handler; + message_receiver_impl ul_handler; public: ofh_message_receiver_fixture() : @@ -192,13 +204,13 @@ class ofh_message_receiver_fixture : public ::testing::Test TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_ethernet_type) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); ether::vlan_frame_params params = vlan_params; params.eth_type = 7777; vlan_decoder->set_vlan_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_FALSE(ecpri_decoder->has_decode_function_been_called()); @@ -208,13 +220,13 @@ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_ethernet_typ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_src_mac) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); ether::vlan_frame_params params = vlan_params; params.mac_src_address = {0xbe, 0xbe, 0xca, 0xfe, 0xba, 0xca}; vlan_decoder->set_vlan_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_FALSE(ecpri_decoder->has_decode_function_been_called()); @@ -224,13 +236,13 @@ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_src_mac) TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_dst_mac) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); ether::vlan_frame_params params = vlan_params; params.mac_dst_address = {0xbe, 0xbe, 0xca, 0xfe, 0xba, 0xca}; vlan_decoder->set_vlan_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_FALSE(ecpri_decoder->has_decode_function_been_called()); @@ -240,14 +252,14 @@ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_dst_mac) TEST_F(ofh_message_receiver_fixture, discard_ecpri_control_frames) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); ecpri::packet_parameters params; params.header.msg_type = srsran::ecpri::message_type::rt_control_data; params.type_params.emplace(ecpri::realtime_control_parameters{1, 2}); ecpri_decoder->set_ecpri_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -257,13 +269,13 @@ TEST_F(ofh_message_receiver_fixture, discard_ecpri_control_frames) TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_uplink_eacx) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); - ecpri::packet_parameters params = ecpri_params; - variant_get(params.type_params).pc_id = 2; + ecpri::packet_parameters params = ecpri_params; + std::get(params.type_params).pc_id = 2; ecpri_decoder->set_ecpri_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -273,13 +285,13 @@ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_uplink_eacx) TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_prach_eacx) { - static_vector msg = {1}; + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1})); - ecpri::packet_parameters params = ecpri_params; - variant_get(params.type_params).pc_id = 6; + ecpri::packet_parameters params = ecpri_params; + std::get(params.type_params).pc_id = 6; ecpri_decoder->set_ecpri_params(params); - ul_handler.on_new_frame(msg); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -289,8 +301,8 @@ TEST_F(ofh_message_receiver_fixture, discard_frames_with_unexpected_prach_eacx) TEST_F(ofh_message_receiver_fixture, invalid_slot_point_peek_does_not_call_data_flows) { - static_vector msg = {1, 0, 0}; - ul_handler.on_new_frame(msg); + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1, 0, 0})); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -300,8 +312,8 @@ TEST_F(ofh_message_receiver_fixture, invalid_slot_point_peek_does_not_call_data_ TEST_F(ofh_message_receiver_fixture, valid_uplink_message_gets_processed_by_data_flow) { - static_vector msg = {0, 0, 0, 0}; - ul_handler.on_new_frame(msg); + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{0, 0, 0, 0})); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -311,8 +323,8 @@ TEST_F(ofh_message_receiver_fixture, valid_uplink_message_gets_processed_by_data TEST_F(ofh_message_receiver_fixture, valid_long_prach_message_gets_processed_by_data_flow) { - static_vector msg = {1, 0, 0, 0}; - ul_handler.on_new_frame(msg); + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{1, 0, 0, 0})); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); @@ -322,8 +334,8 @@ TEST_F(ofh_message_receiver_fixture, valid_long_prach_message_gets_processed_by_ TEST_F(ofh_message_receiver_fixture, valid_short_prach_message_gets_processed_by_data_flow) { - static_vector msg = {3, 0, 0, 0}; - ul_handler.on_new_frame(msg); + ether::unique_rx_buffer buffer(dummy_eth_rx_buffer(std::vector{3, 0, 0, 0})); + ul_handler.on_new_frame(std::move(buffer)); ASSERT_TRUE(vlan_decoder->has_decode_function_been_called()); ASSERT_TRUE(ecpri_decoder->has_decode_function_been_called()); diff --git a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier_test.cpp b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier_test.cpp index 5549675585..8e950ea0c5 100644 --- a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_notifier_test.cpp @@ -29,6 +29,8 @@ using namespace srsran; using namespace ofh; using namespace ofh::testing; +static constexpr ofdm_symbol_range symbol_range = {0, 14}; + TEST(ofh_data_flow_uplane_rx_symbol_notifier, empty_context_does_not_notify) { auto notifier = std::make_shared(); @@ -54,7 +56,7 @@ TEST(ofh_data_flow_uplane_rx_symbol_notifier, unwritten_grid_does_not_notify) unsigned sector = 0; resource_grid_spy grid(2, 14, 273); - repo->add({slot, sector}, grid); + repo->add({slot, sector}, grid, symbol_range); sender.notify_received_symbol(slot, symbol); ASSERT_FALSE(repo->get(slot, symbol).empty()); @@ -73,7 +75,7 @@ TEST(ofh_data_flow_uplane_rx_symbol_notifier, completed_resource_grid_triggers_n resource_grid_spy grid(2, 14, 273); static_vector samples(grid.get_writer().get_nof_subc()); - repo->add({slot, sector}, grid); + repo->add({slot, sector}, grid, symbol_range); ASSERT_FALSE(repo->get(slot, symbol).empty()); // Fill the grid. @@ -102,7 +104,7 @@ TEST(ofh_data_flow_uplane_rx_symbol_notifier, uncompleted_port_does_not_notify) resource_grid_spy grid(2, 14, 273); static_vector samples(grid.get_writer().get_nof_subc()); - repo->add({slot, sector}, grid); + repo->add({slot, sector}, grid, symbol_range); // Fill the grid. repo->write_grid(slot, 0, symbol, 0, samples); @@ -125,7 +127,7 @@ TEST(ofh_data_flow_uplane_rx_symbol_notifier, uncompleted_prbs_does_not_notify) resource_grid_spy grid(1, 14, 273); static_vector samples(grid.get_writer().get_nof_subc() - 1); - repo->add({slot, sector}, grid); + repo->add({slot, sector}, grid, symbol_range); // Fill the grid. repo->write_grid(slot, 0, symbol, 0, samples); diff --git a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp index 67b67e6c7c..a4916cc556 100644 --- a/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp +++ b/tests/unittests/ofh/receiver/ofh_uplane_rx_symbol_data_flow_writer_test.cpp @@ -32,9 +32,10 @@ using namespace ofh::testing; class ofh_uplane_rx_symbol_data_flow_writer_fixture : public ::testing::Test { protected: - static_vector eaxc = {0, 1, 2, 3}; - std::shared_ptr repo = std::make_shared(1); - unsigned sector = 0; + const ofdm_symbol_range symbol_range = {0, 14}; + static_vector eaxc = {0, 1, 2, 3}; + std::shared_ptr repo = std::make_shared(1); + unsigned sector = 0; slot_point slot; unsigned symbol_id = 0; resource_grid_writer_bool_spy rg_writer; @@ -65,7 +66,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, death_test_no_eaxc_found) { unsigned invalid_eaxc = 4; - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); ASSERT_DEATH(writer.write_to_resource_grid(invalid_eaxc, results), ""); @@ -79,7 +80,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, decoded_prbs_outside_grid_ section.start_prb = 51; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); @@ -97,7 +98,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, decoded_prbs_match_grid_pr section.start_prb = 0; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); @@ -116,7 +117,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, decoded_prbs_bigger_than_g section.start_prb = 0; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); @@ -135,7 +136,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, segmented_prbs_inside_the_ section.start_prb = 0; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); @@ -156,7 +157,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, segmented_prbs_write_the_p section.start_prb = 40; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_FALSE(repo->get(results.params.slot, results.params.symbol_id).empty()); @@ -177,7 +178,7 @@ TEST_F(ofh_uplane_rx_symbol_data_flow_writer_fixture, segmented_prbs_fill_the_gr section.start_prb = 0; section.iq_samples.resize(section.nof_prbs * NOF_SUBCARRIERS_PER_RB); - repo->add({results.params.slot, sector}, grid); + repo->add({results.params.slot, sector}, grid, symbol_range); writer.write_to_resource_grid(eaxc[0], results); ASSERT_EQ(section.nof_prbs, rg_writer.get_nof_prbs_written()); { 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 f5d02b2fb7..a72aa6921c 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 @@ -107,12 +107,7 @@ class resource_grid_dummy : public resource_grid public: unsigned get_nof_ports() const override { return 1; } unsigned get_nof_subc() const override { return 1; } - unsigned get_nof_symbols() const override { return 1; } - span - put(unsigned port, unsigned l, unsigned k_init, span mask, span symbols) override - { - return {}; - } + unsigned get_nof_symbols() const override { return 14; } span put(unsigned port, unsigned l, @@ -132,13 +127,9 @@ class resource_grid_dummy : public resource_grid public: unsigned get_nof_ports() const override { return 1; } unsigned get_nof_subc() const override { return 1; } - unsigned get_nof_symbols() const override { return 1; } + unsigned get_nof_symbols() const override { return 14; } bool is_empty(unsigned port) const override { return true; } bool is_empty() const override { return true; } - span get(span symbols, unsigned port, unsigned l, unsigned k_init, span mask) const override - { - return {}; - } span get(span symbols, unsigned port, unsigned l, @@ -170,6 +161,8 @@ class resource_grid_dummy : public resource_grid class ofh_uplink_request_handler_impl_fixture : public ::testing::Test { protected: + const cyclic_prefix cp = {cyclic_prefix::NORMAL}; + const tdd_ul_dl_config_common ttd_pattern = {subcarrier_spacing::kHz30, {10, 6, 6, 3, 3}, {}}; uplink_request_handler_impl_config cfg; std::shared_ptr ul_slot_repo; std::shared_ptr ul_prach_repo; @@ -221,6 +214,7 @@ class ofh_uplink_request_handler_impl_fixture : public ::testing::Test config.ul_data_eaxc = eaxc; config.is_prach_cp_enabled = false; config.cp = cyclic_prefix::NORMAL; + config.tdd_config.emplace(ttd_pattern); return config; } @@ -285,7 +279,35 @@ TEST_F(ofh_uplink_request_handler_impl_fixture, handle_uplink_slot_generates_cpl { resource_grid_dummy rg; resource_grid_context rg_context; - rg_context.slot = slot_point(1, 1, 1); + rg_context.slot = slot_point(1, 1, 7); + rg_context.sector = 1; + + handler.handle_new_uplink_slot(rg_context, rg); + + // Assert data flow. + ASSERT_TRUE(data_flow->has_enqueue_section_type_1_method_been_called()); + const data_flow_cplane_scheduling_commands_spy::spy_info& info = data_flow->get_spy_info(); + ASSERT_EQ(rg_context.slot, info.slot); + ASSERT_EQ(eaxc[0], info.eaxc); + ASSERT_EQ(data_direction::uplink, info.direction); + + const ofdm_symbol_range symbol_range = get_active_tdd_ul_symbols(ttd_pattern, rg_context.slot.slot_index(), cp); + for (unsigned i = 0, e = rg.get_writer().get_nof_symbols(); i != e; ++i) { + ASSERT_FALSE(ul_slot_repo->get(rg_context.slot, i).empty()); + } + + // Assert that the symbol range equals the number of symbols of the grid. + ASSERT_EQ(0, symbol_range.start()); + ASSERT_EQ(rg.get_writer().get_nof_symbols(), symbol_range.stop()); +} + +TEST_F(ofh_uplink_request_handler_impl_fixture, + handle_uplink_in_special_slot_generates_cplane_message_with_valid_symbols) +{ + resource_grid_dummy rg; + resource_grid_context rg_context; + // Use special slot. + rg_context.slot = slot_point(1, 1, 6); rg_context.sector = 1; handler.handle_new_uplink_slot(rg_context, rg); @@ -296,4 +318,13 @@ TEST_F(ofh_uplink_request_handler_impl_fixture, handle_uplink_slot_generates_cpl ASSERT_EQ(rg_context.slot, info.slot); ASSERT_EQ(eaxc[0], info.eaxc); ASSERT_EQ(data_direction::uplink, info.direction); + + const ofdm_symbol_range symbol_range = get_active_tdd_ul_symbols(ttd_pattern, rg_context.slot.slot_index(), cp); + for (unsigned i = 0, e = rg.get_writer().get_nof_symbols(); i != e; ++i) { + if (i >= symbol_range.start() && i < symbol_range.stop()) { + ASSERT_FALSE(ul_slot_repo->get(rg_context.slot, i).empty()); + } else { + ASSERT_TRUE(ul_slot_repo->get(rg_context.slot, i).empty()); + } + } } diff --git a/tests/unittests/pcap/mac_pcap_test.cpp b/tests/unittests/pcap/mac_pcap_test.cpp index c1d67e036d..0395e7a1de 100644 --- a/tests/unittests/pcap/mac_pcap_test.cpp +++ b/tests/unittests/pcap/mac_pcap_test.cpp @@ -263,1434 +263,2861 @@ void write_pcap_nr_thread_function_byte_buffer(srsran::mac_pcap* pcap, uint32_t // Write #num_pdus DL MAC NR PDUs using PCAP handle (byte_buffer) void write_pcap_nr_thread_function_large_byte_buffer(srsran::mac_pcap* pcap, uint32_t num_pdus) { - srsran::byte_buffer tv = srsran::make_byte_buffer( - "44057e80042080042045000578b506400040066c1c0a2d0001" - "0a2d0003145196440dc78535f4439577801001fd348500000101080a7e069e553740bf250a43116ae9c7fb384dde95580b51f982cee5b1d7" - "2ffb703548c3f9a9220c5434ff30c1d04affa45a62c111fa45e4099dfec3dd946df21545009889b77dcda48b80de4903c13b8844a6dcb31e" - "0a2b6992d01f76f64cd621ef993f2f09cc0fa9a825ee8c9a92e9fff812924111ddda77b58b3e288050e4a9d3372d448308f6a684d4b1ef22" - "217d33e79aeadbdbf3898c2c1cac791fd568013eea27e58dd4ebe01e5a66ec6189a3836148125b58b8428542cbe45aaa0541bddcfee70041" - "53ac8da5a485ea225226839f168a0b256f91d00a011afee18a23596903ba8234e100d97b99f32b6c8206e860b0ff4d294ff73d9e67ba6498" - "385b8ac567ff3e18d8a2a2a79eb3668c1572d5a62cd7a5e46ef6467141dfd7cb5ef0f7307f752f4ff0cc57a2a8c6e8d0792bc9aad7bb2b25" - "4902613899ab35977f751437a0d583584bc7f37ecdb5c1281a2a62e6dd4a8d7383144f143756f2d0c96945526c54734065f54a091290324f" - "0acf452f3d8a8820753f4886c342eefe159738052c2de003822c3473b0bf17cd0953b1a9b37a1bd13b311cf7bdb295cb6978554e32f42222" - "ae7fec12c4d28775ef2cb53d7b99e33d344fabf01ee23458bf63555b4bf709c05a3f24171a8c4ec8c370eca990fb954ab9ce4d86445c7e24" - "16c61a8d5a3bc2222c22e31a08bea9b7d6505ce242d5f7c2340095ed0758971080c235807cac4e78eeaf343a9f24e6a1458e15b26ed72d2c" - "1dda1eb977ea197ed474013cdcf3315d3ab51d783a6c46f5b412a69d285c79718b66a4b655a36b3509420c4ef3ed49a2895123fc6613e224" - "d6a11663d8bc40b8f831b919b901d18b518ef2fa3ad41a0d7f9b11a2ddc388a612969816f7c980408e8a64e083cb3fdcae6f1d3e1aa53ef1" - "19de5c81d11ad9eff93312a8cd685773a794e9755e2b339f57d483df2f9022a8be8d2b883df028506e411c4e422b2b80330169d68927c0e3" - "d9202a88ed1ad0cc561e00875e6829263d1386cb6cdf0f5ac6b13f158b012a59d06931c82148b8eed4b6159bf7f68a640daaf15a94fa4032" - "0896fdc31af5e1204138ead463ed2d2ca5dc2f1e3c5e64f8a56f7de2b86f8ac075b89aff42c9544e9e2c064febb3d4c56ed1519fdaba9669" - "993fc9387e1e21146ee0da4a5cde5be28ed92bd6391925ce90deda54d79e7ab52e125081659f74af8e06c1217b32e84222c5081f20e69c82" - "87d09a2bd3c84724e14668021bf59ac4e6e8ba7a28c089b4f5e65f8f86c63592d97bc4d16549e034e44d94bc5f2f4866da35cded24ef3b92" - "57a8b8068eca7fb9f032430833141996a7d9a17bfebe05b6850bf4c044c0373e96d8b72088e93801817d988d5db23ed3fef99e35c680894c" - "6cae023ebaa14197ca6da0c4735fb12da676a714077afb155aeb4adcaece2df5d5e30b54dc302b51d3b7b54de5eb496f1bae6681b7457c0a" - "05bb64c4799faf3d3d44486027068a1242e6627bf14595b6f7f02d2d2991cc0d97667f8dbf2394703c9de80ab67adc11e3af7172be9575c2" - "ba5fb7d0e260b75094fb0e051a8303b92cba15c54300bdbadf46408bc3517d0d1fdc626f68b237c5d8e6590411d2f91674ed26cf0ae9f736" - "66129e0f0ef79fc949a28c3dc1f4f19e311ccebfc1558c6f8e8ae57711e48a1288ef06b1a02bfa36a56682770e93c30255391656ba5aa449" - "df16b614c28cb162f2d4d42aef7b6bca4fb3695a0691e7d90e94c4c10ca7ba6cc6de567ecc5f73aa3386668a32aaea932abbf6778adf0cf0" - "59754a012d1f9ceffaa982e3a6b7bd5abac19cef27745418b902a866d29b3af0334c7b57cc30e5dd89f0ea9bb1e02a73efe4ab2e32a4dc7d" - "c6b551cc803f0ed04b8e810bee82041fa3f6768a2119c0646d10ccbc83c84099284693332ac2882f44057e80042180042145000578b50740" - "0040066c1b0a2d00010a2d0003145196440dc78a79f4439577801001fdb48a00000101080a7e069e553740bf25484806cbf464708f9f23a2" - "a08c13a3342490ffe0a6aad4973f63303e44f852669a2ea5c07abecfea49111c184281a096edf756aa96f7d87fc0f43d8292e8aea230d342" - "15dc876bb4ae46b44e537ea28f62b4705c9f28f6c0c310fdeb9945051583f32eda754cc01106668b054cd4c5d408f711d440458d526885a9" - "7541b254ca03a4b486cab92c1509309c0c80d5dafbedb5b541067157c3064059e4f7724278162ae3f9e1372c958e4588dcbdcc8dbac98087" - "0f54e2a04f350ed73d0c8b11f41f77419f08e5c8baaaa68402f9d88390a15ea2f402d527237fab9ccec2263efa27a5d3d476b102e18e1737" - "7fc6059cc3fc0b81841de591f6c0c7bef38bc14656b9ffe8c68037bf9e11b8c9263fd6f03c1adb4b4a41fe07474b48a72ab1b3c801e9e2fc" - "5664fa17e08a6af66e358ea128d6c4829fe8ede92f4b9d1f376f94ee0d13a4a937acf7e64426acdfe17df5165b3d5dc92b572fcafce26f0d" - "7ea4f411838fb072fe86a9c612463f144c51cb526acb852fc402ad018e0e8cca61e39197957190ea34822c052357ec09ccdd6a622c8299cd" - "cc609188a66d141a0dd77af8985cbb960098e976fe85d526a19455f79c4d52b591594b21fe7aba09021f46e31d2d1b709e101223ff75b9aa" - "17e07880f07152a9f6cca18501221eb6c1b31fae6ccb95260652cc2a74abc590f141608148419a966fc4f77f402f07f5b39a3844e64aafbb" - "7ada9c37f9cca718547618ff38d6472d06e4d3838e7ed06bae797cbe62be020d66a8b92efc17eafe9e4c04846d9aaaf304c54622559a3102" - "7a3c7ffbd318a80c16e18adffd147bedfd1febbf4f9bf7ca7fe59b4aa84324835834cc5ac484141f2526ec7bf8ffd41cb91a6b4fb562d3a0" - "cf5dca498406237677db96b300dd153478137629e33d6be4547ca5c58322e2e2cdbe2554ee3517b6d55c6da370dd7958131786c920913591" - "9e372355300a34153ededdf490c125f68399edaa8a2d8824d088b2242406aed1c32879a288c9e57aaf3dd1ccf1049b9c8ecc013b899cd25f" - "da3ccb0ce26c06afe6704486f5ccee1250f87816bdb728451941b4bc23be69a78bd732c3fe5fa5ae74f97e0c39c2596e1f7a63118bc2a05d" - "51e6139b9a18b5e05f6f41e3390c9d3650bf3d1ea2d64ff3f016a841523ab25b6a489dc033546d0197a2be0e5c91b35b7f86d4480fbfdb5c" - "ab57b0d797a7570a626642b672130d02b8abff928cbc608f716a3a61fe0178b3c7dbaec2b8b785ccdb4291f0bcf468940f5084959e6c97c0" - "93b968022098e44f72b733e86664e40af6c06b62f39816ecf61a753266e8b5ed55ef738d31ace2e5067043a93241ee8ec22b4cd7b71deaa5" - "dbdc9f058254f3ea3884ba4fe33c3e2835a73f28ddb02131c9ac4b96fc334a9dbbd9aa954916cd83cc38d32ecf5d151acc63590ea97c5e36" - "7d53f8c696e67c86dd00a5c69ddb3fa6c564264cd55436359c06c19895d6cdbe18798f438496a01441a4917f067d73bc44c31bfd75bd1afd" - "d6974f888413bae0100567d2416990b489a23b9d6026fa1c80793803493850e9f4f103a48c9a0bc95180fbaf49deef7a14d7e0770eb76448" - "625ada8074666a6957e2c5ba4bf9e052e40ded3aa4d09e0e26f87cb5330be34d29f1a5b2474e3b06bdcf9bb73ef319bba4b5e04707e87154" - "48c56af97a585b7ff4b40b186785ff2f4d58a6c2084904a68efea64cc5b05836ee1dff82c42ac4493c57ce1736da3f87f4336b57132a43ae" - "1aeb96f847d62eb36d2644f5f66b7e23e638392c9319fb0d05849340e772dfa61170a6dd6f64f7da9c42e984cf669f46a3c2efb2e964401a" - "d47fca051249315b0ffb602e6231aec6e10bacb09a2c1ac275c7614a740d87345d54440b7440618a0eb4e7b0d34c38609844057e80042280" - "042245000578b508400040066c1a0a2d00010a2d0003145196440dc78fbdf4439577801801fdf0a600000101080a7e069e553740bf25133e" - "c28380fbc86e6046d00e227bfeda912f0734082420811ebaaa0d46b71ef639010128de548cfa4bd17084ad4cfd3412c972384332e6069682" - "2ce8c8bd03697b1e6db8257b516944122c04150c3d8c4226dce12997563cfab8a999546082a26cf0e2f5e30b6a7129d89d7c6ece10d09115" - "32c5e374baa59e7c9572f1494a1ed1b7fc704a616ff47a3fc472691800d2e466b2cf58fd43c8ea442e30e9e057d2b9bd8832c9d8052817e7" - "34d76c146a57772a6b9210ffaef44e948029745999e1e3cb315110614ff1fd96bc8e7dbededc52c6e46a0a22e14c64fe453ff3b08adae778" - "810a3dcf06cfcaf132faebf6eed0fae43c92fde6a7adf592223abc50d78f87e0bbc0cdc58f3bdba8e0b7bec5d192d8f8f2a5122e5c4b6ed8" - "4273c637035aebaf7581f97308bf241a44897fc951631e6a72aa207850799956d4ec069b8783bdb5b269b7cb5ef46aea4f1c759d6fda4ec7" - "fa31417d491ae465255619f9bbd02a34ef5fa1ffc1d458d0ffaa4b2f85c3e164a29407e4405fabf1c3744e69a08ea516de6d2cf28e949c12" - "5445fbffdb35745af37023d095d86d701347fa725b32c4c14af69b080d9d31babb072f75d51bd4bc5c798c35cd1690b7f92ff7dc81a9afe4" - "202ae5d331e022e993fac68bcddeadb7d81dfb80a867c2f740d70b9520baae66cbe4f465ed5df3789cb073b06c16a3e1bb0c31fe2e449223" - "dfff32d474b90756dfc3f3bc9798baf9ed2c5d9556ca45e1d4573248d4768a1a29c3671e84191b6569f0589eaa4da7d72f953d45cfc380e9" - "367f0b65877419f71008ba23133d4ab43a42ba792e9e2529de020e41759ead35a80401323bc161b2f970df16acdce0a8d9a2547589b13684" - "66a8ef226c100b848718c460580304bb365e936533dc868b73ae5d21adf9c0b836413f7aa0f41b616497975798d4effcd027da635deb9067" - "c7352383c81aad06a8f4dde55ae6bc5e9b8dfb0e73aec189f51e7d691985b753c8474a55a8f0f88725ea052232b60600b8c9e5ddf4ff5302" - "a99d283a9de75065ed41851ee6631dfafee6915602b9dc9b60aae7327113bf048ca581182121c3e0c31b78f44ae61201ccc8a6286c12aeb6" - "38caa3507e61d36a37deb3aff8c73e8b73c03519c6cdf5638a4b5f238c3f8663ebabde48a086d450c9bb237020a9a2d989e62745f98efe7d" - "5954cb58ed05d138aade6e8d24b0562a7f8f26d382c8e7c22ff4545587814d41c3898805e808adfcc4a25cb2b75629bc01a17f395fc7249c" - "b83891fdc212f7c69e5021758931fd5c370b674caa89f19aa2fa9dfa5f52e518a88bfc2c5ae5dcebee4944c86946d0d4da2f5ca05668ca19" - "9b011a822502d5a0734db7b7d17b3262a0271a01279af7134562635bfd1310a123f240a41e70a88d5daf1d40ed51c6f1593f44d47291a865" - "564eb2f1d4f52694a1687f16f345a78a4e15d5789fb60fdbcb361249d647c65f8b77fb8e7c32dec2a6e4d83513ac4a1d76efa6d66078ca46" - "265c1ea6d74f5c9674239514695a2d6d8645e114a6ac7bd4897b8bb880fe1ae70e4f333cd0e07cd495a4b284bfc8a630fed02c578fbf467b" - "33988e9929893385000c54c6ccb559a9feffa3ca2cf103ece9ab36220f8319e25d22de3185534c090c72fa37a6c168b4c0691c3f10166412" - "8893da7a5a549533ef720626dc12258ce513428f9f341379e2e83e235fe09421bd40a94b1b3bd56d11751b5c4e4d43c3df92149ac07e1881" - "1995512c1ec06818ba493c169047d7560462d2ae6c823d225b0ede7955e4f4db22f38bbe4c0c99805ad7cf7efddc1dcb68ff29ad655adc5d" - "b48a9635c460a63e09656b7d2a7ed3d8633f5f0baa94f5b5a97cc20d21536cfd23b663f54fcfcf4d85fd155f783383d1f54bdf178bd18141" - "1ef644057e80042380042345000578b509400040066c190a2d00010a2d0003145196440dc79501f4439577801001fd2b9400000101080a7e" - "069e553740bf255a0048245205b9248caff7f8b7155c42d1d8dc46e1f2aed823b2495eee0cfed00fe5cf3771fb7ecf68185661a71b3799ec" - "82bf5cd3c646d60b7bad57b04685950c72785a2df47457a020a8b0a8045bb0549692f595465c207776af259d754ba23e478bcd3700ecfb77" - "3592e219b9d4a631536c28c09a7072e2ef254a007bddaa526e95da594dc188f02b3419b3674b7be02886281b3dbfd35ca9f0c34bbae549fa" - "27ea5287df1a2a4e9becf0c38b1107b5854493f27574687fb7b7b89b714809e7b7bbf9b69393a430a01721116bcec2de4a1363d2336ec4e9" - "08a2c8abf9bf6bda8197c8de793692505e44b7ac6d1f962c71453964da38413720b92b2b4c79ef20a0ac011246fb78ee914b1abbb905dd10" - "b5c3d169d045d464b821431df1c0bfe040bbcb2402e2fd02c24be8d464f4eddbc5a43878a4709f4ba8a09761ae6aa8dc53f181ac264824fa" - "827001461625f289c5b25b3fc72ceb346f0abeeb084bfb8e95042efedec1bd84400ff8b70505d2b21a8456b2c21480ea8d5832ed4c975535" - "7693e848558710530333aade318759bc02527432fa9d2277bd275d0532d00e4b82dce5569e87426556298bfdc58fb823a9cbb82c612af06a" - "966d11221bebe6fd536dbb8ff173a93f6b8c11edfb1782f8afac8d835fe3dd3fd9ae2295c40f510d38d2f39e064136e17225f50ff6349268" - "2399e8344b1cb7066b98f1f3f99156d9cae29a42d5f3e6560350ececa558f648a73674a0b04493e458f8b075e3e39f2c14d79c0a330b6561" - "69b84cec386c6b18c73c07102d3ec4f6db7c2020ffe81a6487703fcdf00ed786542c841b37c38f5d15f1c146fe63df462a8b14608946dbf9" - "d5a51b3c6974837db4094fdf3956d0f7bb89db087b8a6473e4556f5e120adaf50489b3b6fa59f17e6d991585f6f0b1d4722d2ee5569dcac3" - "22415f316082e81f34a0c4d4eb698120470c641f2589415b9efdfda8f8d5c2e7885d3e427d44539120fa67403879e9090af2e37c1fc1eea2" - "51c56f6ef9a4738d10d27190a3c9628887409a0af60e7b6904d877078201f925034c027e58d06eaadad9d30e7ed842cb000475f67e1af926" - "ea7cb95e2ed990b4a92bfe45be1a6fb5856885f2c1bcd4ecf7d30b234cf4aa57510ba9ea5615d81f4f5233e1ef8113e461fb4b4f33f0d641" - "e4bd72c34fb822b7631d31e44d5ce2fa0e0daff23ade30a173fb21c9ef1ab72d810a13b979c50108e7a616b3571f037618c5e19be218bcae" - "df349a42174dd455ecc459039eea00123f8fd940d320ec1a3a6e793c8fa8202a36ec661caee265b99323cb67970048b1b0c92370339ab5a6" - "3f92ba26513e17958091af1c950c2c29d7d9e2b21d70d96f19dc113199a748efb00af71e7bfc217d7167cca345a3b2751cf9afe643cadcc6" - "3c846a78ed2d70a7ff54549f9e753010a88046de11158d2f201c42b877fa8d871b1ed8cdc7af523f553bbc4b754031bddbec28f081db2396" - "c6af0300e6b9b364d04198998c5cb3d2d816b82bb72292625e8df7d8912426eca4ae7753ccabe938c174229df27f745c9163c547bd5b562b" - "0f2df75e73a4ff56bddfc660b77d51decc1224658a88e36d587f4e3a0bb700c4efba3b2b015a3857c25e50603ee93060f8dbf262f38b2ddc" - "23e50e63f0d765de27feb4234acf5024582186e017f54efd8c200690bd04982b0fdfb98c2d5142db1d7db49e677d0e2022d801bede20d590" - "2c34644df3f81e777043ad47b4996b11a4727854b0d20d48adc516d26372d15293605e439c4efec98425e82d3d9757d2b95e46ed9d6cd0cd" - "581d986d55b396c3c7b5af049015b145eff9316829f4ba82636c0f5cfd64e6d84e3304bc22ad93c7f419d528a990b66ae71a563e2140d6b2" - "1e77cc6212e46abb03909c44057e80042480042445000578b50a400040066c180a2d00010a2d0003145196440dc79a45f4439577801001fd" - "ddb200000101080a7e069e553740bf2560716ece116fe44e5908e49c6dc7d0344b8b66b78ba472d996cb49805c3b6180fb6ff71fee8fe5b9" - "9e322aa2b28b2fd5b890ddf3b773098f92fe06e1fc68dcf47f18a952d08cbf8410c690843fe10f3dd08c3d55843c7de21f785becd9cf9cf2" - "88582026f24399c8b184e3590087d9cdc7e61f660e92f9d242ac00e401012714d82457be817d6e5385b59ab4b593cfee2f8372bec4439575" - "6f2c1ce4c5e9bd86fbf2928aca6a96e05211993d555186c7d34ffca2fde8894e78de7ed273af95e65ab0205efb8a43b6919851ff0722f160" - "eda4b2eda8214454315cfea01100db6e36ad2670f3eadf2c04b2e5e023287f95dc7fa5583b4d7d967016678f79438836989786b3e87802e0" - "bae34dc9dc60d50faf8eb1717c2360c4b2b4ed2196c57e1c87493c5b30b7cba943acf997db34b58ac7ed788adcb14575080716303a2c8d82" - "02fe70faa498d1d1a1da0751a853e63e966bf4fc06570910d36df079fed97f6a7dea4b09fd1c7464500d14e8be5b36620b5331329def6ce8" - "ed7eebc3fc85bc316e4b47b8b4996172586a4cf41423da8361e8f3f1eaa2096966da7a086752d207141f098b1b622ab3191b5e59bf1208fc" - "2e63d52f712e1201a78e6705a7fffa1c4bae3bdf336a852cc8cbd2124152fc6cfc4d690e28fa5f3dbf667599fabb3a3d524f88030973ace5" - "6cae4c57cbb829f76e189fbc3f743521e5fe75f782b0619bc313e4d789e2b0f3f37ea443bce572eddce722c9949527949af00b40ba30a332" - "c6fd0dd96aebfca81e29c5dafb73d99aec483ad380d92e08798846ec67519661a79ef63e0c37c93208823aefc4a8e6f313e4c54c623d6fa4" - "f6bc3216110aa3e457d05864f0fd89b3f2da57aad3cdc05c5e4dc73549d80a61cc3b1f5171aca79b35c1f1eda3b113fc635d07b89233c46d" - "577a1a4f8243ba23f5c2b57be08ca992d14e43b81c277ed19b85b903269dc877f0d18cd69c7ef7b7ef8cf2f461c5afbc9f9b679adee58e53" - "651f1bd28dfd3480171cabec6aacf1919ad803fec869345bddaf8fd4534333ceb64c028d0c17645dab653263ecd09aa9add9639544f0559b" - "9413d98df6ee4ad22ecd690bd15f6881f98afe55d56847a1710cb020a67f22f567f7dba73dc9c9c8e31d1733c1a0654f5b49f75be9cc2cb6" - "401aba58906c5b1b4da97d40bfbecfa55fe09783b560b5cc3dc28f72f43e167c5e8dcc9836515111657cc7743674e8c6d38595e8f1860a57" - "ef23a95e58340cfa3e60c784ceaac7612de4c0789f9baa2674387270d1fc2fa522a5246f4e6bf53ab935a87e5f790f29b083c10841c3720a" - "233f21257eeee31a8b987d39a16278b302869afb3340f9dbcf07dbaaebb88a6f143adcc1273d44997a12c52322b65995c086e557493709c2" - "990142ca039aa5de8ebeedebc482143c2154953c79a59c0777238479bc934c2a35278759813df1daa70b1f508560c1aae54633bd81964949" - "e6283a646c810fbdabab67030e7dde8e2f3f8e974b63884e4a62ea58477d79dc2599b98fff4ab84f6f5a7312e3f4c71727b00930782c59d9" - "366cd99e38f71088d86d22863be20e88fad8319608a882ad8b2a4b494a7a55fce46de429b5b511091f7ca2c5fdf560ba937f00093d73e77b" - "aec14ba65884374e4d73dec271748d1da9802d9f06c2d4ddb8faedf50964f7ecdd10d850f9e62a3fcd1cf8382b0f3a101d68cb8073291c46" - "9b16ed8ead94a0f21b663c712c299acec7fcd3b3dc0611cd7722979fabc089a03bc9d5d5459d08c53cb479c16360d6c5a31681657806f331" - "77e34e972eea75fa843a68a8f0570acf84118cfa65293b26f4ef6c7393bd2549c1a946e0e01fa8fe6a2e94fd04bb24ada5fcbaf4124520a8" - "8c13f270eb548e079937495dc505ddf5583c9cba44057e80042580042545000578b50b400040066c170a2d00010a2d0003145196440dc79f" - "89f4439577801801fd7a8e00000101080a7e069e553740bf2517c92d2556716d2a974fe272b0c77803f94a04931bb9f4381f66da6e724817" - "316ecf5e9df3f873ef661bf3d97f0d0cf7f5811cde2881a3dfc33e25672b9f48043fa4211c856bcd0ad44179a979aca031b48635ec0f7d25" - "75c5fdd1d8f8c6f9e3d8ff2b6179947571bb6258c0dbe931fc33f2e0eda5f4a5d0d4c80fc71adf3c2843d890a21390685538c46bfde9cbea" - "7ef798973412122b30a61d7744103d813c58dfbaa93634fa390bd1adac7961c613021824a355a6ecfde1be8e3a75f8f284ac65fdf447d913" - "c595e09cf38659aa9d1fca56b83a7d616abecd29b757d74acb442cbc4d3fb5d51e35d05de2d9d0d1aa6ba7a8723d2839e65ac6d8d87b7fef" - "9e5d35373353dd34149f56d42b534311664f2ee1a612862f5190abebe4b19b30fc9c74f2aee0c85383fcdcfa1bdc00fc6caae32dcfd6118e" - "e503e1d1c0bcc5dffaf546c6c7c5db9276be64bd580f6c4e5d43c3c9d71c536a37e46b0b94977fba6a6b26e9f99ad8a79d53d5f0fd1cf87f" - "cc80d591f02fac555de83f9eef31eb95c1550d377143135fa9b1af56c4a37788b7221c4326ba6e85eddbfed8cde6adf29b6f469d25a2158e" - "a7f2a42357fae6b4cea6c563c90b0d38a482645bcb5ffb4444e938c7964a267443d7e4fad058b528b40739362f7964bdb452700cbde2f2e6" - "22530b87320dbe143ab63f080f8fd3e0604e8a2d34f032bc26cee5349316d71a0014fa6e42498e3da53d3139598cd45008bcca7146ed50a8" - "896217435a92c319696498d63693fc79f97f80a66ff0c8cdb1364ad1ec81b381fa05f2983113320fce491f3c405465ffbe9f93c29530aa28" - "e5ef36b5c6f4ae2d0268eb8bf2d3064c159651936ad0711eb4d93b712d1a433e220ca9c6a31a99fa3e15c5788062d76dceb279b4aa223dcf" - "8201bdb63f4cf4b6e89153856d01c3c681605f4112a9815cd4bf4676de50749cb9a34360da1e8c82f170a5be5b30405bbef03498bc8199a4" - "f602981894537eb9bb18034871a3d9f75ee0351f33950a7a90ba4cb9e886dba1778c5601517c97b43de02821f9de2d2d0901c76176c5270c" - "309cce1472c8b3e04fccf9c3553285ef97ef36c1fab2d9872a5eeddc4b1d6774a86ee42ba8d03d4d1ab3497b99eef7681affaee0ee05f1ee" - "368325b53d14685998cb1ceaa7aa83b2a206eb788de6612cc4689b337b5cb30a90db6e93c0ce8f1e87c64749bbfa94b30625220560511200" - "fc9d850d8349f3958177a8e7c0db6e918b712190658f56e49ddf771d535a3e10ee9d037be680364a55fa36dbaf306a4a69ca57ef96d0c99d" - "c09559b8414d08e842cbd7315233b22868d8e28d1876bc9118a515f28b8ce8ba1a87ea859f6c0839be57fdc0fe5ca7da5e7eafbb7610be46" - "e197f0d4b76cc88fa7bc31bc88cbb7c2b6860fb6dae60569f96045314ccbf7f8f9c2db289781cde585a2fe619f6b2895a0744e83a4a8f454" - "75a2af469e7cd9e29ab5269f372fb4a1dace9db078e55d8e4292c2d82a6486510e9408045a92375cd6bd3784d3189316118b0b893973d696" - "ffedd2f21a18dc75cc57e92601904ac070ac2638de0b93c9f415f36b714be6132b72ff80c9bcc25a2892953814fa5d108dc71e426a8d0810" - "63e4f98c7a498370e853bac5d7b0e34a1664b9977954cc54f72c9e117be2173589061f968e515a5728f8e9116c04d1311fbd4a99f20a2c7e" - "7e30403f2e5e6af5f1edb8d16903ba9c10b7e317fbaceaeab34e0d363814eb8e73fabc43d12d7a9f59dea1f2310b4c2edacc0dd6a3e472cd" - "1c34deb20610ccb2a3bb83d3a854a04f67167ec9902f8be601ef580d7944c53da98527e073653f23827cb2240e7e1a50b91c61e3655959e2" - "b27f585a8a6855250a8b7020f8bd3ad70733f8ac3b4a4e5b763b09e25344057e80042680042645000578b50c400040066c160a2d00010a2d" - "0003145196440dc7a4cdf4439577801001fd41fb00000101080a7e069e553740bf25f3b2cf587c3a0d805c02749c1658f48c2179d4af9723" - "dcba9ae7853969f0c2c1d29090aeb5a93d152e3375b31cb07e670e54473a1f099c8a1e4dea6bb1398d0ef034a455f73139650b9c81f0c4f9" - "7186684ac8d7a3a3382e14068503bb5c1d46e41d1b8aab4e0762ec09aa0a28431aacd2f358ebebe4bff753cb5580ca577f227252bde4e191" - "b8d752e77ab5e1ca81397067c8ad4cc42305ead8b8594973e129378b6fcfebfa2a2434bd4639bc2dd08627da8df874899524994352587852" - "747827f21f267ac867f0d5b187838759c8e21c4ccc5a62687f7abe988efde19378f19b91213b0602f0d780ccabcc5c66892e390f14bf8844" - "6722500eabde9c406f10eb81cfd09ed927ad0641a416dd7218e4ea765112dee1923b8e6fcf849fade0346df045fef523c4f94d010513d689" - "d4f1afad86cb164674390ea9c32cb60f653e5953ca953c3a1c9c580f90eb2d67f8ab03152be8210dff0c19cc08959d1eb30b63728ab607b9" - "0e0cd8715bd2921ac587be877c43686ec21c8a56b8fcfa3bf8c9e933f5e83ae5224ae09533adb9a8872abdddd3a40ca00ce84c63dcff36bf" - "4d0d3ce5a49e26f099bfcf0f8ca3c863f139cebc48d77c4fcef38336901166aa3d2ec52eb5e65a8f49c7b601ff93c61042688d6bf00d7959" - "5daa89ade8af0b33d3ea809c6697f37e92c0dd42b45f58da940d534a1f39cf8346c528d64f4e224ac9160f398769017178403cb4287a3ecf" - "aff1543503acf9b2d33ba69f125678011565af6f1259ab7dce10badd517818fb26320b0f27855d2bed0a4b870c8241d9c1cb566c4830988f" - "818ca2a09c0342f475982ecd79c6cdea4f9cbd3388ce451128b59a24dc34dc76065ae17ee02356c3f73bb8a62731eccd4dbe6c11b366e58c" - "789cb7ff1fa412a4d3c39adc3baedd2e75543d5af47de582099d443e382452a4bf6f0b01098b2df6a74226017c7a857d718233fec76981ee" - "aa51809e70b12604bdbe58140d683f4b233a7816ffc3c52788ea9a62074d180cbe55e06f5b2bbc64f4448020ff2c690a01e460105f289de8" - "efa237bb36e732726d065ed45b364e65aadcad483e4432ab36017630134b6c2e15ccd01d77686750f4716c109421f4dacc5e671e0a96a3dd" - "a032fe2dba0d24c8b3b8d1c7b735aa10949bc77cc8fffcfe079e121e5ea9a395b7ac07144dee4e17f25e0df419d0089124625eea887e25c0" - "749bbe1f6a939740d009da408a3abfd8765f776bccf49ef916da7ae6013318a0869eb9a2ee7dc5d18fa616d5aa47a89e12f0184327d14a70" - "f4e0520a10c35db213c1e176a5b8a28ccb9b3658c125f2afec473d74ce2a6f271895abc1dffe332843d552706f1723c478ed247348f2fff2" - "cb2a1f590ff1d9527fcda429811bb120c32b90b6f3c9d3a6b22ca7996d31c6898f02fb481f58f56057999543e2e9d296bfd41f55f5727bde" - "0b465a7205ed8aece49465ff2c5dead5204ff6eef34c79fdc022c9494bc8e37a5d51228f9029240d308f157fa29586aebe81f1cceb0805bd" - "fe65545386acc385fc4bff3025592fd86e0c618b17de2e20fcbca26de823ea2fa06c820f86c4ace9e48199dd5d1960292812adae90d1f4ce" - "f699427fca67288702e6f3428a7021966ba1f580a2ec8a50a8db84571e0243ba87137841801cc6341a5523aa2ca780a2adef96d6ff99f817" - "7257206c1b8ff6e11ece5694bb8e4dac6a0c2d208ca67fd7db35e8cf53b6eeffc2841c8c6a5293495c28a17b4c2c3bc82e4952d0b22068d0" - "7d28419c886cd46cbb4a67284a9b684fe94f7bb0a31caf435395b37fd5fb322bf8ed0fafe8641f1a48e600749aa04e7514d6090c4e6f1b69" - "ff270ee8d8466e9bafdb1b463c5d7b87d4f3b82cc9b9207fb15dca35f072b520b66bf5bbe15f44057e80042780042745000578b50d400040" - "066c150a2d00010a2d0003145196440dc7aa11f4439577801801fdd91a00000101080a7e069e553740bf2552d692a9ca74e85ae50d2409cd" - "f21b939c0a29d33a7074e3fb25c32bd63c1c1f35c35cf2924399741e0fb02a1c82d73fd6e911d791f30638db8a9c7da0839369b939f8c7a7" - "37057f67c3b241e6c0096c3da948772a03ca0e750fc9a4f3bc1cb622bb841bf190fb2d6b30a00e87bf61446233bf8a366152326610ca3b16" - "033ed60d552a6dbdea561904312e2aa9654f3fada8bf615a445b95cbbdbd62ddfa105eda2625baf4e72dd6bf5985aceb040bddeadfc3935c" - "6ee17d515c755e22c7d3fa52f524319f5eea821fada5ac6c5c680198733c23890d1ab36a3e37aaaf64a3980fe2e1fc9221a725955f6ae787" - "d93862b9769b8af4c41dbf0f35146c77886cea4203187d6de68dd59d773bca6e57b067e6f0cf9020900acd2d5b5790288729d0552da438b0" - "0248c0d34f668461f0cae1d86330db2a1711dd17da77cef2466440dec9edba3b969c57293d115a5c0437f0d051d2ae6636c01975b9bcea21" - "0eb56166af777dd58096bb4d2e70e400e56dcaad8ce168b4638aa24982c23db4355b5034487df21850422651bb541df5adcfd9077434a5fa" - "7968683100f2080da76b4914220cd8b62a467a8f4b74b16c52a08b9d1ca92b3baaa136e73b290dc5221965d2f439e2c473259e745c81fc09" - "f341bb36df316dc11c5727b649eb53fd6b6acf9fdad932cddd68f2e196f683afd04da86ea6854495ae45b55eecb057b66dc450b8f18eb6d9" - "f4002137e71df5aa9df2fc99b5d2763761bc828faabc0b204730a00fc318f78d6d5bc895192cd8df6d4d7b23a107aaed2583783bd1204714" - "90c757187f8edc7307dd011539f7fe17b8b1b22bead8f1d514452498deafb1d3af7e3924bbcdfd71e83050efab05e5d63665d986d17b592f" - "c8f251f7e6b12a4327944b92eead549df5d6675eafe749430bc7ae13ad389954f6069de6e6c8964e2d5434e74e468c46dade097470bbd58f" - "d61dfe0c42bf5b926f6af174fc08e7469e7bcc52b9757255c1f95c44400e4a8573e9a36fd2635d855b392bebafed0d23b6688bffc1b223e8" - "5df7133e30b50486ee5aa953d5bdac02f636218e3fbb998230b15f3dd7cc7b162d19b9498566feca3aa5ec62eab0d23f15d2d760f962be3f" - "04cda32fcadcb3982cdc91ed6b33905caa7c256c49311e8ce8218d813e7404ee4495198eaf1159c0348c7503a5ae9bcfe07a07c488013e98" - "a63f87eef515fd8ad1628973064f528381aa61590f91d0ac43f4094c85ed471049c8581a83dd78186517d3b111451370b8f18d77db8dd7ff" - "072ee97bd9b4eb40d4f34413380512b6cba71a8857c6ca819d438cdc24f2441a00de0b90b1a6ae2b9cd8be6411fad125b3a408fb825a8615" - "5973a0b1320f51a84d7fe480af857336873f6c891ec789d48db0c17fc813a061bedfc5d2622d05f31fa888f3b9cfd88e3f141dd81b376a93" - "1dd4dc0e8ad635d5fed4d327c463b498092fa6864faf737fa18597cef2b6b14379378f960ffd02e1535b90f6e7a244cede14d90d93b6ceb6" - "266bb7883ab72d70aad7541e2f2182fe17da7312f4052a6e86ffd92bafd1d951e6d9c5ddd164e10270e5e627c1cfdee474155a9691cf7c99" - "c90db11abfbd6317ec27585a5bb6c35ac063ba0bcf71799722922e0a8cb0cc4b99bf0df8d38b567ca2084b34e1fe28ac6be3499fb352e03b" - "9a16a616f3f234b39dd3708932adf988fe29c18cb75b00fdfcfc98a9109ccd0835efa04b4b71836cde77d42b8dadffd2b4974100b95a7da6" - "5ee8ca976db21eadf3535c06fba169ac0da12f8361adbd2c359e6da9d897dcac6f21f0902007f698a909d7d190fa9ae5269c5fa6799dd2d9" - "3bdfb6ea7de0c434608bb6d79b34763172233b4c026263959eae59294ccdd9b7e2fc35dd5a208fa076bfd7b87c0f1a44057e800428800428" - "45000578b50e400040066c140a2d00010a2d0003145196440dc7af55f4439577801001fd0ed500000101080a7e069e553740bf2576873716" - "8ba911b8aad1e5691f76eb44590460d12a085172df37c6c0a31bdb66e760dbb39c20827550480a32c6bbd4203c2eae369eba003c18047e80" - "f68c6818019fd47799737e65d762f24deb9d244b31f981a48f676be9bf603599ed5b1b2a323cdaa6b0266db1ce6c4e38baa974c79b91b931" - "c2e74bffa3cfaca592b2f5907903b2607cde85d5e629a4b9b298c6c382feabea51a3e1529fe4c93d14c19efeffdb259317fef77fa01173ce" - "ef95ed3f90dc4d1aa10d5d51d4b87b0caaa004163d1940ca93185514dc59d03a2a38fcbb81efc0aba0db4cee095d5b5987c42ea124391c70" - "a425ee833af4ff8f4bc14f097bf8e27d6e82d75875896798652bdc8a10774d12dff4db2aed1327e44ea7586674acaea77de90c5943f641b8" - "c6573ff1a9de67ef957e93f766c61b5872bc1f6c409749e0d12b17451186a9ef5d122c726505e147b0165ca9bbbb7d3e92a9b3c5988c1fa8" - "2b2ab63572c1be1901d120df48d0edc08602bcef396a9f641b8a29315d374e8c37a40b1024503379488db2088811ecab79210e694ad17f14" - "6b2a111cb967e111c5c9be1add0b76f1e6bea3c1d4ca359e517d6a77e07b6b043a3f93a16994807ddf3b9f5cd35048b358382beb5154f609" - "9941a778e6b91f5ac804c73e7c9869016724797eb58af2ce3365bde93daad532ae72e8273d1fdda73dc1ab9cb6a8d142a536f83a93a3d95a" - "7cb8aea041584ff65d027e9cee131e928bc4507341fe6865b91ed6b9001ea9fb4e648b5e68a99a3707d8af931c3b614f7e89d7810614272e" - "439a74f15bbda45eb665e3d15cbbb64b3166c051927a59d3521116e9025a9a6ff61342584cdee17fd04d62325f20a37f810085aba498dcf3" - "67e0c30d43c9f0cfdebe06f1d8552aea59fbecb8e62728f52f19059761474b7fe9095c04f1a271da3e74c907daf6797578940eff63ee3b3c" - "0ecdb255652ab316ce985477a8688f20d56419fc93f7e752d26279b07ba2a6937468f6fb24c9310a6f13a9c4daa763d9c0cc83c9be2148ac" - "9fd50c2d752c9d5c5cbfc91c793a35950b76475d8ef615cf888369bac922727e6fe2c76c2b12fd855564a076ef13236c5adc3f4b1852ed68" - "b19f21b4b92c5c0a7839a331bcbc3d0e2761523d154e1ce2f76781056268ae11a67d831ff6f96a946280bbe67096f1c02f94074fcb3a73e7" - "dfc46ef6c55e6c5fe5ec311a718892e636d91d4889a54511392d1c46207d6dbb26f10e99797504cac4ee6f0d11a7a2fd24bb7e3be7468555" - "8997ae1a3b518e26f592d2e74b5e545cd83fb587e0dd248fc76bf94958819e3c5675d5ec54a7f428b42aa272d155b10f4bd2f2d237135934" - "b5c7e79d321e31d63f0dcd5149ff95fca67ce0dcefe2d25a3f825950d19af464bb648582d408b3ca36cf9302b3d6806bc52c821762300f88" - "984b5932cd65423e349bb2c19503dfb768e6df5f6641f4a457327015f6bc1281fb0d28cd5bf51d5fb602ac56ce4e79283b5df2fd753fb201" - "900b1f78b2236dee983d13178ac4c46812c9166078d5fa0f059339fbfde05160a0065906c31db6a22c733e3a4dcc3f07e4d4aee0bf9c74ab" - "b29c8a8d22da79621c815c607aec65065505380bf9421445b19ece70d1c9f07f83eeb8d25831fe5961c9247fc75607c5cdf426fad8c905be" - "8e253d02f0563255efc4b9de89773ccd38c9fcc81f43811661ca90c22fb100b87254845c7c5e5415307ea4355f8ab55b60b3f6a42edde203" - "8b0890f15b2a8ca73a8a4ad30099c0a779525ffeb972ed7fdf53a599e1e41fb944af96f1c3302910d3b30cb18f8fe18c54ad31e7201c8847" - "817df67ff2346cb34826fb12482804ccefb46297d4f3f1e9a29f3cdf1be901beb5bc28457838387b33d49861915ffcaf9471520a4a88275d" - "44057e80042980042945000578b50f400040066c130a2d00010a2d0003145196440dc7b499f4439577801801fdd59300000101080a7e069e" - "553740bf256e9caec313cb79a644cfc509c6025f557f000902f99ccac1aace4083fbf8478d094770be36356e2dc5bf235486d148cacb73c6" - "db8d346e74928f238798e9597505e1aa58ffd4b1acfbff1c9ba3c0a73496bc6c77f114dadcbf928dc5a45dce358c2faf7e8ebad82c911dbe" - "b4dd948a1ed1609bd29e37339e865f471e1aad7243c743937d74ef4b68dd1c140544f05a6b3a75dfa1dc0641e26cd4aae81d73b7d2db0c2d" - "00b83a71369e1e5e38edf5bcc2cdd93bbb2083d80c8b858190dc8a6b5072179b996e4bced210dfe54865f32c8d21b58ec3fd6bdb2f7eff12" - "7d3a16a0aff9aed04bbcc9c568bd8c60282e511695fb7bd63b070a81d904459520aa4868e81509a3bbeaba5e1bc5ddb769b0f5a0407aa7ff" - "642e991d370c37419f87caecc34f7c0e9151624067a8d62b6d18f9bc4abe5bbdb01621eff39015b160e2ede70d70bafa4867bd302b140756" - "063605ce2648d08d7c1584d0fb1981224da8c346d33cdab4497bf8f2ebb4631cc7cedb1ca4dd2d31afd164454579d307fb6e60ef56422e7b" - "c82648be02dd60b0a5cae483a432dc7a9f9e339f3f86d84fd3c5745aa3bbdeb67115799e74ca825d4022bcc2e666502c26083f949c372a77" - "52dae5f2e1c908a4a1702fed188730ee537493cddef8cf7feba102486be1ce966ee34c627b5382a45ec00bd185b0d2f7548ddd421b4fa995" - "13851c97c4a5bc24f7496a903f226a3d0cc89b3faea32af966aa7b64d7cd6e985c8d78a96081752b14edc0de777dd1e7e77ea86cf8277eb3" - "250edd3e59424b7fe31c2510bcf3b04aedbaa91eed844122f113cada3b73918a8c6369d054e9228cb3a413e11bbe8ebfadb280f1de41be9f" - "ab9017133af8e01ffacba53b0a1b1d1ed82a55620e10f3c6588dee193dd88efc99a3a13fa3c8c510cef96329fe0c1847532c9697dfa51b2e" - "7dec0c89d4a195b15386360542145f800f34c6504ee068e9be48e10834a6d684ea5dbe5b1005fa6abc39b1783ad5f3179428ffd72507b30b" - "9b7d3ecb60d5e4a84c3e3f23c2d89b9269c9fe6004dfae042cc86ba495ed4160c4adf2509f24abf90ff578b7a007beb732c2c7d4030547b0" - "c0f8d9c4cb480a8c2dbe0af3429f59e2745b31274319b37fd68a0e2fea9144203c6e6c2aa440248f3d9554d062acf8275e77885765646d87" - "f8419b03e1cd33d025f67a8bb05f41e5e79afba5e933d1dd9117b32e1dd06acb2b8215fa09429e150a0e93c10670c227e835ce9e1bc150aa" - "a8a589b62167bb1bfbec431e0605a7194cba5abde9f15bfe116d480e76c199cc36dbc8262b0f9b37228f6da5d6815aaa33c53757d656fcef" - "7f51cb25697e0f0589518479a0b0f06e8540b7b79c8b0c5f84cebf76a7bafb79c09cd70608204d87a16c986753b4667678d34d6b47279915" - "49afa0b872b66be6fa343e1861914cdc5d7194f1ee38997e58aea1b1a37127ac4aaf552e42dd29c7b2ec449888c005beefdd50b20a6c5d23" - "6c66b2bc5712ac2d220a7a6ab8274f3a3b8d7fc9897b0a4d7d2d527067ac27f004e97e2b5ec70629a75d418f67ca91d5840985ad39465a1a" - "8c5563f51cda8ead83970472e98b48f9113eb7d0de5b234da2505f80706954ee8fc34411aaa2643dca45ab654a11fa205521082d1c29a766" - "c9852431f53e4b9991c70c595887d297d66190907b358cbd8fb768af59421769cde354df070f824e863cbe9f3d3092b8e936e2af5b31cfd9" - "e5126451ab1c834aaeeedd0992f1631086bb8a96426e262e3051737636bce015108f609da528d2b59e244893404c85fa6b04944697cc5cfa" - "27d2ec47c79ce923dcec7e5c87ed1bfb341b34b7bb9f432b183102c3f250f11f5cfd652c307d82b251c761a073115cdd4729aff76829c203" - "f6986a9bfee364451744057e80042a80042a45000578b510400040066c120a2d00010a2d0003145196440dc7b9ddf4439577801001fdafa2" - "00000101080a7e069e553740bf28dc9a0daacd7eee4b1075395cefe971f9827027bb282d9c943c6157634ea85b762d06d5c3f0beeb08d5d5" - "c6c64f52b4f97981c0351817cc7d43a357cde71d01e990ea00111fbab88104de2ec10a950ca8d2732ffab94d159223ebb629c1712704f5a8" - "2de15a4829ed1cebde8236c8c85e6eea7d063bf623218023b5573e144d3a3d3e87dcfa88039eef15b416566b49f32cd28cf0c46fa70e35f5" - "50c0e588054e083b9d0e2ecc1e5997f31940aea9c6efa56a09f44f4646a4e9a41fba6f271dd5081cbe9c18e4eb565ee4c84e759fb7460960" - "fe46a4231fe85d81137490371ae9791b0af720133b112f892bb1319655b58c41c208196a06afbdb8b1cb9b1934b49c7de6654996b36544fa" - "1995c2146d9485dbfaca268df19e5addcdb081fd62b975c8c40e8d32a894ed9b217f08bc1150288b8c388ec89a03e38bad24ff081b33a7d4" - "cd73136574937e55b1988f3a7feec09717fdf7900013fbf90b9d382d56761fc9935fa6af177f87ca6249cfaa06a02884bd415fe975666ba3" - "78142c17e315f8e67d71ebea10f43690693d8f15930acd4192c282d8ae3b230518457246ff635b418e9d049e7f18ca25dc45972d0b27dbd9" - "6d4a45f59018830d561ce55287a43beb877f7dd07838ed9d65e5b06143b367340e24a25974d66bf464c6660ea8447f2fb940bca3484525ed" - "6d2a55109496a16b3fa3098702570a5017b6a0d92d8339c5a9d86a93a7eea74f06da506ccce3e582860ec890923df9bc0f0a06b9f3a7d2fe" - "ad9f473a950b5ffe76b9ce70896adc1888290e3dbae0432b86a4c77aa9100f2a5f1914164f108c58b38083dee6d13163ec3d7b11012b025c" - "e35987351c157c5d7c9e6be31e6227f1d1b0b725a6188f710a3fad726043be62b51cf2510bb42ff3ea4f276190833689ee7fb8e054f35af8" - "cd70118d9edb9bbfd578275725380f286faef76e0e804d719d7fd0d7ed70869011c81c2dd4e72bf4bcbae6353e495b5b277647b156aa88ee" - "b2f1cfac789f5a1476a7c7b45971917cb7c4837299798d901dd97716de35bd002aa9d6a96fe06578c03c253e5d79c1aa8de8f2098c146406" - "0eabc3838f17bf6ab482b2bb8273017d45efe6fd61d3c45cd5d99bb7879906ac90cd7f310db5c6dd659d3cbb39cc83c14a2791b797dcb4db" - "3f6e09bf154c26a7b99055f05571763ac1b596908b70100f5f7bf9d213c109d52b532d507f6c6b2100ef9fe2a577d1208aa16824719078cc" - "abbc2e1165bcb0dc6909916c10edb5a84a3f3ad7aeb38f9950fc63793075ece33ca006c9f874a319efa6e8d05986d2912f5753a5cf792203" - "29b5518fe9fc40b0c3fccc70e8c4b625eb04df59fe4700caeda2fd6ab5dfe5704c7e39941a60641eb694d62116c96ac6cd18f26aa78c28f4" - "7aa26f5ea47a33403909a26200a5146381c7d91d249236f544532cb98ee84b687683df1aa5357aec12ddb9c87f062af6c7710201f0e41bd0" - "deecbf5d6e4848afb1925afe219c5371ed191c525a0e90cbab8c5e0fd56bf01cdd1e6a6dd7b260ade0b5c12ccc0b94de2930901879b2997d" - "c30a88621891c21870f4d30064080183b0b86b19a0b71ca7f7406f6423a46c54e0d345891e2d0e9fb0f17bdd4756063b58b997a9f557145f" - "adfa68f75fcb9b97e0c896b2accaa040e195e3ce11bdc15cc59b222e36cd6bcc8dda240ef3b80d76c3ca6d197f65d0f22f1a1e50b51d9f7a" - "7312b97bbc0ff7a18d8cb11ca0a471f219885ef7575c6fed15c0ca110c43e2a7caeb10b9b5a87a1b404a150133f3c681ee30f16bea14080f" - "967c4dd95a0078ef105480277c36aca1dfd713ff62b6578b6268cfe2e764e3967234a6864505c4545615ca12096b9befb66c2941a30c1d24" - "ef52a888f5dc697c96e98081aadd6c7218b744057e80042b80042b45000578b511400040066c110a2d00010a2d0003145196440dc7bf21f4" - "439577801801fde1d100000101080a7e069e553740bf286d06c3cf89c41d28071a658d0bc2016c84c4ad8429b6c8d3c3ae074d8ba2d71cb0" - "5ebfcbb27f1661ceacd610f0a02f6f01c9939470ebd5d3e8c7ca4702e1d1cfd6feacd2f992cf9371d40173b614bef5d98d5cf65feb1235ee" - "8d359aeadc6e6a09d4868dcffb2801558a312bc46584b607ab5a55e6b0d52d643aea790e2b47ee62c58ec9fcfd2fe214b79b7eaefaf06bfc" - "8928b7d17e164e2e9baebf3ec0b7d483f17ecd06a33b04eb5c70f3d77ce07bd4875767e0eeed24b2b002781e99f6892fc35a85cf0dd25399" - "4543367652a9a9ef0c01bd4622d159cf9950a274abc5e869258029a55284db9578489d9209af90d2b4e7557d3d8f587a375cf20cc0b1f652" - "5bc020ec3066ad96c93862453ddcf40cb30c7adccca32ab36d57ddbaf453f3f8ebb06960ec3b60a32d6396aedf5fd1e6b331317dbd7eb903" - "bb82aa03292c7651f733b146e9266e064e0292c5a7f98e936b688d1835a07fc2387998aabb6a5bdd5054de0ded8b67107f00f174d3c18b55" - "4c11939ff59f3d758949d187753e055656ff9bd99b29b0ea77483a288fc575b51238d3d682657d3dca7f1a42effd67bd1be0b062147e0ec5" - "fc823df361662aea3b51d47c17e7dba5d360abbea2c69304851199692030bc57cb9136fc638d258c8e5cac84dad40c76ae6846039284cf6d" - "b17be5097973154e3296e71e5e9b712dce6db02cdeb5dead8e5a5d0fce93ac403f0633337623b60753381f50a4081d5242a8c4d7eebfc016" - "66dd66cff50667d08e2857786f6848647dc248f71575483cc40bc54227999c600534b4af250a9b4a84917e5d49cff1fe39f8affb7df0158a" - "27b975fea7ad900447aabf7facca9e63a72b5113a73547c443f33184cf2dab6cd7e49f1bc4c832ce9f1b44477b93d10b6ccbc8b583446705" - "84f2514782b6c2d79b974f7b583960c390fb442d9e1d4f40d3445d02263a096c3409f69e303d452ba332c297f4edc999f7c1f37bb1edb85a" - "ebf968052c882c31ef7ea9b55905a76b528eef0012292b1dbcf287f15bb95b4c7fe8f0c68341370cc0ca907a9513f7cb6810089f99af8c33" - "7d84b8c3eb9b2c3982557258c0d4e8878a46260cfbbea44b195f2c08425e4788becbbfa77c5a61848036a69468d1b1ff12e8a164e110c4f9" - "224ef92bf949d48bf56ab326337f37f97c8e456ba50478b9ab07e2efb234a59878c341331c534199c6d0abfefca520082c738b5a28210d4e" - "f4c55edd5aeadeb13ff5d4745a3ecddb187aaf2b6a86b538f6bcfe79bec6cb2c74a5cefd3f6088c68cb53e9489d32a04c51a0577609466e5" - "757586cfd44c90719920a8e6dd62fb511bd013d4d49b44c6fede8bd3bc952c461df78a87a857461296a307b78449a18490fe2a02a574c54b" - "9054dcfe10ffe737493fa69cab8312c8b495001e6b7d379956b71389abbd0d6d4d9afbd235d969f483df1323753073901f0e30d15368c45d" - "3fe98c853be3630fb7b80460524b6a2890daf4278a24fb292e2f7c9f78243593782248c8b2d12d90b5db5d9a52f3eedb7c7c57e5061a6c73" - "b00ded8432609ab28e4f39ad69cb8583b5343680c021c82c6fb9ff63491e250ae0211b6626bc9d24d33e80421d1368ad0223c204602a1cf3" - "5e472ed07e1c1a92c70df114fbbe17542c1b665963883150218d2fb5f0843642b3f550cf66731118cca1f9cada1612da9c039a51f4db344c" - "60e431b3814df9191381fe03d1d4f6abb34408de14beb783f54419777e3c874ac1b72c9826fcfc53fabac402fbf568e1c09af793b3ac0c52" - "83b4c143e31d0de01b17ac376b2519098385240e7471cbd8c9c1ceb80c0bda10076475586a90516739fdec68502afe7392e953e8e2cd0887" - "ffb7d264f2f4705a15b78f98fb2185fca056027db3505cd231caf944057e80042c80042c45000578b512400040066c100a2d00010a2d0003" - "145196440dc7c465f4439577801001fdfa0900000101080a7e069e553740bf28624ee79d2d8b044fd685ef055dac14c406b8e5fe70b9d78f" - "d075d246f2899d48e8f2658280b037d34bf5927a91e5e1ef2b4707d8de0f4ab3e8ce35ede8bef2b5381b32e79add6b418815acbe0fbfd4a2" - "389277a32931ff3d2f2d0e7938c18839269b77612be3d463059e1d561c7bd60c97c922fcd4cb492bf77b2832764bbc9822b09c36ed32ad43" - "4745e7a89e61aa19f1ced30fce83b9172da6b1dcee2fef78d806f1fcf8fb76be3fd41afa6cb040d5c40fc3d75aa97e74e812a0f920881999" - "4c782788a96370753b89efbdaf3cab58ac0399bc6d59b98d5f755240187ef94b4d85db6c23936b60446c1153ef6eaa9d690e19e8b321b2c2" - "8181ae4e4e338c976355efd6780fd8d1527b2ece5e018d32bfa89a2572e80bb1629b52cacaafcd45721338ee6a692caf3d5ade4f9c921bf4" - "8b82cea7fdcf26e8861ccc82a6abe77afc43a3b52b19e8e3ea5a6dd48bb47c614f11e131c907fe3eb4eaf62d37759954f59bdc3085d22e00" - "9fb7e1996f5ace1e62000fbf8e63b4ba6e0395876f42eb485df9cb9946bdbbd42f7b6454badb4f0043c4649ecd3691a3563e365c0e4ae841" - "43d757df3504c724895b222d8568a548b628b7f400f09acc5c3037e866863e8a572692038fd3a7f0b4648dfff431fece1fbc16c9ab66095c" - "a07d08e86630a6ce3966a563a1549fb9b6ea737123fb0d6be4d8b60feb46d584ae11a7cc6df98ebf15d000f39726cda6db3c99b6e57a4594" - "5090996d58580f85e936c85bf41997eab263937d4cb91ddd3e5e618a160a2f34f91273fda45051ad2a2fdc585a2120e5d09fda04010b8a4c" - "b54636a9d8de997b9c0ecbec8e82701bc1e9d18e219efc39e4dfdf6ad6f60b71487b7ab12b61fa74e0bcb2356111baa0c991fc4ffcff9fbf" - "f957ae62e39a6d911fc12992cc2afe71450014794a2b134fc37e848161d01526d2c11dbe2b2581f17a912f0ee8d8e57e9bbc98e03393518b" - "b72ee83d1180ab999bc6116d1ffe6222f0f18b9bc1b3150b52f5909633737a06e1ade1c239caef149a66602f7fbe3e49cc407b10229568ab" - "b3f3a0edf3f19e21367f870449fe433223e0bfb63be430b5e3efca198f1c23d4b6c2d7b90aef2c584169ef22c209a1170f1ac58d1e60cbbd" - "6ec6e8a80235ceeb583174a8cf946d25d9e53990587c68c12f46dc6713d9d05e3ad3d781e61e76129e71e9ae5e70d0010726eb58d6c9ccce" - "3bd86a41161b6e572b1fc5d6e4cca5a41582867df1b72a580fe1f9d8b4c9ac3eb8932c1fce041ecb8518561477c0f2d3c13e13b035d64b8e" - "c7d659620843b474f271efc8d98d3ebb588bd6a01fc964f69e9521d916976bd9386ef63f331047bf20812fe6b514d27b514a57a6451b26ad" - "7f26695aa19a6df807ea93e095daaebfb20a28ad94a2635a292de9a22ccf68f94dd4c74540db868fb8a33a061f50ac77b093683dec34929c" - "7b1da1e0a670d88cecb710d2adb03f23aae11fa578bdc615f7f5a661d6bacd1117da701a5c641f0dce7273f756ab98699e189d88a1513554" - "8f2c9d8581d86099e8e7b20cb45f451bfe1fcad209171be9095b948d2769a6caeb127285ad4f5405010915c3500db6b978fea1014923c4b7" - "53121aae1e05513e7c998872b08016e889ea191d721730bf4714458785c18f0c770eb714e50f2c3b1b62cf79af864d06a2633bcad338006f" - "cde1e772203e13457ba8aeca80d93bea34285118c98e2a698def66d088ad5d36f4d0c22058b92a208d77072a7ab59c4fbd3295869b791cfa" - "7325adc219aa617dbb49415c62740745497919f21989dbfe3efe27e948a581aa53b526722c1b3269963ba00c1653e9b92c03969ab1d89ba0" - "39cccde7e61bbcbad0d25de8e3b1e1f7e59b8c6d837f660bba8f911ca132fa8e211a074a44057e80042d80042d45000578b513400040066c" - "0f0a2d00010a2d0003145196440dc7c9a9f4439577801801fdc6f100000101080a7e069e553740bf2842caeb1585d908b950a399221d1ba3" - "e96439e689b9d46d98ae184d2ac6db54a70e5658b4bb61673aeaca38a8a25860e11cbefeccb9607dbd567cc9f0d07d59e2b86c134a19d426" - "888813c61fd1fbfcb829838f6954ed72881277d00a5c85cac951789f0e0ceab2986addcfbf6eb9ba966031ab5cbd1277deb6438f8d7c836b" - "72b83362b5295b4d1f100cebc6f99bf2acbe2319623b5849ea4f73bc61a3d8bb969749aefc2627be25f0cae587344920ded4e6c3127e2177" - "87252f8346858bc70eb78eba0a42ea5e889ec27112edbc9fc22af418b7fd5cc72d92e5d324220d489ce03b3cbba4bc42ab1458fa90cb8eba" - "9bf389a8597a938032e86d4f2252bc8194cf52f8728344eabc0081bb394a01af9f9496f4bc202132e1b18b5311c38c029bf7c362b409c7ee" - "149563cf5526255ea17f776e63bb06527874dbbacd5e6ea362ca69338d1f704c43c7c84fc876f93caa89e3bb0b2952e92a21112f984e4743" - "e7405560005327443c82bbe538af313d4e45a60d56bcd4d5db54660ace09483205299f274556014f4e99f5c1e01bff35e76319e72c22ab8f" - "b40c1a90a4ea97a71df7f95e6f35ad78bd3638e77c14623897f4525085da241e4ba28bf56d8695080487a17f483c27b775686605f285a18b" - "5945cfc6d5eeab78890f8c6f4d2ff045bb69e98cf5df1f2ab3b199f6d3b17c470b3b7670b95c881e63b7c175e53d93de6a0e1530a7688bf7" - "e192f1790cb3192480e1ef30740b7a3de3f9c48be91df00dbf8e6d0f4368b9ff3b615a2ce101e09485d5f777138dac0e98bfd6df6290fcc8" - "3c7a9423f7528230807e7d15367e58a2989970ecba4d1dbc9eaf324a2fddc9a1d4e81632946016f062e7026bf995f0644e875c286425a9ef" - "7383ea1ba0230f58da1d37d6ab706f587392bcb7079a7b18251a60e61003d8f96a294cc90d15ae4510c155acaee644919ab87b520263d04b" - "c661cc554ce6cb1746ff7820ac2ba6c48f469c4aee62f71d345d60db57bf4c62e94ef6c2d821101c7a6ceeda060c1d09121f282a5e8bfc5f" - "b7d473119eddcc7707afc17e012a551e7535b9e55e0b68c7bdb0dad7fbeb9ec5392cabd684b32f667cf4374731aceba3a3f0f52d1bf27a6e" - "30aa995daa58c8dd2754a3d8c365cd7ead0071c7334b2ea33e2c7449ce163771ef55cc78c718afd01907121c09a80145df03fda690429a97" - "5b438d321e5d81f38c6d17473878e6d0556596903b4ac4d6bd54b26d96fe476879bd9e857d88244ff7d7008ccaf1660314b17dd59343452b" - "c55651ff49538c6e63577250cafcb0b1e7957e85b39e3c2286e8de9da442fd73bd0031b7421ebe3dba8ba282adae227ef69310975a9cfa69" - "f7a53139de8b7472b4295f4fd43a021a0a40f9675abc8ac669ee6739d102c5c8453e432198980b512d78ffbadef5704033d18a2c269e0fce" - "94584bd08ddce5a28b5d4e3c67d2e665af92902de88d29f6ea9ae78e2597a2473de14c26af94a66d6ee248e1313018ea3333eb7fa34cd53c" - "80d6a2836592a944c1dc6317856f440283dee92dc973bb6d9adb68cbe4af46469e0df7409e298367f7e654e32a7a465b6848b953847104db" - "494aa85a38881a00d09b73ff303165f7433946529b2a910fb9ff96b1ebbfcee2f146457e62dda5fb76f6e57f672759eff30c4d281c474419" - "d3e4ccf9b55a1be3334fc222d624f3b24eb060f243430878dab39242abdb266d5a4ebd68225df80f315a9e150ad0a7269f2a50317b735188" - "aa4390e86b54666b84f2595770e098529652bb94a2e764e62915192dbef1979df378012c712277135bd109f7feb09e78b6eb5e62bad5eabe" - "20b9aeaa803c5467f8df094977029ddf728e0ef7ba48bc4cd3f07a3f403482cb017e8a173beb6997d514a2497d44057e80042e80042e4500" - "0578b514400040066c0e0a2d00010a2d0003145196440dc7ceedf4439577801001fd9f6d00000101080a7e069e553740bf28dc35f1a69967" - "9819b7ee5335818b7d4b0fa7b1c575226f757fc87ff49f3f49f609c30295377c1c76f7b452bd33f85363439c89329ed4e2b93f8d7b219b0a" - "1707f316c5913004c8a02906a257ed767eceb3c4deaa6ed49885efe7734100de2672524f83dfc12bd5d38681c68ac6dd2667e418744e5086" - "853c03a2ecbf71bc0a6fc18d6afc4147444191ce93b5eeb933c862f61873ab7f5fc52e7e7d0ff61331bed29162f16406a58b99a7fc75e7d8" - "9597d8c8453f1ae3cf38fddca569c365b921138d314c4010cd0cb245af7e89cc3199bd1cc2addf8fd19f1249041ca066c62d883477079628" - "c225a1e149e59fa9db488b3922841b3cade0f49804bb006170890bc736fee839b995bff2238003182fe3d2146188f0615491a563b4780687" - "651c2180338e7e48bb7e5bd066ce0f323b6efb141b31402b65b5cfe3b8262a19766808c48084a8fb9a0cc1b788dee51503823340359cfdc6" - "a01716ff5b08c39e8afa707aed1831fbe2f374e91064ed1f5c8fc0967cd0c1639d544c06dfa42cd5e9d28d15a1c9d09e84f1a1cc66452bff" - "5ece41874d84a9a9f36885453657cd5227a0c1f6521e6b981a0c5bb2d8dec353ba8845683fbc37451b324568d4b9528fdb3a0450de5c558c" - "a0e7c4d1de5efe0d5c84fee37989a8778cc01406966e9f0c59afdb3f945a8d4bc0a3eaa22b863385870e69b2809ff3642afced72d86d8aae" - "dab8620724b083ed63d900db5f7f248bd79303dbda488135a9f0dcce26407037d5b598c5a49ca676eb34bfd14ba885ecd05353fd29a632cc" - "cb444e9f5dbf5c16c91f9b16f9c79080391e6dd17320129c8ff2c5cf39f9228361cb78ee8d6c3c43dd45019cd7834e38dee9732ad7e7d602" - "7e736639c854861be96051ec86d17888aebc638b08fe3143cac241a968a6a40c510fba5640339664de8bbd24a92e60b11c55911e469a8cfc" - "1bdd462a77b7eb0a6ae71907e26cd0d991618e15a30d82719cef13d7897b375de55c9c8741441747947c4d7d905b442cf703d8c14510aa50" - "472491fce4b4629509c01c58bd5006507f695382cef4f2d16c2f50b5f7a2b0e2968a5971847cc931a3884e65017f6adfb258a59b0265f84d" - "ea32d213cd181937d9d79d71973dae66e4dea5694ac5d421f52b29fe4f54eac644cd25d046bb3c8039abd312f28e2b7f09d6fa1a68cc6084" - "90ec19da53f65dfae5ad54e1226b6dcbd78919f972f2c5c653d15ea5879e54abd1d18972c31ff83b5629170b50ef8e0e662e398c1185b9f6" - "6cd2a72af6a44d33b985b3f67e848338c70ae699f32537aae27f3bfe3f0d8550578bd2881dc00f9c9d6d5575ff9284435a19e8b6d0138e3c" - "4e235b18e33359417b00a1fb8bb04b043097cdbe187837260a12caae17c8b2b9a2c951ca09fe1bd5ca8ce4870e3115c4823ec44d53a797ed" - "2aa4f04ac5953086fe551ccdbccfba09ae766f48dbcd13ec761328fe8b2d7955963c39e4b688f32488e6a605ba51f1d347127b306c6c34ec" - "964a77dcf0c36ec3f06dc52b7da8ab0b8a160d3c176190ac2c3f92af1aa8159d444eed22145b6f6067ee1e5d5eb6426539f77f39a8c886c4" - "9aabfa61bbf74988fcccf0f674e5f01b8722ce5de68504738c14dbb9d665b37c2c82e1e8e545f8d7074eb79f393016373886efc511894097" - "d9e42b88c537e57d207f4dc2d05ac46ea1a2abcf52630eafc46b0f8fc435493d5f66f58da3c153e46843c8204bbd771b96152b429d5e5814" - "8d52c18780821db99fe87abe650efcd8b5f3a284e3648e77ef52ad0a300abb956875d1cb42b944a3809c5833fd21a70afe5c251e0e464dd3" - "ffe7216740f256d331b2f9645063e4e17a79a3905052b5976614de5ccfda74920e0cc6cb4ee97d6b57a82d82e5d79b0cd648df5facc24405" - "7ec0042f80042f45000578b515400040066c0d0a2d00010a2d0003145196440dc7d431f4439577801801fddddf00000101080a7e069e5537" - "40bf28773e58cd4c604bab0547fa1511fc25d10833c651fb906bc9d2af420e0b82cddcb9f9ddb47d76ea9a4050506f3cded63b9cde803c44" - "b17c769d6aa94a011bffc68e6dadc0df9fb3e49f6a77ef6198893f9324ff4b7c90734c428cc91d9e2ee38cf32cc71da968c97e3beeb9b047" - "3e1d39365e137cafedbe5c417b85c252741e58aea7463137bfbe80a25d34c769124d3be28304e95726ed9f7716cc027ccc5b56e0f4d308c3" - "685c2581a8edc263a3cb1b52494d4bf2c545759e704451801d00950fac8ec443195907658ea50720794b2282ca51a6bf020e595d74964757" - "e4b49a987d34fb2aef95cc2eaa241eaaff9eb6613f36d9e009bd834b10b4731961b49085b10e0928c8c5423ff00b677a9beed54820df5f4f" - "ab8cd6f11db0c24ef400472cbb9ee9ff53108d082732b3b11b6e468d90e2f36d231e98b8e9a43008d87a116a92c208ee8797dba79d403fdd" - "96f9490ffbc75f3e76ddad77b016da732a01841d401249d13aa16a81c63ff985abbb442b464c111fbddae09389380123aad6df536233e8b4" - "14b0159a6ca20ae0e720ff63b116a2405aa10990d801defa897cc96634efee38b8af62180e96b8be83d136a0877bde9051b1a2bdbb8f7512" - "431754e8b2fb54dcbab5916d92db3b149b052b3388c29298868076c0ea714251df5f0a5f6258b96be3844727542b713a890a885d76481d94" - "c2a7eb6a108299e841ce44aaf369607c76d7f87dd599b1152258be3ffa81a32e5f7143be792293976c0978487737b61f0a7596eee699f636" - "3670aba405453b24b3cff4611f4072f2a67598ac40c2c1b592371191f772b6552f67541ea53d695c39d26fe33693b45bd80721cd1f8eb9b5" - "1e4336cdfa5e0d2f9709d5dbe26233b9902c21c8f653e4e41c88da6096e19a153157cec7dbe0540d1ee0aa32099447fc211cd579cd60daec" - "9630072bee47cacb4e9115675f07716b422ce4e74399ef05178ca292acc0be45b5ab15707b5f97aa87e79652dc333f22673bbf632da1ad22" - "70a498c38569dfb64582a61e7182c26ac8e2c57c43b5cbbb07693d04e935d78427c0aa8b069da1375e376992b59688abd651603850bad0da" - "c6663ac5cee8c3209cd76148e957a50558ae2c3b6b8572a41c8c7ac2f8ce9114a857463ba914691ad8bf5fe38292a42e56f204ee585a3f65" - "1b4c0567b7774b71d732e1d8068ed69a4e674ef793c3cab6a551f889ed9b3006d34895c7a6401402d1acc1aa7270586e373c5e7a82a30efe" - "17f962ba26bb821cc5befe9c6a91987aad31f61e8f6087ff0d34be5e2573c8b4bf0f1e1fd2faae733ba6c50ef6aa4931211c2faa87279137" - "fb2b53a143ed8c41df2a79d4e67a16eb05172c36f779b9770a0944214a3d1986d47d3f3ebc072a00924295d1401a2df1dcf2a4b99b4b9691" - "38790f5e03576f1d8682ab69096b2e6af2aacd6f124da1e1690cb68fec8f0eb794bd623137422d012e7757bc0d7f9e673878265297341128" - "297df3bb2d0cb1165b2d793e8b002a956082734559c148cffca5ec7662d2b1f374a3a2278eae010f24d1464e66bf7722b4537253ba19798e" - "65d2e2e502560a50cce26265a8e87f27548cd0d19930d6d9ab0dabab2a63841de03c2654481183d4475ce01b9a67d4329bb0dd3220888132" - "deb6d5beb27a8986fdeb444f1158225187f4eb5ddb894b95e509842d7a510ea55e7833c6fb0d1ecb8436963ff9fd07c48faf5c99c3de9079" - "0e6ea6049d565c5837075c4f6a5c37f1c7f7a8803d42c413af43131a17e175a57e66cd143fcfa407c5db50d4248694fbccc83c3fb45c9601" - "dea656fab0226c9a988cdd1069e2baee4c8e894d42fe12ab4846e35ae480d260764e2cdf3107d3fc6f7da4acfbd1d6d5381492b9a33c5d61" - "923c0e4b69619e44057e80043080043045000578b516400040066c0c0a2d00010a2d0003145196440dc7d975f4439577801001fd98a80000" - "0101080a7e069e553740bf288238ea06a46c72ab564d017a4c08b91a1c4f7a782dd91f80607c02d2745e0180b44aa30a0cc671f18ca51859" - "d4e11557f879d1b29af721040f0c9cacc8ded1c50eb997aea391a11f8c0276e33ad606de7662923a5b01cf49a20dff8cf3023fd88c1fbe69" - "269b744a9a8e20a347236344073b14ad914f9f4221b52777f76dcbbd43cbb5a82df44d559c74e451bd2c24cadfc0b7e833204f1315403aef" - "62a304f82304b84f77de4d48ce50b1520e5b786adafe9fc6eaeca13d9ef1a2933849cb9c56db14ba136a7c54e96acf37a5d9fd112f42904a" - "091a46fa40f32f913c1d0b2b83159236bdc0a9508033c8f8b35064c23b9156813eec78cedeb9688aeef00ab2e3449686fc4d58774515d2a0" - "12b2ac19019388e6bc1877711cba435958debc8dff210c17aab93292fe5c67b4fef6a13675781b82f70abc98b8de762d33d4cb938cc88f42" - "68758a69f6e113f565422e20f20f29d643c55d099d8337178c133b8a31c3f36ea232e30c98482651daf548522aa4b1df78de56725e43752c" - "09322e5a4de6fb245d9c08435e6ebc471f2bb5512e36811e8891992fb3e27207dda17c249aa9bcb01e3554eebf5f63e7b148610bf5a238b0" - "df2922e5df520187d3f7e8a0b976aa31a084a248325ee61702a22d2ab5fa57e4b027ccbbeb505248676a774685ebe7831c0ebb3594dd7f12" - "9dd0821fb94ca13f252d988a0e0914001d2cb00e8e9a23c9fcc1a66662ff752f409d4cfcb0c8dfd2793e8e0bd5f172438de45c1b13ca1d83" - "58d1b903282de3fa4ad9b7011fda8ce8ece304794bd97359c4ce19bba977a6eea89049d86c7c1cc06228a715b23a748cf81696d4669bddab" - "40abb4c417e7c5c4c5a1e5c1efc702dc12b5d253aa044d8329843363dd81dd26731fb669687e54fbbfd8016c21265c775da0780fcebfcc0b" - "c5e7a0e48c8c6d0713588755400a59effc61fd36348b3f00a71e31f64aeccf200482ddc39ec00f4a4f034a9b8b339a315e9561369a581fd0" - "b44bf858af9fa68b1622416b5415496e4e26b63548d8e69802c2d8622f270a25b266baca0d332c4b9846cb4a39b2effdc8fb9c75e2c26c7b" - "e6e7c23fd331021c55ef77c4a5afe768fad65af7751b8c30c725a087b19bac74009741d3d07680dca2623d8517aabc55af6ee9954823c29a" - "56c0131d910b3473f267b89b04f029b06b1276624ff339d956db6780a3929a607fbbee6f3c353b8ed10ad2dfdf16b2abc0c015b2eb2fd002" - "d565e0630bdebca945be1607be386f57f8f35b39c6e46a23c1879fc1d932c23e09999ae50025b9f35659fe17d931e6800eda82cfe6f9b8c0" - "3c96f5183b07be34261c4e7f7795e6d3d52b42dd7f3c954e326781fde18b88d06cd518c31eb1164d7decef7a5cca3464599f10c733b92286" - "fae8ad78f077100924ea39dc4e631d4ff7876d66c714f03904d1ae49e1e29b72b9a9d15ed838b1278311b22433e1c0c3186f6f7e23ac91cc" - "49ff91a4ad6db8e67e9947d47e9b9ec3905e524dedfac7227b99b3376361b5991ef1f74341bb4e6de8bca540efb3897dab85e4cbd0c0ed99" - "fbe68167e476153476614a58658643eef6eb970be282248e88b3375457754a790c2ec5acc5fa42b63e664541f9f8a9d3d85fadd01c713bdd" - "064d53fe3dd83eea70c7a7073a3326088dd58bb28ae4496a02b209f300ab19510219c602e2cddc3e4e3b2b7eeb8bc872661ed78cb1b36e90" - "230e0a0b93de2680a59d3fe4075c13a44b68c84dc6c8b63cad59a7819600ce828e88ac467458ed9ea7cb2d712a3859255f9faaf011780ffd" - "80b18ddc712495b7dabc84d98ecf76e43574bcd7eb8b80d951c8ee63cd4ed27f900b245ceb5eb9bbc7b6e3fc984820ca6eadc2a18732d827" - "5f66fae800287e4333936c59f43386bd44057e80043180043145000578b517400040066c0b0a2d00010a2d0003145196440dc7deb9f44395" - "77801801fd139000000101080a7e069e553740bf284e062a28de595a65d6cbacecc766fe4da6eaecda5f19fb71b08c53240c4f66a2b8e124" - "18ce5030863e900d3c369b2f171d17e4548ce59d15bf298b96d18e2ff2d6383b7d72f682ba95b1c1aa12227a57322cd6f9d95cd03c43ff00" - "1651aac8f236385de69a8caf1618168d550cd605a9852c20fc7a2a4e01d5c0903397fa0cab0c54b3834b5b24eb2f6a48ecd0a37615e8de54" - "eb924e238f926becca30b06dc6756cb44187a39f3695f81f6134afe50a194e5b1bcafc9cbd9ea619e4f5b709e8e0470e649a019972bd9db4" - "2569a911f63a050c6fcd715c727cc1591a16abb4fa804e2fdc89705ea39ff8c65eee96e15b69eab6642e19072ce73e75a36fc476715861dd" - "c4dd435f109edbf7ed051216d9e6afbc6308d71c2e7b5687254370d2555380d2843d06c9bb8fade9a68ed47072255564b2e90dda2930523e" - "043c632967d19ece8cc2b833f7aa9de09916e077a1a491b51c532839646928fdef67f9ec2e9363477342879fb839d0cfc979d6c71fd20a84" - "a60e6fddd590cac6ad17a06f9ac4bd4c6f3398c9bd0eb145e62639aea66dcb9003fd9a434731d0545e84c8920f318e709ac4d62988696a30" - "0e1e840bd7cc19f0e9e12212015d9958354c5d26edff1ee5e6e9b04d3ca5defbd364c70903ae6d20fa5d5ddfdf681119666f5aa1d9edceb9" - "17266dd31c749ef137ccbbdaa0381796532257c331067ed4b69f8fb3032ebf763f108706fc24839a295de1133a9d73de5bd0646eef0de195" - "5850a21117d46ea4d19066c9c9af7864715f2215d2ec5df7ed660de71e18909812be6a144d6fbd760cd5f90373aa010ade8b6074d9051266" - "81c9f5b1b8d9ae697c6db8e859b5d1c2250046e767fe19033bcfd55f18ad89ff18e19f41dd3917aec403a2518fd4bd19a7eef3afec0e32ac" - "37d19ee0435622bc95cb776988c4381196773cf6f26cbd2eb26e23db6929cd92c6c022d977c1f7c27e69853fb7b11b5e46ba4cf5b21e262b" - "4466844f61ad5fdda90d774b00d1d00d29ff4547e4f7d5b118ef0dc5236fe2de42a8639b9fa0b76af01e50b28eb671de500ad648264dc1b6" - "a050dbe2dbc03a69f49c0f96af37f142376c1062cf345fa41079d82fce0d3576cafda35a57240afbac64ab7ef4c1bfab2af7e987e84e4c51" - "7a1ca4063b04fd64ed2415bf7c57f29c55bc0cbd605844c78eef9187ee14213e05d370778108a5c15eda9faee4625f6d7565e7dc8851c248" - "a29964b166192945d1c9f5b929766025deb1ada77ceae4589c112e086789af32d5e27e3e3aba24d6104b692505dfab07157afc7bc5384749" - "10aebaa0ff429c831be046e383a019af8734ed4b81df2ffdce964fc4b81fe803e0215eae8c09c16baef7ba920230360ddddb4903a507ff47" - "568618de4202430872ae4384d9651c8df81f2515c2527b12eb77f9780139fb996b35ac405d0371f236ecd9ac77b666d9e6211f9a616d028d" - "0a4f85d8bc4436ccedcaf19a0f3013451c9c8f982277ab87c832cc9966a49e1904778491982697cda2eb9288dca570b61abcb01e6664cc0a" - "b6716e316aacbf7a249595a65259b9b90dd594c4e197ece10922810f345ad0c3d3f87ba745ac0a1015fbc0182d58362b2247809d38b18c4d" - "2b49abbf3eb45eb09b3be4b395bb62c1962560efebbe3125fb9d7f3d38ef442ad8d61589613c38143f427e97caa32941706a28d426a29365" - "f9957b989d87b37616a6d7f74323a61a09690527c88f882ab0c56c7dfcd439556d4b527df3bde157c527a93d577f8a5142d0e2716754c2c4" - "f12f6abfa12b842aeb7fdaeae54badc9174c9d47a93b5c25795cab8af2576a9104fc5798f0bbf07de22ee880ddbb436be765ee406efec7e5" - "a962ca57474919cd184cad8e8a23b86a951ea8b41e574ea7aa44057e80043280043245000578b518400040066c0a0a2d00010a2d00031451" - "96440dc7e3fdf4439577801001fdfebb00000101080a7e069e553740bf29315ecf5e7912acb807ca5a84480c7fe9889dc1978e13087db5bd" - "1981f81cf8f526d6ffcdf51ec8abe550b07b9d025d0b7dadb45c52f05d1c8f48e676b3cd3454262a33eed82f7adf6c23b7bfe4cd414917b6" - "0fbb9ebe2dc8d298f00ba9e7bb4eec362e11d3ec0ceaea534a5c8d764b3ce01f4feb89517c1c042e599ec27cc6670fe68638b1c85d79cf3b" - "53d9560f14a8ee0523f225e6b3af104319412b9efac207207bfcc3317262f5fe1e57c0b20cdc189db4f90db708a607aaf0ddff5b1bb86800" - "4fa3d848a4b326f1ebc1a40ae7c49a74d3b7358b3125b5efae207abe28fb3fd79108d57476f74c301d7bf552ec487b2fa6f1507ad845499d" - "8ac22902b6841845739f1b89b3ab32b031d3856f72f4a38561ca11c1948c1389f2e9378325b508d02fb1e9f9836bc0676dbde77c7b53fb6d" - "1d8d4eed4ff375d10e1682bea72265e3d00eae5846f61a25eb683e6f43d77c770257fb031a03c7d8302900c034b29265f59551c28d0a1463" - "b637aa52f31f726d86713f97d1da2452c3639c3ea4d6cf61ea4fdd81344e40a840db04c3e72a47fe414cb00eff11a7083088903d0e9b7aa9" - "7deb0374d154098c803371c4a3aaae04e1f62bc2a6ca2e9aab0fa5e641242bfc6692931b90512f384756cd4be36498cf63d043b22c624257" - "05742f1fce701c8ed5743de0b7caea12a8224f783b65301abf613dba6daa4b378f113105d762cdf2ff2d1195317ecf1ece0b4940cebbfea2" - "e577be41143c9b7cce4f0b450bf30f180d472da121d7c12535fddde0316da7233f6cee0857e455cb2d91740b5b458fbb07cb44f9ae62bc2d" - "c0bc33cbd9d45a0cfacde2a762485c9a1d998d9e222d8f307ac8e1448f85ed66ec6459e17970ad04b6d7221a7fe7a33e996367097bd9710c" - "0f2d857f06267ee17e170674bdd4298213c43ebc55e6c1440cd9b9d9145c4122aad64e401d41968aca9885139e78e56a44b5f9a197c93f5c" - "45c618dc56640a8af88bfd7123a6f43b00ff1f8b3dccef88fb86a0ba0ae10991115b6487b27bba7782a78256b76beff580fee0023a3fdf9c" - "a9afbd639d5ad2279d9ac1840425c4b15fd167ceec431dd8fa06e0ab7158203169c459e1a3853432dc6b9838d5416fdcc23508a70b8f63f5" - "2eab63e5749fc089bcc54dc378838650b618945550b2dbaa058783a8f6c6707d64a8b76b4f556e6b8e6d03a7adcbe25eb3ba01dd21ae60af" - "313946963028798afc4b3ef69f0fc93a75e9715dce0550916413c78c7ffa744911d04a58a2367450b8b29680d9c75534e6e18ae523030881" - "8b483f20e2d5469c6d275d73cd09c1674c188c6c39208f4e787e93350637299c68c6918326752030de952c79b8dcf6996de74d81a2a29d98" - "ccf0c6585fc1abd1157505521c151e6c54809a6b809ab76c150d395b641dfffcbb389b68c32eb1144f4f95b4703124aba8837baa41ef08cf" - "f415154201857ca7bfe6f62418f78f76b51aec06d90ab8b8f677aebedb7a453e5d79b514a6875162368f065960e023d498b436b5a3a8ddd1" - "42f907b0453dcfdaec7aa6862c9245fb47e19a9a20246da583b7f58fb4b2dfc8e3bd826563ef3e06125f2395f0a6fa6f16507c67a70d5508" - "7824aa0cd1364b3dd7e7e8913c6d37e5113356b4b13fe0a1b3e842b85db90e28247159a150a20c7969eb7dbf3f389fc33c6c48ddbfadbd37" - "7d5ac7b557e37d880bcfb486c1bc3c9b69b5977afa1f5c6806178dadf751edddf044f8b636b1411a70a0ed53486414c181e9186655c0f41a" - "19b20f170a7ddf8b63f4c58f35c3296074ce50380cf7fcc192858ce157c8913b24c9cc0a1baaef3821f0c3342bb12ae2e7e2a3da486797aa" - "17bbe3e13b97c261bc0f852a418a0a388262ca5f986e462ed4d26f1e5dc0cefe8cb744057e80043380043345000578b519400040066c090a" - "2d00010a2d0003145196440dc7e941f4439577801801fd175900000101080a7e069e553740bf295637fa0a13dac10c147eca8c0f11b2f4cf" - "011a7d538e331861b680ce8a37cdbc13efbdddd57cbfa9d02947e481f91f6fb2b3217c783f94718027958baaaa12ff58a1c3ae6fcefc7a2f" - "c99016206b1600a49713968e7e827332b286fcbd8c90cf2c69fab1e3b1445f4be8ab4938caeaca406f57ff6c86cae1d7b671accccbe0c1bc" - "3a2790df221d462772b17c237ff509ce956f52c94b41acedac45940b7541d2c7e92e2e9e96614078d5e0345b186db21627b9963d0c948159" - "8c51e4f157245d3cf8952ddf9efb5569e588883ac6159648c6cfe9af3ceb6d407734053b58f54a2c598f7c081c98b1ecc0c5dbb4b75bc78d" - "14e2c2dc2eda9e035b8f5a944d764f86169dca70a071aa665a60feb44a845302ba63132b1740fcb02d99846050313ddfd091917978fc7d54" - "cebd431e759a97b59f72bb0e8696b3684da6515bdef236d818b781f75285a3969c54bc4ab89df0ece93f7defdb883a01cf3d3f7abf54173f" - "e3c66dc2d3ee83a476586dbb1e2091ca678f01710a25cb4a33ba9497bd3b12d0e7edf793b3837980f7c828f50a7fc6afa8f8a13b6f8eb785" - "f7bd81dabea4d64314579900ee18cdd08d54bd5d21bd3a35be7992a8664a049a99e3a01514a7e90f3caed201176588fbe68e6867fea2f118" - "e0011c5eafd29b951939f22edcd6387b15685697a87bb9a044eea21585c26edc5518b1d7d861469a26534686a323aa0a7bce436191061987" - "ae951a902c59a000c06922f01184e91a6fcb6d187f446904ac84e04d095cd022e94cf6c2a3497c4714cd48f2591b368cdbdd52c74d50ff66" - "4a67fca3ff19459da417fffe61abc001edab8e6355cd1a133984afc1643f22eb7883ba9e1a1a5fbc98b9834ac0eebdcc8308dbefe85d77fa" - "4c8c224c7a549b7e555eb2aa5c2b7cd4ca3f012475ce57e6539e0c7f1f763980763910444dc8fd59d97e56c48e85e22ffe77c52cccb33592" - "ecab6e706bb29639ea20e7ccb6e809988c60ce73528b474befbdf52ab5e3d02d9b252b5c421b6805c69451c9436551ead2e37790b721c861" - "ececcf603d03546afe82e0f5d9b1e3595c2efba648235adcdaa951d1cb154ac0353285c71653754eb5564a476a2ef431ba295c0ee35c9ab6" - "7719934316dcd48a5e6402e6c856ddc450a94133f4aa8263cdeaa3b98dffa9482f2f0b82d0857eb4861c6ecb7af37d7d6906d51e8da9de81" - "c818d0b74aa7ed8ef40fd86d7637b7f6f07359649b261c4feef1d8c762bd0ec36ee2d0f015f5c23147e138e370d6776c08fe4558f462053d" - "dc66486cbf4a461de4b6d3f48de1bd5adcaf73e5d6e7d7029a45e1bb72c97701dcfba4a552a8e8db7a5607f27a95b321c4d28759174722cd" - "69103b4e2b2dec512e928e95b6a226c7ebb678a88acdbc6b12ccfc05edc553e019824699c3cd50e86599859a451d0b4b8d5dcddf7990f1bd" - "40fe7868f01dbe7f622be7cc00544e7c8f2f043f1abd8d54d349a2ae831df5dcd3d1c8d71980fec3fe0757ec150ab46c591534224bf9e81d" - "af6596f30ea927d15106d427ac011a94a9639adc1ce9640f228166fd9805a9bc26ceafe48df9b0162c61e08a94eb26457f7cbd1af6b95b7a" - "fc589e6a75c596fae5c697dbb2273bfc6ba93301a670e4f4150c59eb8fe03751d2fdb0e39bd39a895b442bf938236b1e470874c2380ad708" - "b292b80d231a78daedbdd1cd7ce5ac7dff62ba755e1f8e40a6486729defdbed283d4eab803854d460bf2800220b54105105d016d6f8c3711" - "6beac8e77c0337e751a6ec9d4027f5d82dbcef9455ba5a04d3e7dda839284256ad84d293e3e4d3144dcdc00fe9ba3ceb7dbda904d89659e2" - "2358d3cf158f29f932836d80c77ee7a8119754e420421614529e7a844ac51d061c517c09e1b93d832456a744057e80043480043445000578" - "b51a400040066c080a2d00010a2d0003145196440dc7ee85f4439577801001fd2ff100000101080a7e069e553740bf2999d3885c63243399" - "7b96e9cecfb5b2a1af1d509ca2c655d074fc1db853a0b48ce673bb7a39e55e853cad990babfa3f9a4a105032985dcf59f6abb471f671b428" - "4cc2a0d05f2bfb58b711288863a4c30fcb9407d0550066131704d03e94779531d360ff07bf376431f831a5fd3510d75ea6f96f365a7bdd59" - "41e9c823990cc8176976004fabac63e21b8b490ee9942fdd01031f73c33674d308c59abb6e364f8c24fbb01a6dcaea822119493da0572429" - "6ecf7eaccbdd916840ffecafebf9294190246ef775447d9717af411c683aed30dd00618836e323395d6841b1e331f8b1ca6acd3ceea33d74" - "79e52c7aec982375fd5a2196822f1af4237c91a850e248944f40acec78f58292dd9a5c7f2819af99156c6b612795cfdf92315a355de51cf0" - "2c1e4d26c36281b06e61c0e188d1814d35827d4268bf0c3cff9fb1b48c6e9e1746acfdc2662d97cef8a2de62e85613989c8ef817b79c8f93" - "b6836f57ce9ee2cb7391c416e026a55690ce5a163ef5e8472ba59f87f00f739f8d2629a60fa28c363594173fb4ddc569b8d13c059cf7fcf6" - "b6dc017db70384cbcdf5695f13f50b4d6d6a353b9fb0dfd25dea4dfa4f5d3b189ffbcbac49d8c8bd94112c065c22b03cc0c4ca8f50cc399a" - "51a00d0c76dd13a7540615caf5885bbc78e17aa9e86363a646634a5bcde41471ba9d30792e01a4a5af1e186bb011af5649dcdb59b44f3c67" - "cbd76f1d08f63d3f51a7f17047d16235decc310cefac66efc5d3757d17016262167a16fe3a0d92c2866a3233cc3e108c493f971279a3392c" - "aef8e5a50b04b6da0fbfc2c867de1292c94618675ace3c9c1e766b0367530217ad10f7a9954b65726f77ef4e3ead66b793c865cd87160c0d" - "f27a4fead65979f49b1cd972ebbead93ccce5e523de4073229a72543c2aac4ab0e5c5ec2efa60be82c8f520e1e4eb0e1753622c1eb0466bc" - "47fdbd33a02c6ff4169a54ffd04dcca7d274d949339afd58b75ca608aa450de2001f7c6ffc5200346d4bdb52ad88a4b096c9e73172c9bb85" - "8f7b427d56da11fbcc045cb9756fdcd19740d815d5975c53045cad2e9ccf373a613b06fa778db3ae98af38fdcc46b8dda3deeaf63a1699a2" - "4882e6d5f2174ad4b5c45d7846e300524657346e0cc6f888b88d540355e945cd37709016b33fe39cf0be40f1117dc4e60007c2597fa4b539" - "d866af371cab103da7103919ab79f5d1c6bcae2ce8677864bbe7569cb2b64320dc75d3fd15ffdd289575d6bf8276c90d5c12d4e335f3777e" - "e6094a019330a1f158ea1575af3923048f74faf56ab26475ceca937a380d5eb00b00fa4128f0a10320ff19ecf66869f7d8b1d1b15f0d9d84" - "469208ff490d855691046e2d07d6ebe116de9f33e389ca584f80274ee23be41a07425aa15b79c3e1178a78811a445f45cbd4f418481a9257" - "8d1a1d5bcf0f6e0f6cd48a49717afe12cc22ddc6052bd63be67093064fb8bc41c88f7df1e43b68d88da7495c41e2b19a2e32b15be4df1aa0" - "d35dfd9bf923d2b37ce9b9fac2512a4b0b4fba97af256ff0e6240298da8442057fe5fcf42e6eb6d4eea7dfc39cec0f2cae9d215bfcae7569" - "3b3e9a993f63a2bded024126688175bbfce19c886408dd4938064c64c7cd360fc7fbcb6a332de83ea9b2f5e9d07a8da140ab3ddee20ce2d8" - "a14180e33e60ce39176a20f1aa6801782859e3e7130d5332d6e43b55044476259960fe5505612d32b8374746986013849615cf99f497a4a0" - "4e44989b6020ec51e1c2092a30ae77f98501c37b7e43c10c9a314fe0035cf64ef7c5d5c4c1520d1a6bc81eee9023f5d9e351f03889b1ba8b" - "f14b804824020c1e7a9f844af9407aede9944181b7eacaf92a8045bd4d7e2650e13c261db7122829b8ed2597ee16fbeea884c71044057e80" - "043580043545000578b51b400040066c070a2d00010a2d0003145196440dc7f3c9f4439577801801fd4c2600000101080a7e069e553740bf" - "2994b610a7ceb3f6ce5d8a410fadf739699f62207c9e3ec403de9bd13d8029b12cc03fb90cc1be5d50c5f7ac63c5a654b66fbba0626d38ae" - "3b31db9ad0dc4e1ed2286ba98bb10e25b18b9f75099b0e0674bb2e45c8beea180b8d81cd96d77d1794b4d78fb9a65ed0a95126883befe779" - "0bafd4dddac0ee1228e95f588b741871d2c5613e6e4baa0f7abc34829b70f20e134b17492821564d9163f170bf5008a4de2708dba56f0c8b" - "8097cf50de6c7d9484ea19c6ee0f4fcd8af885e9b87a265f20276e405898908931c2a7997932fa06ea0fd3f40a88df5df19e1db69a17db1c" - "a8d0fd3eefbc3177a62c4a23b5b34525e61296ec0edc8be9c830dce8ab7f135bb3f4f1ebcee2a8f032a9fda5c3f622c999f54ff5fddd7a45" - "0255454c5fa3414fa27cf1783432eaa313016e8ddb0d638169277958342a596ae61ef1fedce40002ec048c4eec16e3f2e33d76289c63bca0" - "eb0c35dbca936e6b0d45605ec4df402e1e4265baa1edcb5b90ec35746a497bd5327011ecbb3df282bedd27d333b0fe7691b9a7d4d206482c" - "438af2dee0f26018af72c46b0b553d1ef5b10285811216ea0982fc6f5302033930d6b7937a169e7025dc5f4c3c7a27f076054371194b9d0a" - "c6c445bb65061138b8ef01bb3d410a56f0d679dd3ac100f8f3661d891a727a6b8125d1321f004d00ea859734c09aa0150f962c497d7725ac" - "54c271672721af2a4856d8643c7f472105f7b25c404f9ba2a441fbf8cbc4f2d0547d164c663b5e5f7b16259c711212d7c2158200d51eb719" - "352636cb4844c301c6f14f95d9a8906104a980dd0ef4a4ddc5e2abda6185bbeac2261294e046f7bafae165e0dbe65cd9235cd3760c78a147" - "0eaae69710981b8afc4941f8444faa459e736d89e96c7d686ce56ed03e89a097f317c2ae793f8234f7c1d5670697859c2c22f1dc652fe369" - "8bb45c86023c78190427ea96d00a601150b41d16ed02fdb97644e4019c352159fd4d6c42a2e33c4d9c368aa1414b597d2239cb74674de5d7" - "5e32e37d3e012dda4dbd31f04f144a7ec8b5547d94efa8d47527f39d9070ff5789d99d08959da1b90f94bed1174b76ab8d4658c4fcee307a" - "679993607df5c18cce02b1ce749ef51622a8e037c6173b80caa60ce664631094daf4a3a4ee6826b3e6d60a6fc3478813d2ddf12fafefacfa" - "2891dcfb0c0105759cb57f97c02caeb415227aecb9421b85c6c12735418f37a370f416a265197639ac0653095f43a0ac9e0bf29fa4aae50e" - "d4d5be57aceb0d684ca1e4160674a4330be11168eef9e6d8be6620694e2d9c4485328f05675c02fef95527df63214a31848f75ef8b96ea04" - "db77eeb9d06dc13d7066b444498d02f5daa8beb31c33f0f3193af1610194600527982e0d833491b04a87ca24e9fad38560d66fe1c7934711" - "88931676bdc17a09525a89d4faafeb86276c395c5a31ffc648ce1b0d0b172d01d5f2c003da53e7ce4773506fc9870a586c1d939a02aeb15f" - "b246e44a8e7f9ea9299fcbb61220d5957db2d1dadca8c95a42abb88e5081315514a5f5300edb51de0d6cbeaacee831c85cc47f3db65a1746" - "7867a059145dd92e23422017f4f8d79f702e9b6372613c968ad303e36732a44a6ea9308ef241debcbf5392eca71b167e5526bca2400a202b" - "2c63d6d497af01fe59ce18cc498ca7d83c84809fcc87152c95666c190476f1d5d31f32eee8509752bdf1629c1655b2ac9c6aa9bfd5b700b6" - "1bbfd2a560a6712d423143ed533b53a6f42033a681751939a0a4f5cfb170dc5a3f25974477e81d3a9b46e4c192f80d1dbf9281384d42c436" - "db9150ab76ddad58a0588545c97c9af3aff9b3ded4e393a8c1d20eeb6c10f47d85e22a708efa77e95996926e61a746daf517d15316237899" - "dcbad145ef44057e80043680043645000578b51c400040066c060a2d00010a2d0003145196440dc7f90df4439577801001fd5be800000101" - "080a7e069e553740bf29b669ec746dcd214a4efbe2f661b3e24ae00f2e049a61a70af5a280218beda8d935c681da89cf5b289f2f5f26e19a" - "67c33f03b2e92d11717c4045a1fb4eb0bda1d33c693b3b0b6e02f7c04d4e25e04c4bde98ec8c8c9ba5a96588169cbc4392eabc1d2774fdac" - "343f9eef267432aded780209644deed9034dc31de67dbb85278ffe26143847f5938aee38b95e34462339dda575e5d3f41a513a64b1db92ab" - "05f64624cfcdcbb9860b45a0205fe823080c08e356ebca1572d05a9dddcad42ce3cff44a9c30f54bb48d7cadf954d1e5f3b865ce2f80d7c4" - "bb944ab00fa52796b8ca20b1f7705983984eadbb217180eb1320b5b6907daa36c52df041fd899105e4e80ab0f14342083380c666830fcbd1" - "be96c4e45b3e97a574350985405aaab76aa60387b6ab55769d5577d0e7b9197361d356d2a0fd0ee8bc0e47c175a8a7a5d693ce2077f37396" - "2031dfe888feeb1fdecd4b9c102fef968f31bae23e8a875f37ac7190f39606f7081c7cb57eb1c1da914bbbfd17fcbb21a7c545389e02538a" - "88cff136edbd4230ab333269179df7dc9aaf1e58fd3d6fd0b147275816f3fc4f17bd8a24e5bca70e4fdb84fdfa08340de9fac62e8f60ab1d" - "157c64ec44252342f07d6fac74c9ecd2353b052907262adab67356bc907cd542bb82edd64667dd926f4cd7731ebe74d32fa7f5dc28222be1" - "5f5e228ddd9b362fef36e1a8e2b061b8b7392054f3d09c314e78b6092225b599f6e5582d669d2772317c1015dc980b01bd126b0366951f2d" - "5771d6752ffb359534c65e1e29a084fca38be8fe98d62ed173e57f5f4216f4bc7e3a87ac4d220d125acc6dccf074d162fee73d8318a1db7d" - "8f1483e2f5763f3f821fb1f1bbe8075975f4ca7d3b7b1f0226ec5cf928edb491a9a2f7b1272eeec67aefea080a85e47b9481fbe0648788b1" - "554fee8a2f2d6010bccede09f7d547523ec786a34a96c5742edd388b2a31867ce916a3605f2ef0dfe931a45dde384e7521dec6631197cc77" - "2f36ef88a8006504e5640d9521160bab97f133a8b0d64a7254e8e7aa96ab013aadc0d87c4293739af495064f938679961792302cb08944e1" - "153be15dacf495b4db8ce770f35ef7587c15f0593b580ef909bc65cc430a93bb1161c740fb284ead922f43ec86e187266778fee6c78482a4" - "e93185309461e1e7950154ba623d112e7e5f9985a5e259f46ef5b4d0515145e35b2d47d8de2b2e7a7507a28bffddda95e396c3418f3c18ca" - "35fc9e09db60222eba4bc736646919ce8b4e5f2d8be3c3cb0c435f93f9b26f4d4c68557814760b5959210c86794074a0b013caed322ff31b" - "5d920a23fd467ddf3aab09fde58e13066ec2e3e700feb19353a9d4ed59d02bdb55c1a65960199abd9604acd5757187dc4ecb6d6a1916825a" - "c36a452a1e7f67a6e558e40d8d3eb2ec22c51315e7253b1fae27947e8127ee03b849c5ee03a846f46988cb748470d4a48b1a470382f42bc3" - "5b0362862233a775d8f8ba716e2e5e20b6a35332e2c6c70df96f5359a050d4bde214c65c825a218720f840a3920e29de4f2fa2f47e3b5d45" - "a63b5e3a46c99fcfaff7e8a17b362874d9f10334f8eb0ae17a47f731ba0d251fb20afcc138233970f8bb470cb5accc4c14fd7a809c6c043a" - "6bb0c892d16cc0c12ac9410b627d911603e955eddcfb61483b6983980e400caceea156362aefa28b3aaf73089fa4a2451678d2aadfc27f98" - "e95816dd4eaf3267edef37a021ea037f277bd4a8a209cddbea616ff1d9217c098eeae145c56846519d6080a9ddf44cfbfea63114a5d8b2a9" - "48087291bc6e550b053c60baa7ff4775d2b1d79eb59e6775437c7ed2ad3e7b59a64d7d275ad52cf835f13d272a71ffbcac646b1f9ba40203" - "cf6c24161b942354647aed19c36a44057e80043780043745000578b51d400040066c050a2d00010a2d0003145196440dc7fe51f443957780" - "1801fde9cb00000101080a7e069e553740bf29d0903bcf10fef312552b5f2ba18cc8bd95f06b27a1050da328a01e6a3bc9366efadaadfe54" - "bdb446235f346e47114a890e99f43880754cf871a3243ac764541121e147e5d8d374a17b897fc12c591ae0a2bacd6f61ba3df6525a086404" - "04d6e4705adc09f3c79330619245baa95a2995870d1eecf71bf3167f98f9cc535e664dfa4d592dc8a394d5dd47ee3efba60132a01416a2d2" - "ec89613aaff587bd4a84eb55afbd6fd9aaa4a574318c4b2b3d896bea3277c7cc7a55ab40b468426aaf442feeec9a24ce34dd4cc287415431" - "91f32553c9cc38fed9f3c3ade6eac9e0c85d57d306718d491e802a34185e139c8623cb33cbfb0e6f29530517f644693904f2e5dc4bff5207" - "c927ef5ac8466737289d4d520225598f3fcdec49db5b5c85231b6dc8d8ad5f77dd4421714841240548aceb1ecfd7a69ecf61431359d35c56" - "62bacf6a5a4666dc49fbbd7d084b99914d93515837a4d4c369e16dce95aa1de43408de8d07c524a610362dd674a0b2e5c53dbf65c4334809" - "4a9d16b21bb4656200833f1cafa260bf299765b6a5f719459b7a694506b8a495599ddd02b89ac26ca0d480b388b9e4d3f82dbd5dfe0da5c3" - "90070f5cbb82349d265fec7324445493f9ba01cc18885f53dc24c9a9c29aa3b1630ee6bbb0f1d611cf5b6de0eb550a259e09c48962702f32" - "26fa1e2084bec1bfeef0c033259b5847ce51f355cd3685d1d67dcfeefba797e8a37e04bbd21e8cddae5842898fd91d005659f97872af03a1" - "daf963ab6e82a1b5f5360bc541693beb94e0bb3c1a3a8994fd615d17e1fad2143f81041ebfdbd281b6bf14501d199fbaab651cf23e677cef" - "89e042b5421a33973bb9d0b886052e48612c1459a743b1e45f055ff4a65a86ff263c6260a2effe8c47b0e4d40b07cb6eed915efb3b21eddc" - "530a1baef0647d41495e5bfdb9c543700d0425fd27f96fe1975ed87543d5856a55992c1ea3047df10b6df065a0e2c39e466154e1ed3ad1db" - "d56285f8aeda7901242999c59da9eaf80ca1d7cca0bf2eeef36e7ed111c6a46a6da46ab6847b9c5278ec801c426de417095790bed4678eb7" - "ff4344249ce5a3364ca32946a8a17a9776fd71975b91e13acf8f27850e0dfc782df50206f7cea1e6f9d637efeb95f5e37b1e1f46c8378e65" - "ee91d9a39a684ae3cf88d31fbdd83f48ae7a294647398a52a5d8e151b6343894af7e7a09cf4a32a6bd98adc6142262d299b40c9106e18e0f" - "e5a3d276d6fa337687376cdc12afda1144c90ce71d5741d30526841343b6adedbf5b4c19e9bf11e6f5f9bed2b6da1b32a678f844ba25fc0c" - "279320cf2a51f5c6d13b4d5c069b1175002b755a569fd150c27fc0ed1cd4e6a59ae256415a801971f03c2f687fa8334e1d24b3722be0a027" - "d4627a77b28a11296c6c23bbf3d5db9f04624d3782c45887263cc5478a2f77153d47d0eb4541d4ec2124d44909e4c416276e1970c4469567" - "247cc68b3b3093cffda619c56f3bf0598e3889187ba9c8ff63268de7fc45c6c31d74650a7af58d547a343a7e7cb9c7acb162260bb05c8202" - "296bc760c15ef48f8d2fbac536a3be3711841270ed1e5f8176571d8cbf57124f6705cce2846ec085f93484af36626a2f61f798d2f1437ff1" - "ad233890313233b9c834af67731c627df837f260f21d67b974568119e2510eb814db969f8b905a62c24a07b1c9752aef518c09de46ef2636" - "9869941b5469688918276d9686cbd60f04a3c6c3df17dccc9e7aae9dc3dafab21e4f0aa1afcb5a3e49ad710738a1287e1253c610a63520d3" - "0951f0e822ac899982c2c70afc45584e1f7b7e9d68d8dff0562799b37b8310cb3e51148b59cbc292508750d193c05ba8e400a429eb624d67" - "6ec6acb7d9a2ea1c08e13913aacb828db5c3f76b2ab92d44057e80043880043845000578b51e400040066c040a2d00010a2d000314519644" - "0dc80395f4439577801001fd873b00000101080a7e069e563740bf292dbe203d8f53f094dfc68355550b1f11402d76a3ada1f58bb9e3f321" - "27249ba3563b4218a802b63479346c63c1ac7eb1f4060d061154ff4d6011cb4bf1e88de1256e9e013b3732c70a1e98d176e6a4389f91eee4" - "afd6e1454aaf49689db0e5a05303d315df6d45473238c03ace96656748e3da6be979d88f731ba9574facc86e1aa1c2785e73b1f8c8f535b9" - "3bcd7ef83ef3347e21dd95c5eb5ff5eecab7805f2a40c18e6bf8ac4c59fb4beea4c913ead55b2c9136cf106781dac450775f5d99c12fe45e" - "76e6fb73dee9118b324cffb663a41168a38c9d6fc76326eda1d7d0b4483971cb86da593d3dc38f57c87901611596ee6e78ccd992a3cd9a69" - "b6330738015e9c4e655210fd59f217229ad25248f7d141366c13dd8ad3865493eec025889c759266ae86b06ab443b0e504016add5ee5096f" - "9fc575db6278cfae764de449e0b3a7c32bedcbe1ce936e02674178cfb2046956d124bae5e6f1681c314e66656bd6efe39009aa1868b8fb99" - "d97adc6d188cea51e6269b08ce65eaf37728d0c0d673537b557811036aaed6dc897c03c7e61008acbee5709a63f5221d9fc77e29eafbd0c4" - "e774a73cd4a4d045b15b2a0b0fc11ae858552fb2dc12164d85d26e7351d16696b198c8a8b6ee480d42923403c5ecc5a78819280c55ffe054" - "eac68df9a0d47e8245832eada447c2b45b82211743cc0134c9a055768f7784eff34b65d41634339e52f5276bbcf53c78e834808470b07552" - "b57eb74675cc378f55e822a72e24d44fb895913dbb333054fc62a07dff2824627e382e1bedba9857558d2b1590bbe05bd2a7ded769252662" - "c19149db70bc50bce8c69754648fc3aa853c3766b397d739c55ce8d0a9e11b6273685f3ed0eeaad7b10a85dc95e3bc970858a332c806454b" - "2911ded18b0021d4a7d003e4c32e32802615452c8cf19a4416403a3c91cf2facb429ab9a435d2c38321564f4f2487121857f0b86ca81997e" - "577042beb054b254882f961c8c0561ce554ddccc736c4771e7583c68423aee34664b8734223ebb1dd7dc1776262f99c4101323e0c16761f9" - "16951efae256a3abf7b6a1118d01a22ffe0236d8829266b171e181902d31afc3b1b9a4cb8d4a4378c47abba777541cec86694a862e330919" - "3ce69f502e7639d087d3241e3a244031519cf236fc8f7d67f0b4f31ac56594361d92af1c685561493b47b34edf54134489e4b3f3a0a27b3a" - "01afa4adf68efacf78c7beaf865d4a2e93b470bb78996dde7494d524cc8aac288627c83331d37ee1dcef7f79095e40f153ceddab746f57c6" - "069639e182f0e853862cc8e99affb814611e4820de8ec63e283d22ce8530b632e4f81646ba2d04edbcf4570c30c94855b6bfe4f3083598ca" - "255d6ad3f43e2fc3cae88fd1008542c4826b6c3d663d0679d04a9f82ad98143879cf4d490b6d539af84e65d60af3b4c2a9aea97007d64229" - "619627471e80d8053ddba4b902b7cb9c728d9cda71cff5f08dcfd5f8bbf200e382ec214f7645aaae2a0fc9242bae43a7ade4c54841df89aa" - "11ac36022f1a65d67b7ead52966f133b3a1f2adf9e2e03e4d0b79354a7b357529e583939f6216453991ff1a5b0f9989d7ecfc84a95d6473b" - "64ab34bf99d3036aec02b55e89d8f03fd47cc72f7092c276b3ae963ad777f4c9b54bd790d715554a9166af4be901bc126bb20bf3a5ea5843" - "d5733205333754dbd923f9e61e6c050443055c2387b76f12fb23342a9b51ede02f11c08465a564a639f2b271bb5b15c4cf2ca5f42e5283f6" - "aae798e106a8bb2fb06fd11ffc2bd49d3d6966a8e6ba68eaba4a63d2cbe6132b020aa4f66ae5676ca62ed4ee5adf00e9c30882ea2c849f60" - "e7e1f45e301a09663a25ea6ea54aabac87eb5d643209122427936e08d5beb4de44057e80043980043945000578b51f400040066c030a2d00" - "010a2d0003145196440dc808d9f4439577801801fddc6e00000101080a7e069e563740bf29929ec2f558974d56ad9ffabc9c298cf3a53ec1" - "3ad482bced1df977e5dbcfe03caa6d6a9df9b920cfbb99ad7b94949775089de9d0d465bf1276f185dcfaed8fbdaf1d8b185dc37941f808ac" - "10ad539dbe65b3dfc66209341a09400795aef32a18a3ad379e50925c7e95d24887dbf102b23a854f700aa97fe987a5bf802c29ebbd8ffebe" - "8e877f6c498560012bd49d274c1d88f9d3e9fc99259ecadc4c8649cfc9b5f9ba41aab69ef5fe02eca166284b7c52800d86d294aadb830be9" - "c68e17ab5a33700b913c6f2583eca229963ca22503ffb84f65a9ae4b371689fe053a48bab4dfc2d08ded64fe42a8ff167c67aded658d997c" - "d6e6cd612359b03e9640d4f6c5e677584fa5819f525e7d451b3f1eb86e6579eb0eb5c7ae0ae95091afe46ab8eaa66448fff0fbcb47f06557" - "38898019a0c2d4d13751cc751a8151351863cd9f8ada3bba626d81a21185642d5f29a25892180dd450968cbfb0e9da8091f1fb91376d1d57" - "85ed5fd2bb37980a464888475440abb0125da31d7944cd445130395c3c6d2e4bc4f06078e43e3e0f159860e08da0a17b10e317ac614a3a66" - "d23dd4504a0e1c5116d2828582bbcb8cf4e65b1fd517044bc4dcca56da14a82bba111c3175b69012ba5c54e4a97e2a0e6d56eaa273f7ac50" - "dfcfa37e3876001b32919f476c39076b07e2226c4b69e2a52df4089b6931c5dac0ec37b7ebf32bd6a635e46296a4c89b8696d69b5269cdfc" - "d17806f85482c987783485b7d8252d14957407803604315468a14642012f25b9c7a3ca5b84ed552671f496867ab02e06c74ec69376143b80" - "843afe236810de9dc103dd1d513b2d1e9ddf28ad19b1d11a39f5d2f929135303c44a523bd00ecd94e04e72229ce532b53174a3e0a09e81c5" - "ccb9466bba528c90e520115ad7833f524a6e157041a70924c661bc32945f066ba904ac12ee2d3fba17c3ccc56bbafdfcc09d7d78e22675d4" - "2f72bd2e3a8a029e5d203b5338e532f8d20a4aec761f8eda62a8c2f808a5532bd037b804d753f8b813d6c2c81e89de91fa2fd6fc0162b24c" - "eaa6a63ede95ea4bef89da034700022df24568466436791f4709eaba6fa0ca8118a9fc82b2e204745c7e3284ff2c30b879a5bff7cb86d3fe" - "3000b1a2bfc4ef85d6fd16a44f34bc09e4be46f75012e31bc0e07584df63ff2ddf090efec41f83e450153e5cbeeed9ce232892c88fd9ebe1" - "9e675e7ea4cf4c96089b6382afe7e0cb61879e17cd3d56986a92892cc16562e8cfa7ca865029e42d83b4c3229e3a249ac976ae2fb0b6eea2" - "65edc3ecdf89781db7e8f51c377c4f4ceddce7c552b3d9de0ec4865c436459568393e154af99847030df189eff0210f9048fca3acd2d01c5" - "03f5de0c045eca1221f33fce88dc4cbc81e6835596fce338cf680f37d8b070ce3a0729e4b7a4948f6516eb54587d423fe962f10a598042a8" - "47b32ed8a0159a82dcbac2de5ffb98f9b5474cff3562c37f4662f7b98e048520553e39fd308a4eae8085b36c3117d663de983c9c16bc16f8" - "ea86df1e87a19bee0e35c550a54a9d02c0e436aac234eb4cf18b8b96adfe1c11be88c87c58bfa6e4b041f1a2ae83cf8a7ddadaf8eb5946c3" - "c7600b87472e53eb8c5fb51e25af3594d7697236010d7d8418a8312285991b4d26daa77f0728c6efbac1785c052a1a5b106b54f3e058e280" - "89753c6c2f1a48ee5fcbe5e4ff5215a5b0836584334a909d979696869650bfb02d88d677102d0cbf17374b4092ebc7bd7dea966c35d278b6" - "1f1592d93dfdbdaee9a28384c2dd246ba3928ebc1315c6a4acd0148e02ff92eed414d57425bbcce81c82b5d361101229ab631bb868d4da4e" - "651671e9831e7da32df66a6717fe940e02a2d8692949447b7bc788a4250b978aecb2fc8eafd834efe944057e80043a80043a45000578b520" - "400040066c020a2d00010a2d0003145196440dc80e1df4439577801001fd54eb00000101080a7e069e563740bf29718574ebed5b64d8a8de" - "60435062eb1f78d629ea21823e0e1845519632edb478439c8eab87ed2282a48c52c5c69b235b910dae08925206c8c01953f42af09695d012" - "e0e504880b8f9cc26b1ffcdbf9ae695a049a21d7dd0f7b74323b7861eca23b4cc1fe039f3bd0037e61491ee7e4a3a634a30a608620b312e1" - "f7c9a92c3d1f8088f4c5d520a6ceffa2f6109bba34e559ccef55aa5724d6588a0ea9cf3335a2c93a3f85bc0a9df1b14906132dad7d47c627" - "10bba4e867799aa72cecc2139a4f7ffbc6768aa7526e4b994d16c772ba0d57c94b2361d3ac6474bfe2acd09274bb2dd76f297dba97d747f4" - "d3ec5af0a7f0b58af03ec04a9ce06f765ff0cde59941b78f7de22278aae2617286c7b366acbdd284600a3d8f2dfec9c64d730a2d7955ab9c" - "7ab7d052734b78b842f1ddf10a885f573e7fa072763f4691dddb3bdb3fbeda12c58fabd2b3d23adf9bc2023502567fd5eeae278067b4ce15" - "72e3b03debc6f33152ee04621e6b9be0411029b724c2ae4d75041f0973b946a3c13799549ba8999999a71b992eba1e0d783b0e7e14444e77" - "61f8d838405b26a85c7a52acfe264d8daafbf3db4c409035a2e17a54280659dd05495bc9862ce9a730e8540fb7bc9ec381e6753aee79afd8" - "0518468d1bd796a611972e2e03a1cd8017ec1f8d43fb486837570256406074768524b663505d87b5c8e0e103d006e19b68a39c57f5f49185" - "0975bdab2f76d553429a7f13e7d9f44656dc51238fc80b808e651c6cc36ad4f1853e6adf38b4a2eb4f9c71520e28de15192a057d0827e511" - "ac4bd44d7d5451a45905d2b6223e3c03c4ace8fb71dcb81ad88b5d55d1ed6f5ad6b362898baa5a18c861f780b5de15c3edf769160236c1ca" - "a09fa2e1f5fe2bd304d2d050580d9997995c185930dc8b580ea905bcb9630480eaee113ddce3b4d7230bb4a9379159bffb13fba111e84a70" - "136db3c36f5727381acb137c897635bf4cc1a75cd6639218848928d14b2141fb87da913f00ad96e29e3623bea306cfd100ecd2fa41e06602" - "568cc66c08f875b507d1f329203fed52242e9313a1a41cd856a980835aa228be4e9174f9a993bd7c17d4055929c1a19febaee324ffa11ac7" - "b5f5f5389833923df2e127a98814190c37880352b749f96961170f197a9448050ce469d4d123387861d153a1a5679627dd3a892d422cc358" - "a917e545240c62f629551b03b66eb46ea514c8063b4fb10e2ea13c0a4ee49674ac7bf76677537ae6e40f9cb999c26a94517747902c732238" - "ca92a53fe5ddc419faff02ba1d69ad18c1f84bf4da640e070cb7be2961ae5e903cff878a85011f4022941c15f686075fd002b3f7923b636a" - "8f063c69418719afd6e1e3f57c3b248e015b65fd098bc74e6c2597b303330b34a6a9af84ef4a6cffac039ef7c4436659d90e4a9d947c0e78" - "bbb3541ae6b2ace5b306a70f5820518c6d57d0f88db541677622daacafbb813698754a2b9aff432e8d69b03417f65ec99fd07527691ce09a" - "04c4cdc20bdddba95b2b0413bde8132fb2c86f51c80e765a1de80894ac917a256a43bfd9daa405b61704abefba8552a7958335124de264f8" - "7d6c33bc9ab0e58f956e17009fb65e651146cc26415045c3d5cb1ff0ecabafbcb3cbcace2bba521f87c8dfe7ab39518dd95c8fde5b6a678f" - "503eb7091a74d724f05ccfb3ca74dd29bbecb303f9ccbafaf9fd65803a412b24fa04274686c8fec46eeefbdfa887e04be810f9a45e69c498" - "fdcf0b0d38b5f3666737c40f45cfb9834fbdf35bc464489cac532ee2e2aa55976087fcb570b2651665e5ebd8f0256a8e5f79a104de47f18e" - "ab47de490db10babadaf7644d4a11655b6130519236baed6979648cf938cd986308f92cee99c84ff6a714245bf7a1c8c249a44057e80043b" - "80043b45000578b521400040066c010a2d00010a2d0003145196440dc81361f4439577801801fd36a200000101080a7e069e563740bf29d7" - "4ed4b9cb8f6eb78e6a5a2f600d62de02d87041ef90ea250d910ebe5292faa225bf8538fdc501c736e098bb59bde78080541448280357c03d" - "8c9b3225f6bb3c01638f947ee50ac24b4d8ba347b4eab29e0af0229c00c830f4021ef5c958902d39d9b6b92351da83f4819311d799e70049" - "f605bfa7846d9f0d72b0a604d267758941b42a6e01f53fbaa597917d08da474a1596176e2151f60967202ac1c66803d3b0d6222f94590082" - "21eee6e8deb0b3963e67462b2630599c726cbff75b94cbea43b6842d789cd8d5bdf655f2cab07a3f0a73cca5ea6791fbc615ae9b8c1be17d" - "59878494c36c8bdad06d169b7f5e25476ec1d48ea7e644dd5f461adda7e70172f5f880745ca86161e100da6aaa7df18c02b479f006e29e74" - "a4b08e1916af4cc7a4f31cf99f2c24cec6b5076442c434d9da48664cb84c4f4bbea9838f9c5a5010f89dc9f55773b93048d20266c0358270" - "9bf0a0fb7560dcb76b9429b0d1c5e714be86eb4f48070298615ce17798c72ba4bfab01562e06be2f22afee7663c911ba100b8ce91560f2f2" - "8fcc980751863d7c8c8adae78a4d28da74249da4ff36b37c75069a39073f3119bf152b9145114bc84792da79dab1d1094e83e299e7eee8bc" - "1f887c689f2ce1d6bbb4fe7ddc9ea2a4a6af97fd7e616b11802f930b080ba495084ac340f8fc993a709e858c6c7fc6d30a97f653c65494df" - "e6c3a92b7e9965d6b56ee834736c2bbbabf1ac7e9a1733598e58746840d7da4f2b8e482dd74e46876bf44d20744be5079555589714bd7156" - "c79789988a53bb68624a5ccde6e1a282c9524bd2fbe178da4fd6404201cf6d6f292ffdbed6c5068332600752c057eeb8572df9d986d57630" - "d0dcc6e3b128ee6834db3b7aea39b48b3638a78a7b83b341ce542f524b1e708b88028a2e3101bef6e195ca6efcb67e50c49e725a84478f8e" - "62309d0a662486ceb49c79ffe537357eceee0943f38e946bca80c568709589aa27bb5d94cb7ff97b2d71598dda626d03d395b4af9b5f25bc" - "6ecf7b0b1a7f93407ea07bd48a43109a065bb13b4d39f731fbe6f652da97ba27dacdef55b1561ea4328d119151f34bc5524adecdcd95e4ce" - "805c1a1ce61745dc1d19ed6d1f342cd2bcf2e0c697257ee919402990682535db6f630feef1e13adcd3618d32903698924752f037c27a7ce3" - "3b61e8aad81102f93d918a4e617018fb24975f2355d1ec330e66634f5d22a52093f01beb324aa9b3ae5a656339ad6bf6df46d51b1e9b0016" - "b9151de788ac452fde4d782726bd3ba79a4691e9157e298ff7965b42d710cdba23691bf71b0fdffec7e8fdda176e98fcb2340dde1b6dd5b2" - "f6a917bcdeb5f20027c419990031b6467500327a9c53c4c9649f7d7e848193623c925573394c58a4cf8833765d7606cb107ebabf00e1ff0c" - "cdee1fdba3d6836bde91644aa4536b8ef22cfef44271def7dda8f3ededf94ca0b9ed58c076f84afb8dd06e54e812266f6dbb27d089af91d3" - "391f57b3860d390e182fe19e3f5c4ffe58d3e1a27fe7ad8b3ad1c9275fe9f1cbc4ffb5944151cc85b66fe9e261689cd60c601cd6065ce851" - "b1651cd486a8def61f76cef33a8a0e94967a21c84a59cd9d4952836ef310ceac3b5247dcf3a3a009c227f5552055969c691636d7f7cd187e" - "4c7beec932ae6bc4edfde8077b9c2027ab8fdd29ec93e196a14069ced55929d851e04cbda17bf05a6446f7c3d8efa29115cc6269e5d037b8" - "33965159d93b70dba826383eac0eb0e63b8a7555318a8fe3c762121c4dc27cd40abfd45bda5fafb4bf6ccef802b53fbdb026ca46fd3d4e42" - "4307344e530d5800bf7d6d995d0c65cd8bbe6e03898b357e70353a2ed94e2d2eb9da6a0912c1023c5ee8bb1c0252cb7d363969ba637487b0" - "9fe2be44057e80043c80043c45000578b522400040066c000a2d00010a2d0003145196440dc818a5f4439577801001fd704000000101080a" - "7e069e563740bf2ad9f7a221c7a23a4a0fc879ee05e60d34923071cdf2adb5878769992dd41fa3e38fb55c566817c7cb40dbf323895c58d8" - "6eb30b826895add563462db1df28a9a3e6440a1453a6dd23b7553f17248cdf9f55a5a871fe5aa0c840a3d360092cbbb4b229af5d1f87c37f" - "ed1f63210fefe0e108e936335bfab484b8cba7b53c8e1768713b6f9058a724d34889fca6d66f9ba0da9ea3c2bf896fd3f70d03798b1f8c3d" - "95af94d25ab56beadae7fb46a2ed94f83b84bf6c2dd6d07ba8bf469df23ef8a264a1cc11ee0cc39c3395f0765f0dc3019bee13482c4caf09" - "b7990d16a0740710fa8032d2ef163bc4544f207c54e53e7ce95c5aff5171633b14c38caab7602302413bbd15d2afab2bfd1cc2d3dea68b47" - "eb9230d40376ce444e3daccdf2d50cb2952f3068ee64b8d61a239dfb6a47db466237ed62fc1c45fe425abcdc8effacea50d861cb82e80568" - "8a6723c2cfc3cb7499cabaa4edf9a13e52c2e8e220d5c7e49fa8744d476cc9c1b648efa09db81ebb6b3b9e4a76eb82c9599bc7c78b939697" - "f4dc3c42612650b97f8cde18cec328c7e46db1fdb0e8e777daecb6704f28ef44b8c2e89d6bdc19dfefb1c3041b1b00c7f969f88374278072" - "d8d87c3d930ce4065534df86c672807bf300e3ee9125fcb9ab90028fa72f3e548b202cbd93001516eb7eea36e302942e0faea9619124a5a0" - "6b2c56b33a21a8d5ded781dbaacaa6dd769432fe96837e4e7e12233554a3a7443972823947e7a68b2231b276192248fb78afd6d0a5cd0b62" - "d0da466b84bb92c404c98a3fff6e043f11dce64d3d5c1f4a4cb4f47fc0b72a88292254820b5e1eecac7eb96500e584673659f93ae1876caf" - "b16e98c1557175594737383a725d4b27daeba18dd967d7537e3fde2ba7bb5b8a3ba5d9288745a3ae79ad074cc6c76d9c5909464289086eea" - "6d925676d7ec191122410828b532a18ee68320d7043dc28455cef59e3ab17567931fad6072857f28652052c69110e14718d360b85ec46a1a" - "3d4aada29669bac8efdb43001e10f155da0597fa2a9701c047ce9c14fe1d5351348b3c5f6fc6603b3727791812d65b546105e8c22f1748e2" - "872b16a15faca3fde0b0d7c144382fddf7185ebf1e89695b2abee966ae6d95189310e2c719fed5c25f1851ca1a5c9d739a1bd52a443dad6c" - "d746ab11ef77af4ac38146152bf0cdf85f6f44839467ec69da76a34a7d47dcd21c8bfe041b33f344c0bfa7d36b247054c665a79cc33f5268" - "56a91a058583be6957b5d4b72d1e4d12458cadcf1d0c80289fa5929201fb15e07ae0e96aa8533ef44ccbebee5f160e2f51245d8099b08ede" - "6cab162b2badb2b4534209a2feb4ad921ba4c956b11435c8b1d07cca4281b32cfe31c2f5947e1ab60420a219cafdf66014197ea8c5926e37" - "248cd0b20e5e486066ad92af0451240718e4c4ed6be9cc4337195d81c55983fee698fbf24314e8cc8fcd45a028c8c88cb0f43633a3c5cbfe" - "598fe277159a5508bc1040718c43c262522f94f83c710a0ee6a702793c320ffacc5afd09d6fb88f66fae4d0bf2596fdfb41ab30dafcfcad6" - "8b559a85bcc0a07927b333b3990582baa7435245800c5312e9e77051fbd1c1db6cefd72405924915667c208e8f8ba52f227cb4ceb9a09199" - "ba274f05bf8d5d84f9c6303411b4261ffbc59d07f2781ba1f5b831b01caa9a955f5699436532bcddaf1d4bd947083e7367d64d5c66bc7648" - "d6f8c18a07f87c2ccdefa9b61f7255a21cea0f85a0cf67f51995fe2338722604325ae2ab7cb891534cfbd8ad139fc8b2b38c1e04cef91214" - "0905c0e7cdac8ec49de78e617cceed19672c16ccf1e7de4bdb0850b35d2f73ee28200b7a0e5bcd8fda3d0531c5300743722968a31b02d474" - "294851d1021e670f7fc4c21a44057e80043d80043d45000578b523400040066bff0a2d00010a2d0003145196440dc81de9f4439577801801" - "fd125b00000101080a7e069e563740bf2a61eeed759c9d502cad61a76b9842ad6f4aa1013def63f72b4ee67fa6e999152d220f0481fb265f" - "cd8d1d20e9c8ec63c4ee6fd1d03ccd73bb573304ee41fe3f4c3694d892866538a89922b5ef07f68734c3e5e7fae1adccd6e21894abbe74be" - "af7de77b12957df7c74288403f928696e276e79b6d22febc3e42b24fc24c3c17d8c7135d5bb06eccae35a6881cb85b1b9522d4cca06823f9" - "9bcf8ba48fa421bb71eba7bbae5f8252fde23b90894af2d999a7dca58bf9fddf3c54f698532f6c05efc7da4cba03f2095a4ea03113a56a75" - "cc851af1b8bfb64ec114c38ab547ff04c264cbfdd6181b41f694d771a48b2e689c37324989a54b337ae74db0083559921e26d58b2e16845e" - "7cbee395813ce58c6a8b10e6b47be70b2918da7604422949c04317cc33ad08a90dd8898b647753075a1851eb5f57f9b6b96580fb61e85fc8" - "a5f1e6a1f44ffcabf0fe816ccd208fbd0845f72d004898248f9969141dab23af2427fabf0161563595b054538277ebc138d9b9e8aadf74b6" - "bd1601df92e28ab2ebb3ff4a57e614ff8a16accbdb6edf87a5d2bf87d963e1d9c3be05c3a63676db8b986bb228b516aa095624814fd4b50e" - "24147571a317564fb465c6560c1c0155bbbc4304df56a7c8532ad3e96a397ea87fe66f2115d053e30a41b82c09282c5af4d0ff543ccdaa2c" - "c25c1359e6c40a21b6409e33870d5ab6aa15d43491c87f65b5298db0764d3a4fb095a54ff5691478c602bfb541ffc8778c0353fe48f5d4f0" - "20236c3f2200ef53e879fbbc6d09ae6b9775ac6ddd5017edd955ed4d57f88d2f0e24ff9d04dee9b83bf26b2b204de167ed8f8c922609b8db" - "5557b77981f106dfc997bb994efcd2e12f07a99c49d66641566c3acb0bdda02c6bda285eaf4bae694e9fd6d2b19144d2308186cd71fbdd8f" - "6beae66f831bea7919ad3bc443fde441ab0c704a41a4ff372aa93bd8655ef81bddb0bed7a802fa2c2d38965b047cced233a42a78198b61df" - "02b9020211cdba4acc46cb86308555c5e518fd356253057512c4da6d3df2e74c9416502d44cdefed408b63daa86130f946d00ebed9e2c5cb" - "65b635bbcf4e97dfe7c1260af18f61ffe9f266e72a672230d5326f62d943fc1fe8e5c7d32591e2efbcd7890e1e0f9ef1d2f57e4d476d2a6c" - "4a7126a3e5e340d62f0d0f75687c8ef364b22acd5e3fd3234fcfcbf62083bf760116fef01e413d6077a472c143abb75fbd994587d5016f97" - "71e54847c1fceb2b24cca5360518e89ef7d10ff04c45a4decb69954b7b16062c4b65c46def639d8ed0864e6490a16f4d425a0d4077d578d2" - "964f034588c1d8bfec2cfa20cd96dab50e881343ec6402cc68b7a561a2e6ce12c25afd3642a4d294691d909cc556d9d5f1c7ae170348a68e" - "d85efe4bdc1091da27a2180aa92d205a58e0dce800fa0ee38ac7ab546dd6f00791787aba2226798d1113d4abdce01404428c05095811e57a" - "f1fe575c56461cd05805847b25ec792335832f1edf674cadd3f598fd2226e68f12e870934cfc11d7c9e6d8216f55d575e0bda5b64ccb615a" - "0af3c398db042ad3ffc409c550c5f3433f9f36f2c43ae69497bb5c3ad69b5d738fdca25867881cf2716bc0459a7d3cdfa0f8de66682fe87b" - "a0e3976cd759a23a8bebb473868988ba6872e9e44a77b4494f5fceae2c3c3bb2388a42eea2471e5753abefa22216f56f58ea99d602c610f7" - "f504e6caeb035f87335786d559f76ca7e36c97f8060b9d65c91648d0d2b9fb7496e2da9a95c96e986b3c3f024f73a7101ad5617ac93dd165" - "bd49f941e1032e74c095eb8706a4c3a52def56172fc87b07a7dbe012cefb6dd3c738c50980bb50f2fddbc0dc2dd99f47bdcffa43a3187d30" - "4ccb6c9b1912a1f8dc66d7ee15c8b6007941553d6744057e80043e80043e45000578b524400040066bfe0a2d00010a2d0003145196440dc8" - "232df4439577801001fd2f4c00000101080a7e069e563740bf2a383742182b65b90dabf63e50d655f769ad8729a758555d93be9bcbbed300" - "f05b1e74fabc541ccee22b93c53dd4834f0b8072d27d1cade75922d65ee18b00e28f3860d61f69e7c7411fabb47abc216b9a148fb2920e11" - "dda898c11b58236c74b8c03dc56931ff0a9f2992f607a81566479142b45f0be1c8c0e01337165b9ff9ea7d19aa6489d8095a824966275f03" - "061390e4575a173aee2fc0effa353b94ffe7961fb20d6d0c5bfb8e314a99f08b1af9d25ead1c0c34cde9884af498eba94ded1eeb079e6121" - "86fcd6d76283073d30d49127ff400d8279037b90d8d800e19e064ccbad6fe7138fc22a77ead7d631f46248d44ff5237c7e4a26bab6471f9f" - "02683d83d57dbaabcf8f6ac237842710bcdf186557aae87f5f74d178fda9bd6e8ddf01a1ae6ea723199c578a9e55aca2f119cf4e4e9fa0da" - "b4d08d999929a20dd8322fa3066d6f4e21f25510a457bd3029175580453f635699ebc6818edc171cab2a2e1f69cb5946b2841269a945a89a" - "0a19d25a687aeb45d65a070c5fe4b204307eb40f7210e0ff915d41956d6c2dcf766ac982933f85c242687968062b51edbb28f71d9b010b14" - "a71097dd976cb8f64bc76e642d7208e621db391c467c9ea1606b24a94e056fb73a7dda67a277cd21d0e82622bc5bcea9dbcd64f03f2ebeb0" - "a01d0ba97cb6b4323a9ae9f7c04fe72c59c152a1aad503fff3c6e6f4a07a74f8c705ababc87a635b9dc5718ff25b9ecf2716efc50f8a42b4" - "d0debccc4550cae711c979b7ff7733583cf4aa863a2ddf7b1df5a66da8977d843d5543db8f14da427c3bb24adf6fc4864a16373fbc3819b0" - "15ae27159deede377ec8a44d53df78c4c030bba77f496f96022d4f9f76c6efec9af8400623eb9dea142ce7bef7f624ebebd86e1a87f879c5" - "12dfbd9a10fda28324dbb545e65e9df45c8cf5bd5ae013b9a58bdf8817b2eb0dcbd3e9cf27c8c1496c45ef0df35934002e03a494a519324f" - "cc62860380aca620e2976f2117ad655dbfb1cda7832d5dfce7e8f4275efae351660a6cb89fdd236127c301bc74bd32319643cbe67ed30d49" - "679c239b98002deb07dcac5697bc2969ec582061d8d168fbb834f024835923af0a9dd341210a253c7efe9d24c0b2de6b5f39b86e2016e969" - "448ab7b9f9cfa1065d66686f116ad5e22f2be912eef22d18f39f356d0fe881e4b0dcb59dd4d807083cfb6c655af3025b0daad3aa80b2835d" - "c0fcdcb4db3aef1384f52ec6433df255779665dd5dcdb7b5264dca783e38819ab875fbc185cf9e1c5c7e96c3fd72be24136df52e005f34ed" - "c251e430a3870929308b108520ab9f4395c281280a32ac4f03247c2bb94bcd16263d509d950953f109caa25998456ebde28275c26572ad53" - "879c62f592bee8ab97ee36660b190348dd1e7e851275e3d9e72e0ad23e0a9777f05ff97d74e677044a551b8e944e2167ab74469b8ef4c2ce" - "5b08b048c8ed8aa8d0117145c7634fc2f8987b20dcd5ef523556d7ea9b3c144dc55d89beb11b5a79067826cb47c8c1d35571ade600bf3559" - "3d21b82ac89dfe7981cb989d8849077b50217d85e28e6c635ae988dae76eed790b8d0a1a257214617118d73ebe7459f3ea86e4da8cb664e7" - "e60914c3e77f6744f2e29a9cbe3ab8f41fab89ec93095adf39301e8d64008e276727056a45724d370f0b38a504398f88ccd89bf13f5063cb" - "e8a6a891d58585f218ee5af66bb0935f3d822227bc1122b9aa2ca10e179db77dd33a2aa361d7fcb3f34c599e13e2ff70750e09d39996c624" - "9c18a8827bbc41acc78b6652120c1d70f01a545ac142e4cbe5d93ac10e53f0ce0352b21f3bfaecaccc86c3977bdd8d7157f65556fa449242" - "b9028c9fefc94ee338f02eb968f9e2b889768bc55c61ce2979c1807662e244057ec0043f80043f45000578b525400040066bfd0a2d00010a" - "2d0003145196440dc82871f4439577801801fdd52300000101080a7e069e563740bf2abc8eee097f88ee74d36f77716debd7b830af21f039" - "e2ba9bc4cf7aee7f412f1f8b3d1de32ab217f12665981578611d9deb5b620d374d1633bf82ac88fcf68219ab6879ccc32658208d400ea188" - "089cabf17d8e7c3cdc9de0a1d47f86ab0abca36468ab5f782b4fc35f520cba4e9598d76d20b11f5bbcb3d4a894784dd547136587d4444b14" - "d0292a996e4b69084ebdbe3fab2c20498639f43a1664d5b381ed9f5536b04f700629be4e017a2ae2982f7a5fecc43cb2d3192b694d33e15a" - "a83ea4aa33746474a13a19e80bc88944fe2f6bfb164d777ec9b25e03835d50999cbc22bf4fa0b0f4c5a3a68228dcd77fce86d19ba815cd0c" - "9d8d96fd85d88fe50b6ebf6c5ae25ccddb809261f9fb60b42370f706f5a8c9a65667239c3971afa2e8de418fa75c779d7de493bb9b20d7bb" - "dbdeb89c043854968c150c28067e7d3de845008456c69fb9eeae146b37ad8f38e27ae896c0a461f36148fcacf4a61c51edd5cd4bf31f64ee" - "07928d96264ca23b88ae1a8aa12d2cc91f90c66fe2a2ec0ccf7fd973e4c5cdb19e12bc464dad6a794b95b39273a22cc97dff073cc9707671" - "8672ae1b71482fd37a4eccc27c6c1b76cbbefe6d5d375516a66cb511da4f259f96fe9e4ecb2aa2d1c6d879f5b03f68d2c66610db886891ff" - "06af9371fc3d777da820f30727b2ca16bbbcba5f0beb8b7f2fb4f6a95018310cfdc0f9b263f32df1741110f36bfbf2f0ac523f60ffd07bb5" - "b4155ea7c33aa0f2115751800ee564f798cd7be6a8c873772e5fbe6be502e19e5a02beb96c8385e1752c166b78ce9eb85ff4dad61edd386c" - "938c21975c046ea7e5cd10d6d87be08113bf3a4ab71b3e827e700dc9e23b1c3e8d93005582fcadb3ac9aede42da2b4909c27d1a836b24f18" - "3f3d0d521feda4190b5a852d019405ad26891fb29ec8b3652e8d434663ad9568ed1e8e8ee5ad96411191af435328a7eb52055c3e07c48fa7" - "90ffeb10c165ee8272a41ab6b95b243381fb0ecaba9b099082d964ce7814c8d43861f05710e3353e0d144e9b524ce9c6026e880e27212d79" - "b563e5068a7b49d30930cfa1805d4494f0970c70960eabf2a9f87784a578ec2b387c7099003c45b1c1c9faf54cd543c2e0e287d8f258f391" - "41aa7369ad28f2d27694dd53fe72cfbea38b5dc075d7a4e427d26460cd5e892ff08abeed5dabb99559c14c6214aca07cb218a537c2991e4c" - "fa2c57ae800eabccc5d6ddf2112e1ae7895fda2816dccf3ed0e3932d443cac851b923bec4c0badaf7fb1338a818fbd6e6843ebaf34073603" - "8b9bd6888c897ecefb14f9ddb71f57d2df4cd29a180cebe3ea0e25c2a9910968f32ba2994ec1d5a7b8d426686c6f0a5d2b150543f78e6488" - "2d5eb52affe50eaa58cee4ff8bef3fa7df8e3cbbb46e01c802d85caf1be1687555109f2f340873ad304639fd14ddce7845e4a48899287782" - "32c8425b445fdd5de1f435b2be356841624232628f64f1a5344642bc8c4a0c3cf06c70717bfb0b4098f5d4dce8b1945f21226d1d80f0ccbc" - "bb33534223ee68298d4bc4cf840d88c6f34b66f111adc3a7bd93d05a339d72c6368df20b9043e6e48f897b9306e3bc45e322047cbada3244" - "ff9dc4f179a470ab9bc7718d0227dcd7c1c49e0a842b07ee0f12cf94e3eee66232fe6c3f36f6f08fd50d7608bf5155abe4a15963d22fb6eb" - "7c1502ebe1a6efa74068aee711e454e88c96a8f861c76cbddbd3f88c0e3beab7f08fdb8e68ff30bccbcb1de91b6facb872db3d85d49bcd67" - "57fc729fb750669b3ce96f5d00a717884eb53f6fe0ecd152ebc1559765f4131526d4ddfcf5447341809acbecfc20c879f5e5762eebea7318" - "2c6c8f3b067b472e0f8dbb11acd14ec0bad2929eb6df0d87896289823b4edabcb3c23bf9b7d2c044057e80044080044045000578b5264000" - "40066bfc0a2d00010a2d0003145196440dc82db5f4439577801001fd261e00000101080a7e069e563740bf2af8d8bb5c75e5d9f6e1c5af03" - "4bb3f7f0ce8cc0e53d7aab489854a02f849e286520dcf9371d9c9f6a43d5f606cb7eff2abc7f9fed697028845a888be0491fe33cf3a3f63d" - "0bba4e880f8537c769e2b9c6a2724d213d43a680189a7865cddfbd0e09c5b2fd451e4915bf4ca30970daf3bc657d6148f8ca0c415c93b41e" - "686edd7ae663bec135ab499bd23fffed97b87e019fb946724127664d2ac4aa87b084730778c3020866f3908c9cbcbef681c97425cc37b30f" - "7056bdcd401925eecf8a1db0b1943add96b56827d9b8f3197765743ab06e449731b97742ac7f13e54917413f8a9e04cfbbaad9e8b3dfca50" - "2d698558f58359d86a1a8e619df10342861d15e459cc89be3b20c806907f64e16a1115d592eed6532870184d6339b37ead8ea5475c4aee25" - "d8560de2ffaf2f4c71b02b67cbd0c676761d980a08a4997466b95f851e1866b5f890bb678d33268f5603cc2183cbb55cd7c909b641eb42f1" - "3a7469bd6d09192463a0077415c4ecf9cd034518b31c8693b2373dadba9608e8018a957d4fe823cec7014c2603d77274599d1d7c21f725c9" - "bc24c6642ec9238a6020dc9fe3c4ff339e2e6b61222f2097bcc78e339e0e5b58654538756c6111556ba4a324b9fee69056d9f7c663f3b6e1" - "cfae57e69cf6d4af45af1a489934bc7dfd8a904f1d017888f61e5787319b5e46ffb28f4784aba7aa483be29aba720414c1bd7461b2efe0fd" - "534631d7350bb364d6388ea5c3891c78d4b7c78d6a70830a62f84c489f1d8e0eb94a967d8f50dab8a5002e89a8178c9a0f24ebd848b41837" - "c66ba9ef6405e087ea2d890ab1373707aecf109900f7638dbc0cf48ed378179e39e048ce17c49011ee2a83f7dfd7714d05acb1f9a9449b5b" - "412e1d9a624e8d1f246f2f241c5595eba3feffe9f8fa3f5aedbdd425ce29bab8e7688feb8f9dabb354a179ac405b9c5409792f8f1bd45be6" - "cebec267ec3de8d4550123587accba0a3e4afe654f3cf0f4ff460719f4fb40f36654f5e225bf4039b3523019997ec82c9ebcbb6110c8edab" - "ed8b4ba076ffdf5e41bd4bacff701a5a16eff3fadc82b6b03b1cb3718045c92dc306cee78d2fb2a99e3c5ebcf0ac6910a9e821d7c26b15e3" - "5c520bb71a0aa07e0d7889f77e78efa7086f604479c540be098f458fb32a05c56372118fb7a989266570fa7de2a1df2b715f9c41c36ab639" - "4d0f3426fc6a0ab9436f2d4860ebba5894153c1d963ae3bab0fbc10a61ee6f05c309593cff2b7da1a3b3433475b084f6f4163d456024d742" - "4f3e1219e3f23da4b82fb3ed799a5056198f957548a36b1bc382fd2c1332694cbef2df1aa50142dc961b6895258fcacf32cc22c20c31632d" - "3ca400d4977624f7641385bde0d69c5303d95a58af76da8ee49875490091bacc5b017560982367907be12fd7ce0dfe1bc7c2af86fcbd872a" - "14c4f3a4647df6523d31850b96b7d575ab94663ce7fd5311d2297d3f0fed50a66c26a5cc2e2d28d630ac0020985b3d669aeee4d954ee419c" - "547a738f0f8fadd1fc12eee4a8210d96a635c8860d06ed995d3ac6dce96c872fa298bb24006502ee2aff91e9303d040b63e6d51b4100b8cc" - "47c9dd9084beedf9d3b624d8e085bca2002f51faadd85de96b9eac0d62dc514cd3fd5ee9e046400bd40c7cd28f30ac9a1513069d69da6c52" - "bd9949f7846e25dd66ddf0533ad3d9dbdd32bd47ac58523e0015ff98a810402dd00927857f820336c3b1dc1a9ac4a558134b65ad3da1b551" - "010215e7a4a9b79d59c774dad3171527568d0c76bcd229251a541e89fa3b24a7a1b0677a63e9f8e864ba4da5694ab3a4a463bf02bdc66b28" - "a2b62162bc418c7d8037c326b50b12908066b1dd19293982795c7da3da0bdb8238ea18d7e2168bba8165cff8c424a61644057e8004418004" - "4145000578b527400040066bfb0a2d00010a2d0003145196440dc832f9f4439577801801fda02900000101080a7e069e563740bf2a17aca5" - "49d8dbab7599d78df435bad80ceb8df61524054df2d159aa1c3f023d13b8c7e3ac57d1bfc0f1f374b8d99bb5d5af44bf3172e503077eff73" - "d5bcba48f65e8e12117a26331fe1ad98a0deadeb43398f64fa03657f13f4a8a6c78e0dc495991fdbf4e6139bc8a6a4f5d09c4aae527d2024" - "be258ef9cbafd0679667bbc211b3a72dd117779023522f74b8e3973cad0cd0c1084bf0f51de6ccd7599e644e9ffa8e061dda98a369463835" - "a31c3c9df1c7d35eb5dc45d58fccc621f475a082d457f7f213d40a963ee0fee3fa8e24371f229dac77872e0e2e2f66123b24062af39ad61f" - "9fe412c1170d8fac7cfb2e6c2aba4ac536f459ab2f21ea8bf0ec60a0d4c328ebd89ad720f0f1ac9e362602672798828fa8196d4ea7478acb" - "dd83a97c7c037a102ac060afdb659280b9f32e4d18df5c438c0cc1c0befc495068f373d3286ed08890af10e2f337c54d6891bf4e336133c4" - "f846440af81e4bb5ad307ecfcb624697c4b4a6592db3316404312d0264902be2d6fba7e7517ee74f13674a5ebf953eed625e1ef7a1dbfa7c" - "6deb0bdd6b9949fb2598709f5ea0b5cb766204f1cfae980cd124731b8ded85fb76961266316750808ba220dd6bec9aa7c9e1c0d21a2f4f71" - "92e798255d248e0dfc6eb776180f0333de7f964701b644da5bb9459b2ddaeb26487e35402e0594774cd228e782e1160410be7449826013e9" - "b53fec9627468d296546b07e3b6969e4489c04dd470993ebd928cf362577b20253a0b3b996ab7ea89efbde89447f46c6d47b7aa8db17549c" - "2eef3e1e256d972f828d2fd8801fa8659944da4dd2cc26254390acb8a52ef9abf0254b83fb6da04a91dd1a8fe7260dba4bce58b12374e7d9" - "8da9191f2fd106a57a298d8e8e26c5e20904d7f59ad8fa227439a4a78feafa9741fe47d0e84bcd1284db2fc73c0b249307d13aacceb63ba7" - "7dda62fb5764acbd12f369e68a3c1573ff9990760ef6892cb17962ad771dab99d4ed256325669b5888e5400ff1a5420004a94e29a77d4895" - "a33b5b515971eb55d07b9aa829312b7d7a99dbac61b6bc2d32233170d0bb1dc0dd3686fcd8e8055d96b6a2927a08e06911b4aa0beb242308" - "5024629cb5eb9f4f37cee8046d524a5d27ef255cf7b41aa9f47567ae2d12e922d3743028438063f14b379e7d2aa47bb6795e068ce1f3c235" - "327bc26d5127a185ef195c073841bcd38e63b71310c5b33e1b7c02c08e14c249d4079cb4903f9a344dd9be25f4a5030934fec7644e294ecb" - "3840414cb168580db4d2842936144e54f4434a7abc82d5e25bb19ee14ca62706b49179612c561389bb3c019a9e0c106a0404b4b87e85e6c8" - "4f619fcac333c2a953bceea82a5de492c768d8881b1442df3295a4005e73fd767cf262cb28248e53519a178f0f5cf685c48a3907ee4d6779" - "d767dfa78f009376aa2c3f64d614f97b798fc6651baa1b634598575d2fae3bfd7c148c4ee97aaac63e74d676c597b89d7f88f9cb57f0028a" - "25253f303da6225395d13c01ccf551d787b775f00edf6c77f154f0b823584057416690ccd9f717538c25bf50c096ff8683d57a81abff8558" - "73a1974c20dab6e904f21ae0c01263a1e0d56b32a73b44ec1a5ffabde0ddb499762e6f1769982c1eff86ef2461c92f0c3ec3c8e32c2f1287" - "3633ac539b8df915b8fdcc3aaa1e00cbd84eab46aa78adb4431ab4201736e6375fd95a03670528a3d7baffb3d2bfc53e85f00f44ac5cab48" - "d9f1e820fd4044e44803030b9f08281270c618cac17d84b186b24446bf77fe3bf33935af641e40489b08da19d96affb271387b7cb331b3d7" - "79d5532e0610b0385137418952d8337b39da9065b4f3332cd5df10a5b867364898f4e67813142054ead3a436c4a40a62219c960c79682cde" - "1f44057e80044280044245000578b528400040066bfa0a2d00010a2d0003145196440dc8383df4439577801001fdae4c00000101080a7e06" - "9e563740bf2ac2d8a713e6caf003ce3421f81a4f705ef9245877213d6524cf121a548b2ca819a0fb2d5796546525e9c93129d616020d535a" - "2a79ae034852757fb019461c12b95e0d13340fef3f1e893ae2ba471b931d6cb3b74635bb2167e3e3391c069df988379f76026808b78a880c" - "b9a9e79651bfd08b9b885404e3247a98106705257d6227ec9b91da489515b605e95a5177c05111661d28c2881b1ca2a1d242a2ed60093b18" - "c38605ed0b1909bb747a50f07f5e60b6843e27c0892b2aa2332c450479e2098f95dba88fcb0bd075b81c16c475b6213e7ce8d23afb063e7b" - "482776ceba1e88fecbab826762d36ec746d34ce6141fd297f722e34e13f0419bbb23d7549dbd1dd9ba79124a24f7239784841bab69cebdb2" - "a0f9d7e66f6c3a05048a0ff94e2e0c0efd516e8698a1bbb8c922054a1c3098ddb890fae90ec320da0fecb4a2c9086b72a445eca1b71b0aa0" - "b4655168edd4d3d6b91ae02fd1ab9ef4fc4fb51c28185d6373cf090b738bb530d081c3eaff08916754f96c0c0a39ec60c5ab7c31d8549fef" - "6f6e4fdb08fcacc36df44a76b001c10c28d6f52ebe6d577f571028098dd2089a111d38ed654e243e52a292bdd5d57985804d9a3fd08412d2" - "918d97b3299e34ad2c9cf57b3145f19529dd87a79165fa732f87ad2bbebc8c41d6e4e591d42c350407d2327b1a202a682d03bb0fa6c421d1" - "e355d958a158099a8d02f514ab71b7d2b1613596c3f9e96c98fd258a9177872ccb847c61ef0df5eef69e84da2fedadf36239f570d855474e" - "c99bd9a651f3c74bb7d626ca52d57630eb3bc352ad98b300636c9d911e1fd49c15c178ca222ceba96f86a4b68f5ef65855c87f1c90061f2c" - "fae3c74ed699e1cd1a55fe4dff9e07ce1c285e1593f6e180991e1d37966eb0f84ae454e3915de5a81c5b9fcd4f88da53f1afed9827d1c5c0" - "5052defa563adc7e6cf117c28e04a263a7f387a5755ddeae2af02169311e69c5bb24061c40b1801bdf75c233f2822c3a5a47b34880352322" - "6fdc4bfbf14eb621069db5f5bca88f8bbff16bc67b9921942e948ecf7644c3efde66abd9aa420163e21337c7dcf8d0e67bc757003e20ce63" - "ae6bf92087372a58ff9161de96587564b0aca9e8da5f1c207f26320bd1eaf2665789657d08b675b80856ee122461b020d8a8fb871a4cb719" - "d80db0ede0b58ada7e5b3a98ea0aa370fbfbb42617e394373fec9f2f6e8e1a0e991a8b7aa03b9b36252fe44382398ab9a5642e6b52fd91a1" - "58231aa4ab7a06e3eaad2d8d171161c8c7dac00535c3adbc74ff222e29dc6b93054a0a94603545b2f001ce7f962f20190bbdeeebea861db7" - "1334ab5634314647ebda9e077e2ed79138dc58d7a45c6a6f3e507216d63146f739f50167ada612487aa709ceeeb04873fd3e16f392bb6f67" - "9b8bf46db2e651ec07d870f60fe563a083eb5d00405225af9eef1ec879c1e0f7d1a8296724db3cb2fc7185c56517637d30e42285cf9e4066" - "62cdd05a99dac47baf060813649a3e90a61c8e37cd0a43c92b63daa0835798dcf17237a6e5d53f13e7de66b520457af3bcfeebe6a53aa1d4" - "e04362df88f6376b826adce4a2398d2333325325f0a54fdaa65e0bf02423afce6aca2dfb073d41f083d9a0ccc250dd4b1f3f645a1602b6a5" - "4e051a32d335f4bd4c9d650615c655c9ef5a80b76ea138d0d3fc5d6b74317ee8d05c01b5b1280094b1135f1b6a41bd2e9818f5048671c0e0" - "0405ac761ed1fd61eab78d8d165df82fd8382485c2377a5040cb902a04a732e30f87839dd2cfcb908562dbb3801adf95cbebff1b49e8d5ee" - "f98cf70b4fc9eb8f5c2e901fdd8bb49f737cc2a96bcea6c58be3512deefa63673d5b3bbf452888987ea3f16143994100aed15c0e4ad0eae2" - "70f9bdab75802b6874dd44057e80044380044345000578b529400040066bf90a2d00010a2d0003145196440dc83d81f4439577801801fd6e" - "2b00000101080a7e069e563740bf2a0c21088d1e98bb6f405da69b7114b31cd39bd9fa9f8a3d560c971a7aaeffa0d559e519354107b0d6d1" - "987a6c977980e2affe708ef7d87210e1ff4e2a6f80bf320171aa2af3ca7c07b67a20efb1990f161dc2136262b637995d0cb1098699aad650" - "146d15cad64f013d8e12ce6bfd0b26b88f7ac7dde43dd36eb5648e07dc908945cf2d16e68df090c078e2a49ac7296abe92b403834aba392f" - "ca42e24e679d8f9a6531eb33ddb131ddd303ae55ff2be194a4d33517861c44194953c7e947462769f9fbc6267c77a5001c9e319fd757ce85" - "53a55dda54b66d236a1adf94f021fff170e6c79f78f6df0c70d89216c4184e4420423a57c862fc978ca45a5cefc7c483a4a8b20bad295624" - "9ca443fab2db282e2f34e9780758b0fae6ff33276edb1b1319a57e962d380c9140cb1ab66cc6f20080682242955ff1c03cbf83f88ad542e1" - "7df00d728f178cf3a2870ada42d0e2554d9f531ebda44dd28779bbbc240917396369f293ffff3df2684e1c2b775ae5674fb65875a41b7001" - "eb8cdd081ca4a0333fd22bd1f1167ff06761b2b905eb7934df662d35eb12505cdf8c56cacbee18cc0db2cd1da6ba7966fc3fc51d6ba9bdde" - "92c2b1dad4554728b2605bf33928329089dc086727e99250026516a3b54d740e6cbd8c59a26c03bc77cf885271b53f2b05cf80ddfd58f7d3" - "73503ed7096894e860a8acbe45ca804669e677806f1539e83a78b8bf157cc4119a6f488c90014d18ca0e3970e15e47467c4e40b6278948a1" - "596ef01c5d75b828b1837e53a2a61d11545f5072d4f42ebf7223e3992207fd097f49e873a771f338c965e290d42d9e85656cd007b3a56326" - "5cc81d451f07c7bbb105929eb173a68f9db4bd83dddbe23204e30afc87e7380af10a342f13bc093428d909647334642bac17ba3b4ced923e" - "5f4cb24c73b45db5d0fd0c8d4efd41921fdf1d13171dc4e9b86b759774e35529199e0b1f90136d798cd0cc24cfff715c5121e1c4952cd5f2" - "fcb1755f8b3c3253c32f0b2e908fefae3174f2d1595c80226df7ff9cbb2e739fc0db3592ecd816207312327e6457c9bd524ff74b5013122c" - "6c6d76da9dddf18453d1debc6ab19046aef0d78b5fbaab199e2e9743032899d07e58c67cbbc846668ec7d806d672d47cb0c7ec2e2841a41a" - "091be88648752a1b0ef2242e26dadd564558700a378ef7acf8ab19abb5844cf7942777a68bc71a7fc534639a31bd574e94c1b9be0ba48b32" - "a1545d7fe9595b62c5477d6a731fcc42560d19fdeb38e99abde7eea8936b06f724f8a2dc772f4155367011c72d30020fc31fb201fcde0d8c" - "c96c41261f66de063e05d1ff942b198400ee6e3c101bf01d79c25978b62dae925e1764e3ed759c2658f29b8385b62d72380092dd7df1c650" - "82919dda6f6c9b588acbc4c5dc64006ec875c9b9ed153c5e42350c7de78a6d06f073e94da4ae5c91bc9402f5edabb6b59d21ba93d13e80f1" - "eb11add3b98d91b284dc3701911b802d336e90d64a84a310a1dd97701361ef9ad13d0381739b699fbbf3ce0fb8edd526740841525c5e6a27" - "19bbe2069d706fbe668adac924899dc107d64bac40f268fd46fbff978a828f581af05baacedcdc8294b4fed0b9fb73c7baf1571ccd4d7e8b" - "c0544ac917fd7d8d5b64bb91f7d49aa4471f5c192fe488daee786b4320f6a78fc8f3ebc81537e9027bf57882ac64eb044a866fd9f1f97eac" - "fd87400aec986315967458ba64c60f5425f12d9c8087d564330779d88d78072194f55373e460e963e10040305baf47d2580c6ad68345fcf3" - "864cdcf653acf4fa88c14def7378453f9549556e8a5f168a1633418cca8ae744fd5aa21be4defc17a4b150434d1eb3d036573cea6238395b" - "577353d7ae7325a0293c80de065a0d76459b0044057e80044480044445000578b52a400040066bf80a2d00010a2d0003145196440dc842c5" - "f4439577801001fdb96000000101080a7e069e563740bf2a8f2196f53712eff2cde03ea9cfe3528f171e6de1d5b3e7e17812818a53846f3d" - "59e23f3a9ec7145c0ae86cb85d3744530ae190eba81c7454038ebf0da5e0329b5d79256687af97dbf754de8532f6c7ade76a1f6d100384ff" - "2cbd0bdf4f3105e6c232819cad5c50d2a99182e6e28e8064eed123d79e0932825a0e79e4a3d92c417422727ab6429dfaaf4c0d63da20eb2b" - "cd2512658ede0c99f4a54b95bec187a310ed357944fef9df08e331e53c390e795f79248b6f16919488e4d93e4830297d2913b5fb81cb9ca4" - "3474262c68f8d81bf97ddd0e9a7fe5a8cd7cd926bd7cc98581349e7960272947f06418eb85141372f1de7a989049a978441c84385995aabb" - "0deb6bd3d25955c9a977ac63038022d1e91e260659937693c1c144ddae8fa24dcb6fbe5507514ba769d2ca556457b6fd320b58496c5aaf1e" - "861999d557e8645847029b3931f6d4b0bac580b2c1762d9a0afe1cb397a49b6e1340193802f870ad58ff2fa9978d06ca8ab530525f485ef4" - "aac91f7755a79fa0f50700c96d6a3a7eec0166d8caf75b9640c5bfe37086fa68067f428851cc66198fb7299e2041c3c4198a40d84c9a96e5" - "3d552d622e871b6481dca60c9157259c6bb9ec3739282ef10510b7019f8147f24672a6744e397683565aff36f4fa15075e215228da6bc4f6" - "450000c47e7335d82992c4cf4d5aa603b4e2d7e42c3435b271c55f60761bea760f822a0d7b0907a0c0f234c5ea32a9d1cfe5583520d7f791" - "763135fbfe584f40784a8f4e8bb173e5afb481aed5a743ff4a4af73b609ea7e26a0d2a57bf248d0613bebd8489be445aa009a3637531c263" - "942de771ee88501987ee95e6343fd6a03781bdfd113384b529333033771e4e0b18b92c182dd7dc7a2b2469d90639499f487dff03448048f1" - "11e1355e824418e7aca1e976e7faf353077c0c77b1dd472a0762d90c4beff87df9a0e5b7a254989e66003b64459252f42393f52348f71250" - "517db4d13b2f33b5ab8830487100447e611fa0696057a2c928869081fb1c39fa73b874b4cc46ffef530a629d216ece981a16e95fe49857e4" - "b32f4ecb0221cb3ce81b27333471d0d887929eeff3fc88aae10bb936c0f402ea1c1938255f579c82affef0a45c408adf154edbea09fdb2ce" - "b95536fdd75b8d5ca0d81536a3c636eca7b60603d4a93aabc6499bc73a5fb01fcbffa9b6d86b837a9e43831f1ed888cc87cd1d74aa4e526f" - "598aee7d0651c0655774124a1a80563f720558b0e50ed6549a263fe46d82ad51ce045882019d51da50b7a8cdbfc56034643833047f65febf" - "06f8f4ca864e643ff907a2881c85386091b301588c0d8c33f94d46da0b58080da82a89a5611faffd219bd25c9631a2ac94adc234707e47da" - "7b720256752b3451d98291d1b88803ed63065fb393e0cac127ebd7e5c70e607256e55f23b5a65d177013502761fa27d56a11a98468e1a431" - "a39daadef7c92cc432ad163032ddcea91dfbf3ba69688019a9d7593f40427ac554f5f0d7c62557160fd14d8cf463ffaf51c6a75354ed19ed" - "d7380defad7df1735481559553eca5b4d511daa3402ecbc042ef1de0af565a0ccac3ebc7ee460a6d8049ec257b60c7cd93b2009e48d92b3e" - "90ea83baf1be71cf03c2f643c50a00a886ecec90e35159401350e40b217938eaf4ff809ab138d9003c16b0a7d33edb5a51888a040f2734b6" - "c4e2316d1eb1fd101fe634b8e3422c778edbf48555df8e70a4d2f6e6e7723b629d98b7a637fff947b04e95acc6340b595f76e5983b22bdd2" - "1ab080852650fcf363b2e4476429c24b9d7db97052c5cd4d39a64bc717f8d55375566bb27f178ab5ca4a114370c139daa40728e889879aaa" - "114523d64992865db285dad985a90eec7cf77085d57d426c989a7a0444057e80044580044545000578b52b400040066bf70a2d00010a2d00" - "03145196440dc84809f4439577801801fd4fbf00000101080a7e069e563740bf2ae5209c3e4e6a2203f125393c7a0f4936dbebea1b90c30c" - "457ecd4663312c379e7252fdbcec9b020da65b7a9803f65e65dcee2cbcbb3d44e0f17ade7f1fe38d77ea29c35f35d68518307647834192c6" - "0fba7966e179967b9c43de7d4d6b8933d3d4f9f2ec1f6ed723d55b73c0e2fe86e3e162e691347e0c66dcf86c002352662dbece56e1e0942c" - "ffebaea229a07202ab83dd5eb09c1927693fdc0538aa22d943f438351a8991e2098c782396e419b7635cd2cd52047297568e76b03d3de0aa" - "67417feb631f558278aef0be2420b14bf295bace180545f3aa19b1a713f0d43d71a67e8e1c457d40a9ea95022cdaea1f033df9017af46011" - "f75547e8703ab88a73e182064ecf7952d2fe213c32fc91f5b70da3254c613d3e58a29cd27e8602634d1191d4f835731c0e0890239a24b8ef" - "276d6fc8b4d42b579b87fe1b560010d20ef986c104a33d5e0ef1f1e20d347a4af55962820c3dc02bf74342cdb297827fe847b112bf8a5d2a" - "a47fb3a445a0c4c5c6bdbb3a7a94b850f81005e2d2fcca7c1d43052c6ff12e707c24491bae3e048a124785cf3e4eb1d0e7b5e4f1f923134c" - "ceb6f59a701ecf219584778416ba30fa256667bdd6729a999dbd37544e2e3a37c82cc10b3529cc4aeff3e61891b9c0f3b8445db80c22a914" - "8d7d87aec5bbc1e633f77b1706641136f97645abe8afc6e7e9da2abfa21d61dc151a258ecfaba01a4c1710270445222857212cffd8f19153" - "b6a13360507f28cd259b4476295743f15c8a4f713ccd4b67ab6ca557aaa74fe235e803754ab5dbe8483f4262baba08c61ec4c79be0342fef" - "c759b19579eed014dd633c2010fe77f07d276ea88438f5864b2873d86bdebf087493181e6cd61114db9606c9cf5fc2c7a3a76b3065dfb4aa" - "a7ad69c05f8bc75a5dddabf354903c78fd2ddbc16b7b7f00bf3583cf31337f0d835ef36e97515ea34e278eb4a65c3a449745dae72d974404" - "16438eaa2adb941eb6c727eb6ea06f6e21d72878c5d5eb2608524b26aca64e3b64961e446b52a7603759f97d499da7b15d8e596a486311e4" - "1c8c3fff579294bd417a17fa49f62595647b9cfd24a3e83c3e3e4b76a0f3f1f3cc70808bc71f8c383976d81012a56d9e1fee3b72f8f3f9f5" - "02139c47bf39ad586b7fb4215e0ac7190022976e6640d81cd58dc90ac90c854cd100e58605a0bb47613b0484faa16b70744ba0e8837e83dd" - "d5f7f91ce102c98e550a7721565671c9ef754618e098593f31eb7543b38eb93c2f757f5d2b2a81e4c06ff482aff877a8c2d7c18c7bac754a" - "6de8b7c9e116887fb36a48bf401613ada5c57acafe8a8c90cd6706dd11ac95bc3c6050347b610bd1609e4247c43860db8a0906b3741a7bf2" - "ee60bb07c415cb4c1beceb796ed423292359be278f1827170adb019294a2a4b863115d8961833ab21647a4c3360bf2e39d928974a3cd8dfd" - "ccc9a1bf86bdd46546e246485122baad3e25628764f188721826ae6a27671b62ab43faafb15d9d8da86459371c170a2d0e0fac711f813ab1" - "e54c760600055c55f1d7e30f802666db4d7417ebacfa8290070edfbe67db9a85597a8450f9f9fd0214462b14c8b0db88074e00fd9c4e0acd" - "36e8099cbf055c7614ba0b591e8497f1682657d3c674910d59b0735fd4ee728201dfa07ff9ea8854e1547598ce4bfe25ca4df1ae643bf971" - "0d4c8cf15e24ef4e4f0e30486e678060822c86579d51d15c8c5f7d96de05f521e87b39e45a3ccba32d97f3dfa7377f09ea87649c374a4d8a" - "69ca8f8aa4132e81e6c3ad3dff2093809eae7e65fc89e11444c0797ddca6914a2855d51da9a05242c90d8302c56507d0c3e090925ddb3f30" - "6671e62864ac042aeb6252caf6e4889f40e5beb2c9823c068ef8a0aac5944fcea510ecd72c44057e80044680044645000578b52c40004006" - "6bf60a2d00010a2d0003145196440dc84d4df4439577801001fdbc1000000101080a7e069e563740bf2bf7174d901ae7e3ec7acddf31b296" - "464a330426077ec425efccdbada85fe11248cd72830721aa70074f056fbe7b177817d846fe675dcbf77ccfc372a3afae22d8de28074e1c01" - "fbca89dbfd449ad291f300949aa9825f98e28c4e9bb87208aefdbe6743b35dc54658153bacb07e57961344147477472f262e3c6cb95f17d3" - "8bce243695dccab329d2ed8ecfb8cd0647b4f92df286d073e4699e7732f9eda64449d2a3c6168839d4015049a732691943a2279169f11bd1" - "a84012d2ce8ea05f5278f8ad5f355c0bb3ea9ba9eef3bb574c579bb4f3a0eca40bd3a87c182027d96dbb29083868f50038fba74eea5c8e3d" - "4b576a4f30e1ae942f0168423dd44b049d9f20533782fa9873aca6641b9fe2069adf3cad7a5deab9d4c362d5616a57330dd592950545251c" - "fffbccb8c40a5f8305f313d7a9e828922d3f7eea3099fbad4138f47358009a4501c7121bf9f2c1b115f5358fce37f76470c9e19c5decb971" - "ff31235f7750a240c85df33b9117bed8ef9adce5bae828ac29f717c4742fad20e5c9da7d9dfbfb92ee44c1371fe4cbdc2c3c808ef7f6cec2" - "32931fe56d84f82c93d6a48a8194c88557d01d06e4b30ef73e37be083edf073455c57a75887f6498ab3123d8b819869f3c93a33744e2f7b7" - "f2db3d0d92420d69e3fad3199fd2e0509fcc8b1863ab9235726b2ba69ddd6d18cd75ee17ed719c6e904bdccbed17e3c4efea509c5b5ec59f" - "56ef8fc97f9ed2cc8f1f4e254fcbf19d040d06eca584f5930ab5bab37fdbba7726d4a010c621672f0a75736fc65ba8921f73177f79e8eb40" - "fa31c2dc5b1ac40f95eabf5f7abf65c77ee59960c2c6cc975bb420751bc9b3a7ff00358effdf8782aa57afeefb1f9b227e0a4809633dc37e" - "a9ac7effd0868d3f51ba5f56016c3fd31a8284ed220bf6c6d02dd8308a70dbbc65cdc24334043cbe612252ea8427cd0394275d11cd3ed369" - "32a4271173c8fe029a9b80c7751995dd4585456b95425cdf29e8f6055356a1790bfb5b6ac88c7d82b7d78fb266dfaebc8bb507ee0820abd1" - "1900da3b20ceb32d553a21e6bb6ee8a17d2d0dd2ad5f288ac681ab45620352346a06f32cb17bc3c8fab4928ae438f2455dd9b9066d9b290a" - "da6f7ae5fea9ce10b619ff9e1e6dc1cb021002fb0192cf6d731855c14e88402dde4c20584c7efaa53c06bbc97f9dda127b36d8a9fa8919e5" - "583ba82062abce95834727badf7ca27202e8ff92630153f7b74586ac1bc09e805b73d2d3387dd69952ce5bdf1278761aa1ac5cf68a679f6e" - "a453e7bea383c10f8522d42724794c6840197d92084fcf489219df3d8a94db5901278f74bb0f02839a682ea3f15e831783a42a4fb090e1b2" - "63d8cf80ae3c8fcd26d4679ee66e5e7165001900c3994b8278f733dba27672b5105c84cdc38ca8724f327dd8edc87d3aecd9532f2137fb8a" - "1a34b44a459f3a93c32ab76b1c2dc5641c66a68725e2fac5952254adb7188295e1ee58e8cc429fb9b2f128ef87a241c36790d92c21f20500" - "c06a95ac9071b4b817a75d5a23226fd267116d43cafa476f867e92ad6ab623d7e39c3cad1b0dbb37e3817995340c499111ce079762326720" - "89647d026c9e037a483e0da6d32829328eb771d9c34f63d55b8c79f0e21d0111fa1e398d9dec507993ad3de2cb4707af223793d33f9ba326" - "f53fb3031e48acad90d8a38293910c63c2ccb5bf76c691d45bed94bb039d3b01187a7f34dc7c32dc264d902d2cf65cd14c477acdfd295dbd" - "796ceaccfb2999e8526bf91913c0663c4c13cb459c650b7977af50b605049f67315e40c96a592d872d769926acb4bae01b837ccc08577898" - "a095c57515680625457cbdb268a866f3d5671575ff90e56b7c7da15604f6cf6126c50d921323482aa899f8eef53144057e80044780044745" - "000578b52d400040066bf50a2d00010a2d0003145196440dc85291f4439577801801fdf27200000101080a7e069e563740bf2b91bd99b9db" - "f617b69b6fc454a86bc9b0f467796769a4af16f1d44dcea11f99ccc51d14c814398f39992fff7f83f06adf882fb530c8d2fdfa3f97f015c6" - "0cb6078cfeff4589c9a5b6dae80c5331147aa99e0628d3c3bdfe2a9b5beb8037cf8b785f2ef27ea8cef333af8b32d014c0721cf9b0509113" - "94820e9622627399115f429b9ba489124d49c6ea9a6d5a815a80ec9a3c147bf6becf471f5da05c4989efbecf39d0176f0a9dcde5e1bd4751" - "90bf28d8d52ee6be08e9ce1e95414cb06d4a56626559f5dbdad6804c6ce00310f13b44e3452824a39c967ee1ff2527c3028505e16fdf4887" - "6a65e8d21cd007e70f2bbe7922b708b6bde5ce137a099cea6d40c45659ff461e5c2b95691884d595a1cdde8ba75a5ede653f5a865317ef34" - "c828b67ad19ea6bd102065c99768308246b6ec31fbac60f5f5329b91325e69193b00204433b84f24fc8dfa44bdd35375a2bf895a119dac03" - "7df8487b6068d60dfea0e374dbc137e8a6b1c8e8390b8eaf8c30d2724d6da6ce23aaa888b5c930e525ef80c3c0a1865aaa010b0e4af09f44" - "5f38fcfe78bdc0896720a23cde120c6d7479be7c0134766ebd46054ecd6be44f160811b5b3cf243dc5a9639f6f2686f311c602bdb1061229" - "afbd26d185149b9c23ef13d8c15f93fe80ad84c76e6c0dc5442069bb3cbda516d93df26392d1fe11c5c0d0d85c614eedba5c87b79bc87ef1" - "c1f4a4c4ec5f04fc5c320b1790763323843f682731b8c9456e998075bf0fda6bab3f076918f7d4a2a8b52d08354222044781071e85e9d9fd" - "c06c949eafc66dd730c6acddee834b8227115043a59b022fc1baf275895e4ccb3d73933f32fbadd7f87fda314f5ab7a9c9ebef7a8837e53a" - "29833d1f581f1fad5a7b4df669cbeb9f975bda108b47f303982e4f47abe32abbe7cde1f27e19ed2283fce8d676f3d13c77cefa617654c7b7" - "03f010f3a6f9af1e4362c097d642ba1c00ccf4057db7994e656fff20d9f689bb99415dbec1853c0c134822dfd691c78ade69cd9dc4c12734" - "4b717a1eaceb33e3667598464bc4031ddcd2340ea4be0d0ec1c4a25f4e2c336660cc97b2b7b7379991487f9cc032a86bca3cc5f4b5e409c8" - "3de197680e81dfac511dff3a838c489ef27eaa17b3212e77893c71e817b630f0aa46cded93d9bc6983ecd521d066bf8c1d95d9154c6c6aef" - "f82364be2d0cd1323e41da7c4183e296e7d6d1d0cae47ccf590b181e6d1110e80ddff48fdfde75408808e9b1aab0613f904d7b2900d24baf" - "e6f868552f209890cb41e05405e4963e7e1e2421186de68c3282d70f60d8624dd235656dea407ffebe78d7c1920291a6f3d0028583dc019c" - "05cced92ab04c3eec22491545cae90911cbb27cac3b976a90c7aebf8279e858d025d87d381af1ea1cfd6cbf440b9d7a415e50135f4b2637a" - "a255cfd5f774dd47594225ecabaa367cc243c12a8dbecb075e13baaa2434dc7a4eb300698c88b1b4df751bf9cc2d0ca7966c6d6b916d4b2d" - "25637f24e33ea2cded4b26b1b7217ea4e8af489527a44073f39a7cb3e4469ca78d376f41aea63404df307900e4a7db139cdb2e121c303adc" - "870205c70ae12b516536ac3be02ec395af7e81d224562b349cb00447ea98b946704b0ad83d6f5da42c5934555e129567bc3a850aaba35a27" - "226c36068060b186cc955247e87e730c4c990e95a91b3a337919276ba5b1c56489ce781808c32b7e7cf9372df51297eb48ee6ec50a38757d" - "d72905cd5c70489e3b810ba0f7aee833d5ac31fc297d89e4ec49343eb473ba830cab6538cd805c46483be6d51f49ae3e9ae15bf74b915960" - "5a21d411d55e0ef8da3badf2f75654aeb959d0064e6fa3b11a49aaee055a82c205caa8385e170de87fcb139ed745660ac14a8a5e12d13d44" - "057e80044880044845000578b52e400040066bf40a2d00010a2d0003145196440dc857d5f4439577801001fd3bff00000101080a7e069e56" - "3740bf2bc15381330515055671653bfd56093f4e59f9b3b7708ea18c08dbd588ed5f18fff368955eef5d87fa5741c6b6f1dae0325ed97a29" - "183f22484dd9e6d30c1110cbe354146e35b0577b23a505bf533355be5c0c07453b63b73578fe3d40e585e7632b8d24adc56756cd2a7f6cf7" - "e4c77d4f82cc3278d12db44f7414f9dfdff05391bed41c0dbca4ed86642d98d34809e5eb3963660ee1c4871f8206bb6411b00f4898e29731" - "45acb1d689d352aa23b1c21cce7081389942c895ff6f0a086b7a829d8fa57b721aa7b6efe4d0c348d70be2e6e1d8416ce322bd5750d6b681" - "712829327ebcf0a9acef63844de8d4a7e92c0d908ad2b100c115ebfd91d468eb056876123dd04378678a401f72965629273194fba3db8f7b" - "505165da35b997a4eafbffa0f816e15d1492aeb1ae794dfed767197447a75a524d69d96046d9c45bf90eef60adb73a0cd0c411900638f217" - "00e65e30f3d0e922bbf13624d5d859a9a5f4252595fbbea7ae57df29628107c7ae58055e6f277922f7cbf957ed3123f0eeabbf0243148a13" - "36cb5a8786e1fcda9fba3128e7e35eedb785e69556921c44960f3c251977003579c8aa4b205e219972024b4f47f6b758dee2611fe350f838" - "e55842bfc2e10eb3ad7897422a783480c51802d88d50611797690a79d9c35c6ec109416ed9c5d9a92b539be92deb202a3fdfd770caf396e4" - "811b1121f9490c01eb60112a43dd132c80250b79f20f5a8daae65e1d40fcf90f2f5163423e8a71b3df6c9b21d68c83cc967cec96baaa36ae" - "8000347553bae8b1b098f729bf21377f571c57d5800656f17495c05793a8567298f18bca90310d5a242a354837c84f8dabd227fc9566b787" - "0ef08babc56bed0c67f55455286bef55cf35a6e3998fe6fcc97440751c82ae26c0bc706fab793296147067a01b009ea23d2646abb2ceb3d5" - "7245cb46d304162b13e5e95c0586d1a90242e3626a5979b61433149d8bc42869a27f16ca7dcc92fcd0240eb76b997f45631ddc2319b05e59" - "f5bd4b15783967c4cdcf9073eb3c3c9b17fa7d27e74c61e8fd98dbc9f92d8621f2a0e54b977ebeb3d0a2de3fb77241d20fb4f0c2d1372542" - "bfe003486cdcdd45e0327024bbee064b1fcbbfcbe1f6f3c483e77d4144128edcc8b28385e70242e7d8c386866abc5a7f0f161a0feb60672c" - "c93f029ff619a33c84fadc3c1832a15e9f3c470f9d32c531c5d34d19839a01e413758deeabc243e31892fd3f62b0a3ca11acd9308eed3832" - "3e464eecd2b1a6326b7d799a9c6a3aadfa2d74fe3a359fae773574c0937d19242cc44ff0fde358a69690e9dec48261ae3b14b0127737f3bf" - "f707b90881e10864b81d98732833f7b4bdb9f42a1d933d3a10247c7e22021298a48731ac02f065545e6eee14e70ee3b809d84be48a986f75" - "7f229d82e3a841fcc1d306db1f2239d7c59e99c6dbb429f93ad6985df130599f3358890e3fa480566d89cea6a8a27fd17fbd5b03cc97026f" - "59ce132a5fac9aeb67c352e0e8fbe85da4994af2d8c79ba49ccb6704231459d2ee3833db5d072466c37d0ee96bcc70d843ace90967b204d3" - "c534ab04e2f54a3d48f0f1a3161e8ae31875811d51ce9eb8208762e9438b697acf52ed5e4ed463e310d2108af5f0833f57bfa85e9e46ebc7" - "0cf164040d5f619b9f4fb4d8079852a79a42144c1d96c25546fe6ccdb115cf5005dedfcf72f85db0454b3786d95dc4b6f02a58b5db6c3578" - "3736562baae5412b0bf45af3dabeab68f208922312afdce0d92e4a59e83b5d9ac077dc065c2312a4ed9a5f499f50a9647186eab8da1efab6" - "74e0a848d7519c6d3d65f572a8e4840ca4cc0f5b471a5d1fa9f20674fbe95b93d8aa54109fd8a3a5b57b9feaa037f18e05a241420ce8deeb" - "1ca08df2e082cbab44057e80044980044945000578b52f400040066bf30a2d00010a2d0003145196440dc85d19f4439577801801fdde6700" - "000101080a7e069e563740bf2b280739aa638e0744a7402ad2fa31ac1bfc444c29030f691403c01f2e0a1100abde7a1f9653c815f5d61465" - "9dc171eaf3f187b04b324bc71b50d1d3f5a6d82ba1b237ce56ffec743aa30d5edf52d38fbbc50d9d2a930d69fe8e6ef473659b548d67080b" - "cc83e5a3759212f2bb414180216744ce0b71d59f29184c9f2ac63accce02e6205e1fe0be020ab25cabed6cb72104f6f6ab0f19fe7ca67402" - "1c44a7db3f4a95436d234510d789c89bb4cb2e25fef301b079bed1b1fab31df7a45d85bcbb17ddff956d06d568600ae6332111e0db308403" - "ec426e8d75c24c4bfb970be95e41c01d4574dee0db362a52b8a65cfc9c42a9acb77f280d416db4a814f643d156f7e92a9adda009940cc597" - "3d83c9fec86a8d8323978cc07d60e5ec268dd3505306491ee9320e16c20564251d5045252a212e1bcdcf05523d9138345aa17828e4095686" - "8dd54d2643b5d8ecacb32fbf4b6ac91b2aebc329bfb40ceb68ebcb9e5b5063a6a8700bcd534eba7c3a9e58fe04d45e9cea6b61338f804f97" - "4bfd4a3a23c8c74da9b5994b09a29fa0d59c563db393cf5b586649b967392bdd8031fe2b80fd82ad315981c8b7663337b50cc59d02ec3ca5" - "34738d82b34d06f4401a41816543d4312e4f87f0cb6c9f2f74e5ae30d56176174f0cd8b8072405d7e223a1d2c304150b230041dc101a9c8b" - "8e5c2ee848bccafad8c42e6c58182a0a6386ac642cf9a772f996f0388a8d5c4391b5845b2a2e5573bb290b382990ad5e42769c240c4731a9" - "a0371bf0d3cae364b11c504f59c56247ca064972c90e103862aee860d79dc7ad4201a04657760ad4ba0adf5179814188d734c724013ce3a6" - "c9129b11a7d2b967a8c0f96414f694516bb67ce39045b45152cb1e21ea5fb99cd11609eefd742aeb38f6131477784119f7f837b490f4057c" - "048e2242c5b07f88766a3d636212a78d80b5cb134c91d5a5dd3f2235cfeeb4f4f55b75999b70bbe04b4cf1efd94c3bc185fe09e38f6580aa" - "8e644ec2a34c26c7aaef2d8659b17d08aa294100d95fbdb9c59e372b012fff1d36d55fe8b0afa21ea54a59c83d8822f9faa1a5532e3beccd" - "7cab2dee44916ec4aa0133001da6f7fb39e9ce920615ec5255b9d776094026ae76f1191734ae849a9e36898a3b99dd775d793d4037c89f49" - "87e4cf39b5b0d81a5a4a5bd2f14d5becd602696ea1b9ffff0814677a25d78e664c5c05c3079c9932bf828f318aca1e00c415d18020c5f8a9" - "a0efdc51b98548d054df662f7caac044ba37f3c6fe55bce99147e4aadc6f3c3826b7b0dff349da90f6a11de6ddb540122477a673d2cd9c60" - "24cce8d8490008b9f3f657ceac83c179f878d25d34dbf26fc427c523249da542327c7f46a97213a5ea65172d43f34dfc517416ef9756fbd2" - "1660a28f256036d86834d5a24904c584991e42db81266c4012622a36ad6acb60796675d85a9369ce648d397084fa1fa54271b5a6e068c208" - "3073cb5054fea4bbb231b48928bdbba51bcefc55ef720bd5b35735cfa48eacefc5fbd96d3fe66d90754b26b006faea606912cd88d8f53153" - "02a3bd82cf0dd4b5e35e9764681360b55f4d066f0953be3c8bb03792e01bec7d06ab20a074d296bb1ab025d217cffa2227666d1c51ac021b" - "5a72683f7f784d026216754fad6e8c3597bf922d9673bb41163e828a20f1815096039b39664e45a911eedc8361832f622c4302582518b467" - "4aa67e15162f6a0d298b09c6c282ceadf0a1bb969cb17d25943702cc142d4c80be72c5b9c0aa770119f4a8f4f87e319f3d93b22440392fe4" - "979787c02d65e690776a0dbda4c659edddbd4dff18503ffaa23552831c576493bc799d6165e7d9d1665a6e70e0f4bfce7412185b3fa3a89f" - "30952f32c9ca4322d4e2a4871f01412ab844057e80044a80044a45000578b530400040066bf20a2d00010a2d0003145196440dc8625df443" - "9577801001fd252900000101080a7e069e563740bf2b3e3cccb1808c5cef03da8a8f678979754acd488c6512f0dcbf20585dac32534d23b1" - "3dbf27538768e9305ef8d32d883c4ce010237d8c401e8ea5923f62668b697c575956ea9448cc9b83a4dbd4b72a2f6b6593a3a7d700602c5e" - "76dc6b7c4fa992c714ba87a8fe9a5bf53e72369055d34093bae7e6136d13f293c438d5f62d1c19686a4cc4919a851db604a2c9f5407d2937" - "e4cc97d52b5f69cb6edd63358d02b1b15b12849978a9c50da54618fb617c9d706f85957261d69b9ae841c3797b73808abc48b4f46a34c038" - "5436d13253552668bab657eff81ee8482094d87b4cfcfd09c4894d5d77ae6ce6d05f26b48fb271e1735a2e1061b74e63f1868758fd62a855" - "111cfd10766d7eacd128a18c086178e9451822b88577ab1e68dc32ecb60e436c8dda5d97e582c3a5f54d4a77e19a5307f3a6fba11c8034c6" - "ce74c26dd299239db9e85882eb9c62d0a987d79bf66bebf994d91a6d74e145bc15e2e2748a13fa2ef733405f19ec5683f061548305185765" - "f4aefa6db6fce0a3059f2babdafe50c483a6afd3bd1beac674fc91b6db29a9ce06c19032bdfd571d699d1813c82e5ffd84273e7e7978cebf" - "48639aa4ed12088cbfbe70543efb1e7210749ab8cd512dafda711c5b18da005ff34958afd8e1dc6b73ac8fb3ece79019a5d714d1dccc8ace" - "714816ef321f4c91a059abdc33e051ed46354825a09815076673cdd41c27f49c43a2fad399a8ea83a71bba73491207ade9da3cf228e9f397" - "1d7e463856a01313a1543992f756a669654bfbd8b31195572a9750164076e73c13993cac789c2f827432d0ea6791ac4d68ff399c86734e76" - "cc316dcb259b7982152ab5282ff0870fc77f61d4d0a9f82202dd17e35616c3bada28d8b97bdb56645bbd0e05d0bc71b860a4041c6f72db71" - "c26e8bb8b601fff8d971d0428d14c5fdceca6bacc2ffadc1fc9bd3aa6c635399c515d948fcfdce219ae1c49fc797b45f69c002d8685e4e7f" - "09f5b823919e0ee826b83af14cd62a2aeb2631a6162c2bc70da672adbecf8ef30bfcfdaf81741aa7c68bc3137b37b249288fe2b3dc27cacf" - "576de4fb530e05cf7be976eadb5c5a25f10aa121aec317a0c2e873c62cdce76d143c0f4c575c5dae91d319732688fe9267aef83bdfacf055" - "d353c0fec24d5f7670eb5832c8833988384cbca4584b7db7d6b9156e89b1b42d2f90fa2b25fbedafc895b08b5d318277a9e0c80242075513" - "662b765d41312c898a3d0325e922a79b4fa1b403f5e10b0446d8ea72c6411f966da69cf495c8b79d6da8241417c735fed0f50ca41fb6ae1e" - "a4d19ea477955ca3f259df20c525997d238ec8d6b9a1edde0de7e050664ca6f60bf4894d85a80e06c416dd1b16f8bb068581d97494e1d25f" - "ef473b061c1fe3596cb4da02640e1f873abb3d132259e59d32e014b77a6b2a2633b964d4edfd89e5669ed1338abf1e65a3a93abbec0ee441" - "142e99310534af3d7430a01417d078e9e619df5cba25f94c6f2b1c885a34c738dcdbb2d9af5967e4bf1f88f85cf6985c580d6f714eeff2eb" - "99c27d6878bbdf086593ddb1597da886aa34c2b934aeeab8745f1e89182e4889807e5ae0fc4fcc84901d4404ba0ca7667e0553f7c0e8e792" - "b6ba794e55778be94d69503144530c0023a7bf5e2a38ed0101b73515c5b5840c0e57c12b9a08add4d4707635c80c4796e52af51f30411775" - "ce1a1a2ce543f5e1111f9ed48fa6a43796573bfc1cc5a392cbb51fc5911a39f282c91e677bf7ab82d587e92b7c6ef048ee2f9ca1a0f95133" - "e540baa131bf5834fb5731353baa9e7317792cb2726fd0e58d96500618d909a4fe5f12e8a240b25710fe23332b6780000126c0f3c5bf065c" - "ca030ba7c29c5695a34136a31f430cba3fd5ad7ad2c63d9c245d44057e80044b80044b45000578b531400040066bf10a2d00010a2d000314" - "5196440dc867a1f4439577801801fdac3a00000101080a7e069e563740bf2b76ac9734d1c3b4f1f88e7af0afb31110f2fb31da39ab1f5f8b" - "470e6b48b736fc6ae6ac85a72bd023d4044dfb76e696809400e76971903d13ed52f0107ee8a7746edf0d86c7436ff00d1287759d5be64f3d" - "86dfdef452d810e17a60749f02db6676b8b7d1ba06f884c6094654dbe9422a4887e6c9215622637d05c2037dac929f47d05d479756aa8384" - "2c208625671070ab70b518710038e0ec1a136bbbdd536972b462562bae2bd6bddb43a135da51f76ecb699a3b26ce890bea0ea16a16119973" - "72dd399a2a00ae3130c29d9ed88baf242ba53554b8e033a72b570c82cb1c54f02afd35924e9d0e1d898cb3fe2964064b479d5b592ed0f60e" - "c93c70a0ee4b4a5e49a32c5a12eb78f13dc3c7be319d5da1149fd278107e4f963cf978704392a7ff70209db75a0a2e770ffc27500ba79a99" - "dab53b81234bb28fad6901bd24f245c7b1eddb4df03dc6fee69dcb766a2a9232eb8c27d1b00a6ffc8d1d022192a887cfc91e084d3431874c" - "4b6a09b0cc76dd609309f5082ba2c526be65c42842dfbfcc90ea89000c440b2fa1d277d606fbbd712b5b716a3d78b278eed6f0e3cb274c61" - "3704085085f3aca5af2e82eafcdded1000e75193554421286fc3ad9bdefeb005cfed5ef7811651003fed0c4c06b594b9134aa0aa287e59fd" - "2d5a81cd6ff74e453782bae25cc4c8606954510081f2fdeadb67194c55f40041915249c65b5fa92bb96789ade0821a60102046b949b96eb7" - "adf876b0df7f4a51146ea79e51c621acda6586116095a8c8eea98c6bab5fc8985a5c84162139ddbc378753d9617f663235a0fd762cf03ecf" - "1ac3ef3201520cd12ac0f5233693eff7d8c448760ce85420d38f18bad7b37c5d899cf47674f6981f01b12d534c9a7c1c6ef63584a6736045" - "538b4d69713c6851e00b3614269e672815dce462d67f1bce0a3abaa926722c01c011ce5e0a8737877dcbaaaabca2ee1a37680bf260b308bb" - "bb90c729e646a271462a9c1b02e002238d15f8762ff8683ec7315aa6d6651ee8d00ad8561fd8e32dd97536b33b275c44662d050be81d814d" - "08050075484be95456ff19df9b0b38bfe45f72e23275a954cb066df3a063e7929e22bb824bfeaaeaf69f94ee82284703fe451e1bcfdf789b" - "3b06225db816df06869ca0df026ede41739aacbaef8c3d039f293ecad2cb78b8775ebcec62c24b42eca4f7df537601a275d0f575be06748e" - "52e2cbdea20a76d538a4c7a92fc3958c0beabfd972da3ad949ae6a038e8589f6cbb3ed4112adb9269d9ce6fe230d99b6ccc00d42877f51b0" - "660d84df11804ddcd8a11068bc2d6200d32c72637dd0c332e4ad8fe3ca33f3a5272310acf345e5828244c577df6441a89407fcd62c69eb3c" - "5dddd3108f29979ca9f3d72a4c30a73dcedd241cdfa1b2b0a7900ff70e2c870809ad6ae7085a455a1fa4d22a7c625500880c14eaea791775" - "7747e23775b4a594c536e25e47911fe215592918005ef74aecf98938ab67e3b3838f4324ef38ce43748062f93a74815bf4e2f2f50479e0b6" - "16afb91d0bcb57b3330c6e8df718178a4272e651eabd8cc620187889cac80466c9d6b2af7bfa9d1b045a9807bcd8413e282349637c214b84" - "cec1a6aa31ee0a5a287cdd1ce0ae5ca3242efa3bc85a479c53ba132078e5157d008af6d6bc5c3266ddd6a7f2fdee30f63e8f939862e6516f" - "373a06ab8bcc5e3ff7fcb04fd00c1476775c52196f4f9733a2eb8f201d9f1e1c5b85a735a0596352d2643c70b1019a5603a33ba7feab8532" - "2971c96e54fdba1ecc139eac082ca8e390763b26ebe421a230efcfeed2bebf2cf5535793dd947c2e4a674f835b76c6e546581b664f3d614b" - "9ce14ad2fa303d92b1a2f555e4564129aa29021aae4e7b3fccef2aea64ed7391c5a4bf44057e80044c80044c45000578b532400040066bf0" - "0a2d00010a2d0003145196440dc86ce5f4439577801001fdab7100000101080a7e069e563740bf2baef91b710cac6f6b8e915a1c2acf5a0d" - "7e1d74929f0b08a61b47e3b5f81d4486bc29d333e747bf10a85922422cbb3738eec3bcbc92cb13a3fbdb54ad81487d6e27600600317d09f2" - "3ba1e97acde6956d2bf67a335d05edb243ae541ee9195df66370b01783dbb8d936be7a53761c11679523d9f52704add361de53051b60ae5f" - "2dc21740c5545e35825e9efe9a1a9b2441188fef2870c781f4f11b7d5435ca4012d57e781d2a84eb796fcf73c5299342e35439ae7d981442" - "a9fdbdccc4f10822f48f02ca88abdc326b774277fbf932dd79319e90a66a058999057ed90b5318898ecb9260bf3ad221ff3b5f441bdab3b2" - "cc67cbcb8b4fd3888e3918781f8cb3c41d54fb6f7b1140a3d73563b77b51c51c7d46d4a80aca45aa6784a357c1edc60e156c4b7a5f11d6ae" - "e49d66fe6c90692226531ed0a8a2fc572744a2a8888e9ce02b540397b8bbc6cc7f0e71171e11e94fe1b84dbe8494bbd47dc19efae019aaf1" - "35eb74ef56c5e641535e2f93537dfcfa8f694718284c353ef186ef26f1941f7b7720aeaca2f354ff899d6edd248ef8abd046e2d42ee2575c" - "bf62c32dd66cce29b168e6ab0347ac3cbc20c159697edaaba86c8afd9b284b37a3f6baa406414fa8bdd3fd8a0a13c0e58ffe8331e06f2ac7" - "eafadd516ee84919d4f5fcfe9d26883cc9081d31332ee31ac84f3111f9a95eeb2481645389e1c57413add83ab7c9931f46590820a9390275" - "bff79b3145c8e2658f183b578faf99ae588828dd7a930dca7ab4c2761dd62f63c06961b40019864dc2bbf16bec673a0b8497caa195ff3c75" - "079e510e0ba56236ab3b54530430db0471ba4381e16d8fea3e288559be69b44d070e8318c78f766920ddf047fa5d4ad04d17534ddea0129f" - "43466ff5591c6fb205d7d66213226b69aab3e6acd3d5e9dbcf479288c93f4a804af76cccf852883e42eafcf6ab268876ac1b00238486992d" - "fc042798fb27acacb0fcf7d016c36a9f0a1021bda71df49f47245a67f2ceb7804dda0838bf48f7f60a2812d8c871b0181416eb7bf61ece80" - "7365341e36800f8d5a05c24442dd87a8941c5da73260c3fb5d7010be209701230665172fc6a9bf0da67fd8025835079bc6376f01b6df8010" - "2d1cffeb188a1271ddbc52dcc859e3df77face07ff95e61e437328dc6055c055fdd9143b0194a03df8bb67dcf7852b1ea2403fbfb2fe8a99" - "3a572f4eb385c33f90f3d9d1d78d7db906f25dfca57d1b00c4fa49848516ef75d8052f2d9b402250abef1002da37b45d32e5b3f14906a9a0" - "e0c112eb009dd7201ec1a715be573f8dd542badf7dbba74255d984de2b422a2d7153f7eb8bb28a79523a3b5e022f0d21f283e13535993a23" - "fd5ed87d109f4eadaa2b2705d3ba14aee14cee99f0dca853fb28952e4befdc6c2a8ab45eb046ae036e06e93703c6a2efc4712958d9838c2a" - "959b88804d098d4b0b156c368e5618ab120f4a96928904fa292c03f7fcd7ecd762864a41305dbaa0b0468686984d49d6528323f9dafd0d41" - "06c80af2b090f1a77c20b8fa9b3f3f217214073a4948059448e6356e4386b37705346f7977873d28859166204adbe56ea9e2580e55ae61e5" - "cb0881c1612b5f275e96755d6382f3a36be041b7f56827938ec5e06c92947a912904074c41e8eb820d66546dca3665e431579f335d850c2f" - "cb73c6517357cbf1cb30a26ca044f3e6646bbae5aebc0809996611f846d9ce248759190eebe68fed2e113ac70da7188f4b8135057039954c" - "2cfab38d52bd90401fcc93201427c00d1674aff3e61efad08c5a4a5d0f108a7fd76d61e5731c3ee68330f6e4f6b6ad9886b801f7521fbbcd" - "f2f2edf73882163011c52db59203b6c10dbff5f0f4292a57dbee22df7477b73931db8fce04da8fe50ee9e36244057e80044d80044d450005" - "78b533400040066bef0a2d00010a2d0003145196440dc87229f4439577801801fd22f400000101080a7e069e563740bf2b8b57faeddbdaf7" - "27c92ab4ef51ad465d9581f19789fb649a2bfbc99dcbd4a07e2f0a1e91a45258ce80ba0a3787af5ce2dced44939e9ce6f56860b3e69da4ed" - "818e283469ee03878b248acdf4380aeba8d1aa09c4288af8c5b252d7299d17f415cfe8ddb0ccb7b9565633c32573a823eb6bb623c6f678d2" - "5a5d8555ea4eb916ca67f3ea236a9bbf69be53fa88aee6ade14d809fb489a908cdf7f48cbb190f6ba83937d3cc747d97256da91c849e739f" - "64e2a01f692632fc2860317fa76a08dae7eb30f63da8f25e759f364cb4e49863cb7bbe13323581ac02ae09ced96ec1ed384b1786e1f9218a" - "01fb44a1900c627d9e56ac88f2b9e53e3d741b8057a0535ab8b59b80d8c0a9034d641712ce49fb2de7fbaca36ceb12d00c243b26607a92f4" - "0c26034bbeffa3c1edfda9f4840a6ba83615577f084763adcae3a420d8570b521bfb04323c9a9d06ecc98b117e9e5903b927ad9fd5608e26" - "180ac7120bb63131ada7b260be8f2b099eb6d158debfb3117ea452c17c7089324b1f6828c5f0c7652fe3d557119e98fb22afa36e20fbb570" - "60fefa623db0fd26b5a6c3d34e2a449f5ee74f6a9f49d9174ea22c385fb1f25e2eafc3a9460210f1fd0447289ab89712545b4e6f322831b6" - "0e9e14e359c3bb7c81c446faf8f15a7ff9a984aad44724357b545ef8514e4e437e0d4bdfc43688701166dfa9cb0928407621fa8ddfa243a1" - "4f0486c00e449a7ce8e8cb98a41d748727a0bb0360156863b39b8d01c24e75afa6aac016b281e50a62d294592d15b94f070009488751e9bc" - "11f09a70b1ce11ca1206a6600ce252f0dc19bf0a2fe0a20ef7dda172804b3b565c88bf889bb30b591e0006e2b9df2dba537ed9411e51ee11" - "d6e3bedcf2afc4799e72689996ab0b1ecb6b080a97014ccac6df014691380eda49e30567eb7192fb99f6b0b89a2da57dbefdefcd4e13ca68" - "4922faaaf21a2e7b719377699a89d5a7c65da9f21a49ede30dfab7305bd872eb74190c69766e8ec301e42bd909688fb52da5b75340b07277" - "72e2be0c075a3dd8ad520b6dcfc20d1510a483bd11f912893626e599232ae9f5143faed2b78addf09e490e06d2d1e5be82c11b7104b89030" - "40e2ecd96e9cd67eb882fb19b22d1cab3c585e27e9b150af90ed5a90fca2531101ea8222f1cf8dc2fafc54f966b27a83940a3366f5d50543" - "b6ba78f9fbe5c08893a9f7dfcee7610199c195a5386d0faa453a4f56a82bb76678e4f4d1be421dbd2c72129175022b9b72de942b23d74866" - "5d32a5593df35d137f1558cae59e185120f9860c30616ad1696103676773adcee9c6a618b30fe3d674fe436a5d9eeb41dd6c725c589a6161" - "45c87412f0cc6b07b1f68ccd591bc4bcdd8cc2cd26d1b6e1db9358c1c765a75955470b0347d8553c111a21d1e44032feef3000dd936eee91" - "27a0997c9547df2e9f1fcac6a71e39b632945ab425471f3712eb010858842e89ec268efcfe8a8094ca5b6c138136f568fa35203000142c0e" - "a22c210e42d07677b955781330a3ca67380071ab25059b112dfbbdf75b61050782a98f7b0c3db8b485d5d63bfdcc90694b12e6771b059bf7" - "53c0fa7f6d7c4b4582b73e6b26a43ad399f4f07139a203156ab90825c8129788801d2539b237401345a06c9d8ff8c8c2135e20b6f5b140d7" - "5a9fa46628daabaa330d23072f2625e0f6d939c1672fbebd818f0f151be2b22eef7f32bcf122e722184a23d58b790adb810bede3a87981ff" - "951ca2c5a8cc95cde85e088b58958d3d4e24806cd46f88661fb3240f18af4005d5eb20bab5f83a8a6e98e59e4ea838f96c8e52fc9ab162a0" - "52c77e55afe7cba1d3d8cdd87b5d16087baac94601a97bb13a29ae66a73f808969a1d8051d7c26b6a38f8b8d5d57c075f1b6566fd744057e" - "80044e80044e45000578b534400040066bee0a2d00010a2d0003145196440dc8776df4439577801001fd9fc700000101080a7e069e563740" - "bf2b4a2f1cf1787a5a3675635ac42fdef50f2788f73fda7ce6784c4e94114d497842307f92a80bac28466d0b0ff2ea0fb3fa2b3944926fa0" - "c4f2157bdf1df94a610809778be3450d5bdd64e837d9af4216d54a75e1eb2439dbe1e9b2d4c327a6df3657fe88368b8d199c691cae944c9a" - "872a2d00c93b96ebaa26106c6149c1e461483340bd6851348f11db933cd483d810d3625270e28df344b1179d3cbb652715dea6c135267423" - "d543b37656672e60cccbadba3835b339902a9148b8f671117bfcb48f8059a5ed4ce28b29ee28bb0297684c197729808b032efe160d2b9228" - "4f67945840aca0ccbce19063096cf8c78b6cbf8658f9f94652014eb625ab9f0619905ff2a510aa99be9bbb909ae7159b04e933daafe499ca" - "c144ef221343dc081ef1e140ecc77bae5a3179f5b519ad95ccf94a0edd0fb9c4d5bb66f6b59c6d7067b219e496d5b2d1196ce5b59d003cc9" - "ada070f4675b66995f9b2b346217517d0b98afac0fc3484c167a15a5b1d21db0c644b25ca3f254a62f55735f51cab10b7df698617703bf99" - "95083886d233dd953d0c3e4fe79a0cd0c9f14cefaa9c6ee27ac86a8f67fab1b74096d269d264e3e281aede0d63eee96ce5d11e5761df3ea2" - "01eb1e27c703c7988d22739c81dd2336202b8002181f04a38c223fc1a1426b47cbe399fb5af605672a94f486bff0f96fef3a18583c13732b" - "9538e61403d17583cf3766b5acbc57d5b652d27c957794c6c9f9ab318ed7eab146b32522f6324305d03652f3cccae64587be8de2538daefc" - "c9ccd9cabfbe527478b8a84bae4316c7068b5b481ce17a42bd63ee7f15cff1da63fba07b4756b82055efc50a2e7040e3b45e05b05ad8d7cf" - "4b23aa9650140ff996f1506e4697ca7fb0583fef1c1d9a9528b6bd508b6e67e9d1fe9c90a760f350b88ad6b813f30182666e16efa5863923" - "a91446b971aa5d6cad839d4519658d246791e281adc8e563116937319e12902db5d576e68146ecc70860eed942035820fbcc79a0fff53dcb" - "9a95fffd2e46a2307d76388b3eaf15e561895d02eabc82695c23a661494a3cbc52eb5b029b42014e62b9d92550b330d1b3a20ad80d5f978e" - "d99b2cdf8570eb461f6858669cc153c8fe586cb6b521b6422c481057d2920e50ec73533bb78e093e726a92d43f790585e74a9e3b6789a0b4" - "b4fd36826e8beabe10428da1c051939851c616b69b88fdb10b0a9c161b6bb23c659bc0f989b39a751f687b4afb659ff9039f97784183c81c" - "1c8828aa156151afd1105fb4b725a0129b275285412e5aba66fcf90c16328b8893916b792a84d38393e48fc966940042fc71ae9ba936c4da" - "d86e21ac6a3b5c9f5ba2a284238f4ddcadefe857324bc95246979175cae548771a2a24dba37ed114353a1419e0adf4dd03c2a66c411d6961" - "97e8c11246841d2ebc29e8cee271e70d338b4bb776fca997cd1894751b93f9a10bb16afc8d3e5fcfc841544926600d8030bb21d4aeed3992" - "5b0aca784a35d0fbb3af9f8da4b468150e7811e88f6580a94f94ceb254a075039c7e25241b82379a1691d9913f5321bf89bcd8e4fb1a5e01" - "efca4cd1e662559f849899fbd4386e9224f847594f424aaa5cd0b45e225b3df7ea73e3fa44afbf2c59ac5bfa0ca9a36a08d4460367c0eb5c" - "974e562b47b51adba52b6fbefb3b01b56d865674d5d4e4e8281e27e21a0a851d64565076da964c5f3dcd0fe1bf1c8e755ad28aa1747b6375" - "6e2f7ab0346c8ab5b12c0972da6d71bc4aea4996914bb748abda489567772949dcb047077fbffcdb1f23622f598efb3273de6ac98b29d56e" - "23c2b1f7c9c312b4ffae862ac02f09d03451faa3a4f4325c60e1f9ebc0d370eeb76ced0631c09a782cfa7d207637ea49f915f1f72b4e30f9" - "d87998398c4844057ec0044f80044f45000578b535400040066bed0a2d00010a2d0003145196440dc87cb1f4439577801801fd071b000001" - "01080a7e069e563740bf2b1c441aa7bdce792d8979eabc8613e58684bdfdda40384becadc48d56eb96040eed45d1d1a5dd1024ec07dd33d3" - "8d97f998bee453a2f31737e1ec25ce6e3522616e1a569e1647895f38810f2b95e16ce6eaa3c183ace6dbe89407da75a3e6852bbf3fa407cb" - "aba500f3ddf3ddf271ef79f1c412c8af47f55f8f6678a914c33830fed7e6deb214315fbe119bc20bbaa1182ccd98826a3562bdf6e0a86716" - "156d420b82e71019cea5975e8e5d4c23a4a233195efdaed0ec04ca0aad766143c2cdb5a821eab5645e8d6fa96955c778ba2117c954313906" - "763aa5e8b13c9e48a3b65b0c917de2572b4e32ec6ddb8bd347dd9dbbd4883c28b5709461e83489804cb8f9c9e4a2525b7c189ad55672c168" - "4eb5bdbfb179e5c42c3d4396f217a12606dcee9abb4db749983298f1a0c548523fb35420c4dae9291490dbe04e2bf336b21d3e54e6f08ef2" - "c2502952c1be2cf8d9abcf5777342ed016f9d5ac651908d7ba0821a28771af8be7b431d090096eba3970fe0d3ff6952b6a3034ce00335cbe" - "63b607a57eb6d22a03f65762f796004b228af1e100469dac7189a45f4ceaba12c737a6c2311612905a56d30d1b96efe8b7b58450330f5d2a" - "6ec378d888dc5932108b5b294c451f292f6388d8fd86a237dc1b5886fce9e98309644af1f50d8999ecc7f2327895f5fa5315df4a5f82eeed" - "a80f4e060acb1def8b5531ae88c11c0240ad3280b9f386cbfedfa0300a6dfc6d4d436de4fc050f39a183f8f9d168208054d4cc7a2b52b7fc" - "0d0ccc95c0ea50f6a584dab1ff7a8fe48ae6bd8f976297646c8720b4c24d78c37e8b70ac2f121fd51c872b6cd4ec3c4040876cbc4b6b0d52" - "b3d5d73bdd43aa6a6e64d84e14c66e3e86edd377f8d55127e9daa40e6e85c4caee8ccda06c2eacbefe750985e6f7acdcab9758fac01b4166" - "4745e8e201032034e0b3824711d258be516d91c3179ed14c5947cbb636f19b056e21e3bd21a41070693fc5431ec3f03f1930ff4ddb1b6a56" - "b5876099750b5e33660c88694b8525118b2dfe9dd922e6fe2c0e20c13f8b531f3aac8cf1772cc8b3746f7e43f5469eb5888185d266ce02ba" - "d808e13246922987719a91a25e63dbd9d3f32691cc91c5d5bdbae0e38ba9f12058ff8e97a9d5fd919b070745b93e39d1c68947e90781c151" - "141f179b76b43b6fc3c053df6cbdaaf090c673d68b9b2d6f89f9503332daa2ee24b09f86a612623217ad112d50e9932df0c606f3306c6b23" - "0a476bbbdec1c721fafaf95670f7a0edeb72b89d6f1e6c87a106de10925a4812a4c1cb125bc34cf73e5d13b31424b90b7ae4390c09e3ecfa" - "01815d1e79653b0edb955e9bc01836c47992bc65d6c1b9f2c4c114b22d0237aac167b49e5718ec56ef991c2e7863488c58fea2804ea1bbfd" - "4a81be993751759d4c672edf3326014b7dbd326ef41be0c2da30f526b6c6e222b44a1f038d3df4f8754cc453fd6e05b2a8f5e4fb2d25210e" - "1457cbd9c6169bcf3510797bc868c1048d5d8cd80ad0c078df697df4bf5bf039d8121bcf8f811965ea3e748b86f65450991abf13cec15843" - "54c721d1cbe606c604d8ad30287821c796a0510a79ce1c0d623b125af5d16889e72e4aa5f77e396238ccaffbf34d79729c2d2231374c2783" - "2a38f74e9efca820212031ec5780d3c1d5be044f5fa329b3c3e7ec2794263d32942f04b086b63d50b7d763397718a43d330ff30ce063797d" - "0522db980cbfd500b475e0b9e0637742408846b4f507e0d6084b5a8028202787c31dfc8af66530575ca0d5fe86fe026406248d76f2eef4e3" - "acf3ab6b897352fd0f6950319e7ba6f61edcb1cbd60d829e030befcab3bae493cd840cdfe0f3c5dfdbe48061470328096e717680802d0b0c" - "58451b02bb87e54675c4e519c4d11444057e80045080045045000578b536400040066bec0a2d00010a2d0003145196440dc881f5f4439577" - "801001fd70b500000101080a7e069e563740bf2bccc83d4a4c909762301a98871e0b5cef56d49a2e9334062161cec5eb02f20f8425565692" - "69740f29192b1e7cd69534f0412df2ce692b28345bac8817b5da43a4e2bd8b27f84ca20a80240500c1a854a81414ed198fb13214e8620b5a" - "1f394647afb820ef1b0681d71de7e510918a95cd4b18130de7fcb35d1ff7f3fbc0969af42e7060620184aa225c3516bf4958ee8805564336" - "451852bb04d1ba8f891bf07fd342bab3eca57eba3047e4dd6d682a280dfa4446370e197a1ea6d300dbe46fa1cb5623646d901a37e8d8cfa1" - "fef9be428af2aa4bdcef784fa9d979e47bdd4a6003fe5b7b8b454f49f8ae8459ec8cd58c5b253b404b533ab3b422122898369321be7874aa" - "280fc2289afb6cbd273941b3ac80a8ab9f3cdf97ad96319c916f8f8d1538352f6d36ee66e62b8e443b0a28821176457ffe0930019242c16f" - "bd0c6f3e6af40f06f58e2ceed84b80be7667394fb86b28e33b289e38ad01eed62e9c918d08988532ddf5f8b23c804c390ae063d4fd5d7c71" - "d0f23f3d3ccf80353ff21c7d06a0f94c43c9fbab2c7d8e6999bada2d4500d7071d38294d1104220e507289f1b1efdc728af3ce5b1b8ffaf6" - "3e87441e4910352fc6f37e79c31fd0d2aa351eb3d8a972adffa594ae76332b862e1f2fffd2c8fb225bd3ebe2b3d326cc4aef23498e995c0d" - "488fa679053f8c37e810ac16eaab5c684447fd79b16156a75f8708e02330f529b038b150e2e8d085bc69174f38a6412dc11c8a01b2363bac" - "d1734bdc34d4d9d1a3593a69bbba0b2ddaadd36f02175510c0143e33cc1e0ea16e5d907bd379c60467a52bb69ae1e39f59a61e87ab78df8c" - "f5785f3c60bb73049ee4c18d7c9a1cf3388dd6d14c7d82f85500d11df2b1f8ca7cf710d64d49bd9adfc6328fcbed539161757aaf8f055e73" - "92feb3b6c5054c4ec6dff54ba777ef5af3086b3355f4d3d8d742187555f84ae3c033f5f32e9d631dabdcc3e10a6f18a32bf32fc49bc85851" - "58d68d428da3516d646a75affdc9dc0ed074e488c85b1e50d56083cada66ca3f2bbf699606720b3ac81dd3c1940f19a56bb68a0bded5d1e0" - "b82926d19bf4b34eda76755072c4854f0f454764ee86450a96ba223db87db62c4cabf53db84d1071b6bc201e855ceffcb579d89792063370" - "ee33fb66716bdddd1ee511e7900ce3fe2db4a19512fe41c54fea0f4d04843095682a7d2e59bd6befe46f6d5d6d31b25486b9e2da880a0645" - "7afba9d4eaa911f539c93efbe4a4a995fd70c59212eb5f210af7ececc1060c8315221ec8d18a9c1dcd17a80519928beed32e704bac3c53f7" - "084fa17984341743f3f878b9115c9505d274cd091ad2cb0a9f0d61560e73561759f966f59a89d3bb889468002ee2bd1e4b69131e25e585ab" - "2e0cc0704fdc777d97402b4824f39021596aec0fe9a7e8f1afb72eb1dd767d8c08de2937d3d39e2e7e51ec14f3401f77357e86a4a19437da" - "b5a0f17921cf08f8e5ded1138b1778fa407fa973dee162e4211d4149ff802779e173aace68a7c25b7f92e6a891ca14d9feb53da7d30c6555" - "1dd2d1f7ec1a1bb58242877a09e5902e49f093d008620e589003508e5c89dd5385b4f96ace31a4e53e986ab8b704aa41ebbcb8fb348a6b35" - "5e16446dd10db58bf6ead3a8e3d108bdaddc3d04faf704fd1a5076e6eeef3877d77aaee04c7aa941e92e63df1ba0c9a8347fb3e3ef10fe8c" - "cf610b4b070658ee24f6a10747e1279ecc36adb2f9e87a22787b7844ebb123bb2c83bb63ba943543c73486980bceaf4061f12eea3fcdf649" - "4740fa63e67b2b217c61c265fa242326e884d353e0472439adbb08a14f1fd4bbd5b5afcaae04b9a7ab734e7eaaf838e0f00decca546d7f78" - "89883fe64b147d34f484dceb5069090e460fd54ad6c0599744057e80045180045145000578b537400040066beb0a2d00010a2d0003145196" - "440dc88739f4439577801801fd494100000101080a7e069e563740bf2b98d0933602d916374c32cb9beb41a7e66cdbdc74295b4caf025444" - "1623b8524dac0f1e3ade88322d58c3b2ebb2c1db73eb972dc44735c115d5469e2080f0199f6f90a3f620564e4a6401d2282b235850a18bb8" - "333a7081ae50465024199ec841fe5775494926f5ddfa7c23d6b87927e6d57009fdcd76358c243f01323b1b41344db8d46deaa49afa3a669c" - "11caebcb6f4b314ad9784327fc14a8b417329538060ded327d32f7db6288cb59b665526c18b8367dfdf9648a86bfb64632ba499e099b903c" - "cfd16574966657305f80e9236af622ab6a07a0bb501f92901b0c9c2298c80c355414c7d66a96eb25328e0f602d692e07170ed9068fcfe502" - "b4a52d7baf816c5ec41aebcdd5e5a2bd7197791ffb480b6a485e2cedeeb13e138cdd34d5caf526c6671de7f57466b0ea567f1fa02e8ab6e9" - "9de86173344e95dee0bdb26fb3fdf4647700a8350f124a9afe530af427b82650b6a91c62369c806ed6df86bc4601af80c4181357209e4947" - "9c8e759ca001d2a8801f08466ae1660d81e9a561ac62f04bc8b2da2da7147d8075da6f869212c987515b90f9e9b5b467583c8d43e835aa3b" - "66b48e87adc17dc2468789ec4e1de225832aac02381c041a80c520a65bd20740808df454ac7bcf0b97cbd965ab1ccfb796f4751cae9a96dd" - "d2d3abbbce8f4b9f63732e0cc4d49a10c7159c8dc58c6c5fc25ac5f0878a5214d3532d8d8734848f784fa93bf353a62f29b9a1a932b2f666" - "8b5cf87d2429347876373b8e5772e866436b00b88582e9a936ba22db42c0457d1483574ea8f09f2874dc721acf3ea16a626875ac8d077fda" - "e7971d5fa2f75621d0e747025921a91e57202a4aaab2b5c9571682b515f0506d07046dd35b23b7557d2d03ade7fcb8b0e95489fc5159f0ca" - "010e9bf51d80eebea7506a45dc2d160b45ddf28ce9bac3268841d3dbb646baa63b6d5d7ca027f041295237fdce8c82168b1b02fd193d3087" - "a5aaede64f78b2ba033a0d7e034089554ea191f9dac8c31e907fca2874a8aef671ebc9b584bb1a184daeccbc88810cd754fd072b5b77e1e4" - "8f8f381302d397325a600b5a5564af1db1213f334494b91517080bc459c2308f9e8b14175770d5f74dc36eabf378bedd1ae87b35b130cded" - "1b925c90486bc7c2583f45748abf2cc552e6d1b05516c2bb6c26dbd1f4edf60a18ba8e81e55ed18ea076440a9e23efa5b0b03942649ba230" - "7f8bfb7abb38e143c28330bb9b8236f0a7be67cbda8256608c4e762367491e61a0e6a8b2cebec0124e26b28aefbb1b928e1a383d8ae0b3c5" - "f94a4dd5fc90371df089baaf77c97a1e68a5d3dc8e4d2a4fdc7fe6d69c5f1a52d8227e7c0d16b8b5a3fd52ea342586249ea98e7de0aab783" - "c9442b52c59e4c861ac10f6251fb22369c26d1d37dd71b3024b45a1a1a7af86b46166c2c0f97bb70026c3fc3098d33d10bbd31e218dab37d" - "0edcdce783650ab408efaa75dea3e9cd24a3a37a995261237d49314fb458bc65890a6a83dbd6b584fbac80917edc1a2372f9827acb31651c" - "073cb5d5aa26d7f48fc9de3f4c825e0f0e8aaf3f54f330c330bdbc06c00dbdfd639cb4d0ff87fe07cca1de5800cfaf8e4d39a745500df62f" - "27bedc88a1f198a7834d974b841186122f0b467558d4be0b7dd33c0f7574495a151055bb65a2f2b70b130e6eef2b6d44ae91db655f5603c9" - "2a02e0e2bed77d17b0891552d38be9a75a138a702fc55f2e1c670a7516422deab50718a82a9711db0b9fcc008b37725fffa62042573b9bfc" - "5b83a723b8927126e420e768a048d5ac58c2ba9cce131e503018df0e3f4fb47716ba0c62f6a6979b2dacf3a0bab482eef2832141f9f3762a" - "94029e4278d45f3491582b736c0d2bda187f3c434cb7b9f8755a5892875588515a44057e80045280045245000578b538400040066bea0a2d" - "00010a2d0003145196440dc88c7df4439577801001fd764b00000101080a7e069e563740bf2c4561ea729de6af7c0a79676c7763ba17e7ac" - "768c2951bb9870622e77b4d1e904e0d2663c76acadf4e20ec8ba9b8ba83785c9cc2febfdc8ea3d27e9fe1269b777a670dfdce037f19bccae" - "ce3048b5bb19a900e80928c4b0aeb94cb0f9a38ddb1ce241eec24bed501fceb2e70bf8143d98ad36ae0936a9c5c6eb8edd5f190bb58d5421" - "9a5678852e7ef857f6c19ee1ba30f1a26fa1ab7992969f2be4e1f475b0fa04c824529d121c60113d2f4993ca4c4152c82f98857b1c74343a" - "48731bcd487d4841eab3c0cca90d7ace6ee4eb90dcea844e49158ea5ee26ad7f809591b2b00a6451063ea4fb085792767cb96dec83294499" - "2d4862c6c6ba255713626ab834bc2f1acbd3dee8fbe0a071e55aadfdc392968206f7b0a3e2f0da4d222c3a0e937b1e0155a8b6e5757e8565" - "cbdf08e73be51e4f9df301064b50fadaca86b22d16c258f83a4328fe50ac1232fd777670267d10c69bf5838c02b608b8598aaf48427f6cc0" - "2f1b446d872fbc0fc8a3855620b34e1ed3103730158434e61306d7167f79a11f864667bbf38dc6c45cd17e5b2fd67afa2cf9df200a7fb5bd" - "d3196ef2b0485d71452a766db4c35dc941ceb20f18f88ea85bc65bdd9526ebdb0cb0d69609e42b9029f09384d7657f82f0eb562e0e15029d" - "8344e5a49eeaca422a4f9f65185be693b5dab21bc685379c6d52c740644cfbcd655f4e12a54d7361cc242ca0b752301040708dc7c80b1ae3" - "938a82ac6d60881260c7db7cecbbd5339bf94bf04fb1ca52704b53803d1cdc8ca1571ed5e417db023e67bfde37e519fca95b85fb83a92df8" - "a17bffa8ca0c16c8fa17dbbf4523a1bb31dc377ccf2e91b588aa0c566bdd0b428424c375a49093e3e93a0757f86738ca427597cc731c0b6a" - "52c0b49ffd0cd6fb02083759ffc989fc0963f3140de34acb03c7211335ba04f0cc2463b82d7ef55cab7958f4510eb611dc28324cf531a8a9" - "ce71a44b711f60ecea0e00a1a955a400b528fcf8530450b7f9580b1fab14bb847d4d7b29ad62569d906f3ddc3d1c37b75a5a558b6e84e304" - "e7f9d5e7c9a1a9167ee3b758317f7110e95735809d8ed01189cb47ed3c965e0a29ca1c5de7d30a7c5320658bdae1d0680a4479c5e05c1595" - "4fd6089530a25db3f4a549f92aef6d2362b7c6979577b5242ee6d94ef97565cab74045cee009b57751cd5e5188399611552442d686a0456d" - "bf40a210b1de6b4915c370659b8d3c086a56d1c3dae7a0a067c97598076e3c32e0e0a8993f5e9852221efadab70531e7f97b535dcf1ddd5b" - "c6455b4e256ce96c4c5cb6a44bf73167cbd101c5ed585322d65786ff6eda4f411872357f01458e2762be2b5b68d5221ed0ed359dccfd6cd4" - "30a662af3c12f8bd769ebefc619835f4995c2b679a3cf1253be4678a39198907fb86df8c02f369eaf826954a24565467716f62931ba1487e" - "d8b34bbd1edaaccad6eb35e0d0a53253ba0caaa37b15e10ee13a7193d5f9d4c24f131bf32a2fe971a37a4a32120c636fd327aecba2495f84" - "0fc0e062eb2f4c138114bf484332a4193b68058e09057d31ea92dac20f13395d3242d55a66f749792b77e467dd01a90751d2dfb3c2e4b8a7" - "da21eccfed95bf8feb3189433c06d02b134a8a2db80ce983e89702b8fec3448d7071361a7e5c22bd6035a46c50ab20679cb9f0839ebd0ec2" - "c321b2e24dafc4c117264ae1ce27d4d4711e8568b5ed30db24724ec30fae3986937a6b36477acba89e0096ddb1e61d18863315db078a2cef" - "2e0b632c101035e0804616fa2a8058c0a2970ad7023d0a955c75d01bacdee776191468aed782444b664f31d348abceb76ae6ae6e6fef1b09" - "0adef4e73c7e3e104850931b0c4a3917f23a183cb352782665f7b1408412a0719a7a3d4ebe7db5e66b6744057e80045380045345000578b5" - "39400040066be90a2d00010a2d0003145196440dc891c1f4439577801801fdf02c00000101080a7e069e563740bf2ca6c99f170fcbd78465" - "30e1e5bc62cfe2227c2b02d309a0c23d3883eab7e23aae444300c63d3ddec0075f01bb9ccbde7cf775095335ab1034cebd07f0ff74b7debe" - "3cd1aa343c988025c258acd2b299a119d07045e0e02c0c815171e20e1947faa91779662ffcbe374318637e62b889d34f7b71f2dda4d6ca4c" - "c73e76d9c97fd5bb0f0c6af96f0c307775536af43e7754cb53b2bec2a93b92379f2209592aeedfb2b34702aba85bed9d2cbb22340182a2e8" - "a4a7d7fba881d3607f8509caf7df90fb50a0dd0f1b0d809d993d33ef6e3dff6d06ec0932bada41115230386ea2b0063310d078f035d1ca9e" - "0e1effe47e23938e9417e9e1b28cfd9be2d3c4d082968f035dc9c63063927210337a39d6e4cb2862982a7498c06a2851c753eb7176c3976c" - "fed4a2bff2d14fb838630fe4d1bf59614e940eb8281591f12639a8cf58e04219bf6e3c279d4654a0b5e1517d39ee4d14eab1cc328adb867c" - "769034848ff3386e842890a5b1ce6079c3fdb4d562c0b520dce633a595e55b8b6ae8ad899651ddddcff0dea91c99b1d6ec9f62bafa18a1fa" - "d91e41a7d9bc180b5ba24965b6b7881bfe50fa708d56d8227b381a055a27b8f2507f3c69951d4522d1ee66aad8d2780efe1322ce089acd49" - "75a0a02b93017d0e7701eb66ffd058738321c4ff66e58eb6492ba0dc54d13afd74221963a2fb29dd269af9c9b96b2201c67cb7363cc9d43a" - "38036bee19c870b7e3f63222dcd7040e7dc5735d03fde4cc351d7ca78c73d58c8f1c35688d5ae6e49a0b90e2e16568462d07e49d461cfd74" - "18e820d62ad336947b183b0ccbbac24ad29ff9133b0f7ca01bfa529ae130aca14e9feed6f6c7a41b5248631bd3f3e5778f3cf5a820a51542" - "1b07bdd20e8f20c04d855a28179f2f6e2fe771c14d5bdc16796c09a49f3199d4c5a2751a54cb1fa43945c74149eafcb19df3f40514fdd95f" - "4ea9761242f2425d29fea872a6e8d8ab4a99a46132686a35c15c09dae44bfd975a3e264dc308dd0301eb0d000c4181b6586715a1163394c9" - "2b209fb00dffd9d2054d3363e425eddb2b59d7dc297e6697e2e016358dc2c45a3e6244b1447105724193a3e729ed4d1ec3852410d608dfb3" - "5ac801699fd9b76a1a2564275b1c98a34bd1afb2747b845d93db0cbed28a32a13ba7329051513a8ae501a1cc41f006a8e7558e230c6436b1" - "f5486ca38e985d66ab72d20c989a8bfe0d704dc3bf9f72124248c965a5c4ee42e396c341d14f8a0d98736b8a26b996f2a74c0711113c65c9" - "038ed4c00a4ba2a1685861171b9a711e2e852043e14e7aaa786ebd109b71451438bb782f0c2414a1317950a2b4557d4e276793c5df7cf9ec" - "de627334824c4d324356922ea83aa9b60f5649d304619de16bf97e598ab275cf2c61548eb6a3f83e9745215447525bef4feb034e8c1502cd" - "e19853ecf8e0e91448aa3c2ec1c6885f1957b19ce496311766005822d315a658ae3f4de3bf918c9aa25bed8d61000600c86dc6839949e8f8" - "97a56e2ba80151ff8501679b1838fdd0119619e20f3395d75c391478ca6f558ad5546f3095d655d2d719f1fb5f714e56caca16f331f72e7b" - "d50a63c1b18cdb91f7bbd290fa85e722db810033fb5edcc023dc41369ac31d9c08ea3fce94d86c2fe1720f66f7f1f70295014596da03a738" - "7f59dd7d7c105e3df35a6542485e2bb0b93ed2bd4f97ee0aab8e8c5c77ff4fb6b9f6ff52d7bdf7fbe5bd923966d22892c1791cfcfa4efe05" - "97f9ec6a8692fa096d312801c59eb4b513a03df612673a54c6fbfaab20f1bebcc328bc7efba9ea0c82ae4da35c30dec172c672abb8ee9519" - "4b4ebf48bf972eef490d8550c5841da62d9685c6754d4919a14c02d1b810a0b77faf097c55f82c16000f6adaaad0f393136e0d44057e8004" - "5480045445000578b53a400040066be80a2d00010a2d0003145196440dc89705f4439577801001fdb51900000101080a7e069e563740bf2c" - "977c4f8933bac1635ac1b347db167e97c35e80a54f1f76dded8218d38a91107da54274e31d4c221f21d38412741abe1d35937c17c5379b3e" - "8cdf023d96f0fa9f8170a45b3acb62cdd7e00840d73c8493bb1952af1b9cf0857c0c62d1d3eb8ac0772dc36f95fe6ab57baaf77b797a2e9f" - "c4ef0e52ec1bfdaa1f685659906444b4907bd79d6808fe173887afd04f03741f0d7773f25d5b942a66fd7d00a29f3ace4e9ed48ba71a2ab7" - "6122aa8dd88c4f73cdb4f867f6888bd116f3558f351d0257a07c593a885dd8d3996fc3c29f96f46ba3e4800073c4e7a57d7f9e73cb2298a0" - "d8bd1cc728191748660e175a11914afc13077cd5a3d3231161dce49d57eb5f9bfd163505d35530dda334d578be3ba05e790490c6194f0103" - "4614975733f39560077dae5e3a1ee1192b7a74a9570e2177f2c513c32cef433715ec74f4c2efa30d27d73a8a02210f99ba0c1cf41eef66bb" - "bbcb85cc30a3b9dfb62e82910412cc09827f57fe7067f7359cd6be112807502d6198a4bbe666cc240e7ca45d081dda065e20f0f7feeffa38" - "451252494cddc4f4e05175b0baf8b62f4917cc262b25a6337044cc34ba548afd7651f6740c8329ae05b4821247e3d21b52767e72984427b1" - "ccfbe20270dad0130b9d8dbfc758441668ca5cac2a9400ee4314ae83dc6a9fa5e39a83ba9fade0b74ab9d96db4fffb876ad27d763a195311" - "2c13e9b90f409d59fb190a97036037a6c126cad807a314a433beec6dc46508480621d66aa32093143ce3d015e01ea80af8e0967c64108d67" - "85933d88e5adeb56f7cb06a9777b2636858ac787f28729a1dcab5b49200fc89f82eac81148464184861bf429ca70a308c7072016e573a6fb" - "749ea40487662cde6ba88658f158cfe82889a5f6cf3af5a94a78d2f41eadd224afe358859252fd96bc01997c67534d7b74412cc0f3ba29ca" - "815b0a8c8b5937fecfaaab8add042cfd7097bdff7bfa7673dcfb867c6c1f9bbfe3cb6d03f34aca8b89b28c2a2ba55d620b69b4bf4a43729c" - "a7a28bb67e069df0b9129ca3436aaade7ae366fd5a54389da34f7ae4597c8b718e120b125e0ab0e9f0caf03c28bc48e1d3a16e368640a4f0" - "ce8c5720a698764d46d18c003dafec79d62dc57acea5a2cbffd0bab6e3703a800477ded10efd30c8a13297474fa63157343ce27d4a86a589" - "a31d62056db9ad9b8105550163e6451240dc0f074b8f346a91eef6342cf1131343d971efe0773820a7d98377f7d41f1701249dfddefd4ec5" - "33ceec2432806a4b96a8ccb524c3a9905cdde611b7789a42c78a11c9f90d5ad95858b0a59bbc7c14b33b694e2343876be6fd15a0efba7b91" - "e68dcfc85d11f91df623dd03ebc590a01f6b0cf96b4cdde89c226ae8560fb2f48507fdfe5b3142d8ec217473e1b6bbca534f210a6b24efc9" - "4335fd29bbbe004fe38da90b51eec54b208bf9b5f59ea5d78d58638e89672a65cffd0e972d53c635b178682aa48e1d0ba2684aed1c9594a5" - "1c267c9e4bff57a29f9672db16e7ca30cac6cec476bca4a0ab114e653ed4618d7dfec0580b0d896972c9e0bcfd54ac43f1e53c02eb6cabe7" - "5477b522a5c72da5a033fe744e4c23d6716db95f546e0a3aeaa071a1f126409b7e3625980093c6ab42bcf8ae09ae4cc54618dd12f6c9bc81" - "f6cf0526aa8eac8c69db869a208519e50f2a168f037027b516ea44099877a7911830c738791d75cefb5feee854b7916a009077e18a896d14" - "90c3269ce885ec6d5f0ba5aa3b65366745ffe35250b0ecb2366a05b2213d8b989c5ea099f1295c38b6f2c57c6a4cc8263a575263f25e941c" - "c37b3dce14b9aed9c2bdffb4bb85e7ea25d8d1cfc7ecd4e036c9b8ca042e840e967d06c13ba2c51ca5d81af2233dd8a454de51fd9af665a9" - "3239c04244057e80045580045545000578b53b400040066be70a2d00010a2d0003145196440dc89c49f4439577801801fdd6620000010108" - "0a7e069e563740bf2c59b87a35f5179ea0b6405a660fbe39fa5b6860b4fd22c160944c306e51dc2513ff8f8915a0254817f1481a9c2d7326" - "4e6e95c68d4a516afcd2e048bdb4401bf86bfc1d95186a8b14aaa1584799aabc9cedf9053f2641dab620c1df68231b778afa193c570ccd0f" - "fa04646d65f591f8da32ef4b7d3fe593521352a64d2294cb410bcdafd50035541096cc0fe84f201bdb880dc64401012f481752480b9bc0d8" - "0bd493c2139817b38c408142b55cbbc91056e011a35c58b6059bbcf2c491af60cf27e172b3d45241c96b099b9f81e87dc49fe254f1e0c6fb" - "63a595ddb880509ddba227d3c8089905c3e27577d7a10f6434862027983d1cbc98611809a6374be849518c6dbe54b1006d758d5463b0830e" - "eace4f6a5c9e3a281347c8c9483dc72bdbfa4cfeca130779e13e326948e83a16cebc6441c3831b2acfa2060b45f46994aed443b88718bb15" - "5ac06d99ab70401eefae033c88f3d9c26cafdcf3b7549722b343a4d5ed9f39165daf1d4fa21776c10a4cc11ccf0686e9da723d6dc6437b94" - "70bec396e37d9786b3af22380183b6a955605ae97a596cb09a9f687fb68d1ca117e1efbd6c97ebdb040de0145a6483640ee03148db79d475" - "faeacab5df4cb057c52ffb62a4cfc5414681239118491521dd44ca302a37c541c1de68d1140c0c52f27937232f6ab4d69eddf85d5b807146" - "5407465e68a9de3d943620246325394a7674d5c37e315005bd51ff06ae9ef20063019ea4eb7096c763dc91dd999a169d85d87905734914fa" - "23e6dc366a31af19da68c25e205d0d6cae37c862d9e07af0f63a141304dfa94c4be519ac58440972ae31b59ef15ce46ad2714586d128ee08" - "a5011cab6a30ab74298a5221a321cfbfe9bc0e93de2d84a7de991e970fd2299d83bb529da8b451ad34a8bd5a456d7ee24c9e72d7686e563c" - "7b788858bc4f78e3fd38c1b753e3eb30a9fc77d07eb9fe58967f2b8f77cedb539b769df4f8c271cec4c9062df81fc3b9e5ce64c2765e8259" - "ff73a52ef24f7b161ec8975024f097b36761dec9beba2a7c51ee3471b345ce214a826d323c938d7f9040c08c84e0a2314de473afcc21b068" - "9f40dbd1b570619831cb9dc56cc504d9b2a6f726cea70bdc2ae462ac5c0fe1a3a2e713118ab4ec9fa2b80689386d45ecf3c5266329222f71" - "bb774d4716777e64cc2170de49f422eaf0a437233010ae940cabed94ca1a22298b79d2547df8578db2964cfecb382236a0894dc6b0aabd20" - "6e27280dbb101ef311262cf5f349116e4165ab50e34ce0f027a19217ba8d3e393b9d6521cc43c1ac096fe58a91550857e72e7d8655306f8e" - "6d414834fcbe557952b15ac424fc27648155e9b2c26e6c56c1061567ab8becfc8b54b00abfe800719ad5b92465a2c2fbf0d3cc9ddf3ba00e" - "18e8b20bf8412ab21c3a38ccbfb16fa1216869ac0bb9df188ad687acdf90afe47ede664e1ed2fb22a44ac71f877df095ac44f8de2efb3b71" - "540f129bb9dfd3d3c8a87f190a9feacee84e1cd57671ab59b62985dd659810349932c2782c5c2d9ee5d21698659ba2ddefa0b95f787ad1ca" - "21f5d2f3b9a8485a168d7e12a936a3c08ee784d52c3a209aa5a015b1ed501770c8e2507c35156784b8cf167d3ec092d739df7e970ad14288" - "817c5a58f2af904e3e0173decf92bab601c6ef46f7cac54aea4ae21373e929eaf27ddd78e970a9020bf46491e3cc1cd8de04c7bdd741df31" - "a9c7760c3929a6a62d11fdcd81fe8ab5e81a95a6679d003df8ef310df4eb2edf7ad6d94c01cfc67befc432846f746ae801be801786075b34" - "1d5199fad7c7f58dfc27899fc0dbd483e78c2bb648fedd97f73a9aa29101930a8b6f8e3fe22d2764c60b3d715d931a57a85983809a1a114f" - "64b6fef48d7e537e8047b1363b44057e80045680045645000578b53c400040066be60a2d00010a2d0003145196440dc8a18df44395778010" - "01fd9bbe00000101080a7e069e563740bf2ce06f1248bd16d7296bb3c6a263b82e3845e48f8b154117f1e8a30024850801c815d78d522769" - "40519ce6b96a059fe047fdcd5d3b46bc57c66e5b4c919e6cc13dc445546ebb4b547d255c45acf05bef2db27e7a0225fffe46af19a9369362" - "5b81f5a686d9edaa0a23061b5507b9203df4bec9e466be42814712f941decff0f8bd443045543faaccd84c171b4f99ed218cbbd8c4f22b3f" - "cbf12152fcc7e6b9b2735aab2af590edc313bf6c3a2902050115337133632a0a0bed69b76c1a04af38868dd18f5a1818e042a7f202fc4405" - "c54885b0258e3cd768076b65d5d67c9dceaabd9488e997e0307469c95d9146dfe9ba37a4096f3115be6343c532a850e9586b9274e50827ea" - "d2756e90577c490d4852841e5306cdb2b110cb1a5e34ae8702b8336b5434949f00ab7df67d47bb1425f3c66e76c83d8a0fe06120d9d5ce5d" - "3b0d4a4f3969057b6004389ceff043c01f215b4dc3094c5a350440f82c4ec6dd2c8ae80f183cfa0f6c6f7b66f2c47474bc55d6561efdaf5d" - "a79116d4cd17816aed1d2676d3a59e23ef01bbc3f8ccacd9285cb088298f184c235959dc7da033dd7651d04decf58e4ae85dd79784b9f54e" - "bc1d075aaddcd1fa8eec4181b877c972fbbe6e59400e0f8ced3c18f9a3713cc8b98e663ca443f9f520265e26c3c30bf8cf6a06a2f21c7a9a" - "b91398b5781578f7a953ffe551574b87c6d396c48349785e4fa4a7b9e5f9986781c8ec74305dd08b61595fb05de25a9bcf91cf2f80d31c29" - "088861b5fcc599b937e7c1ffd8bba29a01f730b3ef0337ea94c963dbf1bdf32937a0095dd4e9f94c721ac4d310bbe90755bfd2f3ccf1e685" - "77280034f1ccef5a3d1148712910904a5691df56d66d4a59412cbe6c9620644444c07454488087a887b78df14261d59c638c66c38c44b309" - "aa5f09583f10b4fdcf1bbaf10383d810a077d88a1bfe062193f9da8ce7534c896a77de68428745df36b1237b8bae3c9d48ed06109adaac89" - "280947a82a8826e6092b975ddd4f0a6f181c007c99ed4e78a8149adf267306d98a8242910d8b388ff3d65f169b487c33e08a3adef0974097" - "69e8530b00585adc9c60c3c69b66b1281f409f22fad035ecaa6e5aca85cb4564cf6c60a80eefa80cade210fa32178b61d60e3c6c095574eb" - "5950d7c068b18914f65a1ae13b49ac39551eeb4899c427306d26331648b7f131db1e27170d5e5851710d8e0fe992603e0d7fcad95964b1c0" - "46d947df874045ae03216b3d26f5bf5446534f6ac0c0609b960ca1a1cf1be377a86d39d8f6a367673c29cf6285bc44ac6583ead8dc321122" - "9b077da4cb49ab43f100ebd5f7297923b4bf63479345ce2d93458597f52e767106837fa433ab846ffc36ab333d97c0cd63e9677c22d4e49e" - "5e90af290bc3e08663473d8b0daa54e0120cc8039c56bdf76e4a4e143ee4947c6c246e4f7e2768c720bc824bc1de3112f8f84b58fa388227" - "1c9ba3ac7de3a80b94c9dbddb9bb0948f862424509382c9a7af37b4b2dc6a14b364d3674141b789e8277e7a95b5b9d9417f8d82eea297c58" - "42321eab91d36ac42d62aac23417ff08deebc5399f01cccf29f83b2756cb7d3fc2960101143ad7daa190155ffe0b93e943bb3fb4bbd7626b" - "ddfb66d6d2bffe41a7cc350d48f7136ffde25487710bea1e31c2fb7f2614d63e12e0b80a9cc3f6b0e73627517deae9e203a94166d6f6c877" - "e5cb8b6f16de5d2d4f93647ae04b063b5a0b0d18b72fd93a0325afdf3f0888c30d48f06ef9f0a35411f0abc2751ee1cf674517f39364a353" - "3cd276d0379b05ec0da16eb552088e3cb484a96aa9fa43abe2c63ea100cfc074be50a3ec567d08112d952cfe5c71d4d5cb6b1c6f7f6a0aa8" - "27cd90a2c460d894f980016e22f73fe2000dcc7b479b44057e80045780045745000578b53d400040066be50a2d00010a2d0003145196440d" - "c8a6d1f4439577801801fdaf2800000101080a7e069e563740bf2cec3ef95a142a919ff39936cecfc079857d2a3def17af3b7f37e1e05402" - "87c860d13f76e896cabdc26904bfb47c19ab719a081397abb83ff61efe8c7e10abbee769cea7d4fec0bdd0ce9d59ebbd6c9c6ac24dbd12aa" - "1d28c1755abe40aa9ea0c27c341fcd6b19eb0536bd8b7c9c434697bcc0d42ffb97fd66edfd815375aeafe6570520b99806ccc3b154bf2014" - "4873daf12849942b08a4de7e5e376ebc318092d5e0b1fd1dd668a2e179d95c7cf2418dd1a780320c1fa592e7aae61bbfd6810e31d88d64cc" - "809c8c4fed6fa3ad6cc73b7d9c99e233543803802823f92148f55a2ef2018bf559671a579bca0dccf61ee5a84303af875c7dde89fe1dec89" - "49fbdec1794cc215253c5e6c3d09dbe9c9f4a3767fe9b0d53d0d9e8fcf40e4970b733a23b26a22b912613a593ac8a9f07be52c4c3ead3a50" - "ef1d2fad02829fbdc162fe34a48910b170048306e590eeca549517f733dfbcf14ce7fe3734a7bb5b85977e7a2b559111ac6ebfa3355951b6" - "8eb93ce88b9e5f4c059e649d949b3942c836525765e0a104d47cdf72fa64f0866bdba6c0719b490a9e1adaca90f6125050d97b1bf96d2ee8" - "4d6b4470a03c9d528cf57fa659cdffb539a260f38e91f2317fd8c50dc7948a9a524be34da3b3bfcb06b7df0be4483386f9fb1b2578fe0562" - "c5b99fffcb2f88d143d5920d984cd8e61ae9c1d328883ebf2aebd5b8552ba459ae2a54814ea79a78c20317d940c074f8b1fdcf4577b4a290" - "3b4118f38f1a1967971f31a992f1918af007c087877a93043b86f05542c79eca59b4deee91c521fb695ee99b902af085d1a07b19a6cd2d3c" - "c873e9ca0ae54eaa83949819884684e2b59040b3bd24d4ad2137310a36fff470cd0092a2049382c8e8420dd8c159a4c5142eccf6160de7cf" - "225ff5088b6ce0fe89dec6b2891e743a5b411b52dd2780f67f61174c53bdd695579d975ee17c508ff20ba6bf09121822e3fe9ca9e853fe81" - "88400380fa0c4ed6233a68630b80cee749077d08ce8bdd6d51dc59950c5c5bd59ece66abc935965c7ebc5225c7cb6fa23e07beb2d8272cc1" - "0a69f8f8c8d49a89134a6e9dafb5936cad0304f1e5c8e47cc6a1846af22372bbbe973793b1f56c6158de78eaed60cc9a985b18eee85c332e" - "a24d4ba063d78927b9a2250b535e304bd33846b553916c11ec3884a81220903e16b126195417c09a0bf7d2a91b57fd50209a4fe93f1ef5f5" - "8a08b4d1541ef7078d8482965bc4a875eb71e1ef56c8a623225e71700a139ba51366e6e58a3e0fa132ae8547f56ab1f245d8b054d9f4d2a4" - "62f4835b7215e91d4291a76cbf3a97baf8d46776c8ccd137516b251d13aa778bfee34702cd299b71c76f95e3c909b3d38dc4b9953f1b7f08" - "93edd41e0738f7353579871aa30f659995b6e698686261b84aa3bfebb8070efccd5e82d3c2179f03f328dcc9e58af02cdb2e060282b26014" - "d4fdbcbb4f77684118058e412890618b245b52841aa28a0c7d44cf2871706bf9a56a41db5fff9e57b0e45a8e0494fc9049c36bc605c36ad4" - "a3806cb9903409ab38fcc3fa6ee61df69d0daf98c62c8c3a380b6a4033a5fbb257e34fa363f6776e44492f6e87ff20abc82cb959bed91941" - "87767d39ea5bf8c25e93007c48f18fc4ccbc8faca025759abab6be27fb8e8e22d294c60592b5906dd96ef8ffaddbe1eac1477e37be9425cb" - "dd9bd17b17ba70de631ea09f6d9b27245a81b8dd9cd85c064d1832166ab182adb6fb8115d5d9a341fa863e92d2b89ff2e9464723b34bafba" - "9492eef498855ac486e4bab1b9ab69051ccc6fac3a47f16b64a6b43105367e2150603e395665666a4774077d4b7f7947b29092ee01bc48e4" - "d668ec61312d2299924197c3b6e92b025c54e6feeaefa0f60698e9157f546b4403c690045880045845000578b53e400040066be40a2d0001" - "0a2d0003145196440dc8ac15f4439577801001fd38e700000101080a7e069e563740bf2c12aef992cbb4a70140b8edba1ca001962ad375f2" - "5293727698804fd44d7df7e5c7c780b562d06167cd4bf3f5f9d7a42f321083e88e6389534bf002e81ee1191f6ac48538119c2c0671161acc" - "b440f4533febe253f5a1254b7efd5b5e19638efb3565c81606590523e338bd2ca479408dbab1af0a8c1261472e59290f2035885f061c03ef" - "9404aea3922bcfe439e132079ebf026d5bda87c5bd6685b9c30bb9543ede8a537f47627d250f7f92c4b3c3511e5bc1e460784bb79f30205e" - "8e9cfb28b5d386433851d9cd6bbfa2a769ddcfba784049748c23a97b9dd9b0102a688bbe2ca85d40f24629cf65d35ba2d9ee8c5c3723663a" - "a63460364a767c2fb928778f39b03716db7905c6822c07bc2f26952a20356e2611088ef6eaf21e19c767f2347eb3a7ad8d761542ca72374e" - "3fb10b0b83fc07aa6b75641b48aa2bbbcd48d422fe0d215b4aadd43a6cfedf3f6ecc83a3df3726ab43703d651b077fac0e959c1a008f8a98" - "7a3008e9244c90564af123662104f0f39e1ad51e304dbeeeb0b6d59ce60d2a4eef74c19ea89b75b559d4c6a24b31cf4816b19b3f0edb77c2" - "7c93c667f7f46c8b4153664cb9638dfe71244cd555cff202313a517083312245ad0653a8387e63e844020801fdddfbe7e63901b3d75f82a8" - "2c1bfb601064db01a5ac2681d35a7797ed44c07fa6b8d782fb9726dea13e7a3bcc9c0331acd67e43e6cd6aab62c511a365c4fd2e67c2bd50" - "03067f098d4cc7f16c923859b5ef1e1153176b72eca753baa73bc91adbd85a1aeec61bb63ad72c62992d1a78a87f19bfd7abaed7040e4e8d" - "21e8ac08008d8914e55cf16977b7a9f84b10e7f29d48404b6e87c111ef5e415c99fdc72fbdeabdd9baad9efe1e05d33ccfb1643c43e48ae9" - "ed7b179693b4f1a270b2a1a6e5e572336160579453d7bef82855b27dc4a26964c9898fbcc2dd26b72019d86b4b26ee27c1a617cff355d026" - "b620b47ba9fefa47b9cbbc7fc97c17ae9713dffaad490e42f40093106a92df63200253682f2e32f1a7fcb7bbe6ef37abe0fc5275b3d99b9d" - "dd97149c9b466d622b7a641f5a8916dc81cf160f8938185057a770ef7b20de955e30fa904ddc828b97f7e4ad111462dd9bb9b724f8b29d42" - "c94a12d6ad43edf2697ffa4a62c12ab00378912b106efd2c50e8c46a3a1f8b682c11f8702ce1591e27042413e38c340def44b68a94a4e761" - "beb25106cf2e60c0aa9f8c0f745bdc1ecb8e9bfe71e9b4846bbea0dc5d2d982d17176c3119432b90e3a8f48195d21a06"); + srsran::byte_buffer tv = + srsran::make_byte_buffer( + "44057e80042080042045000578b506400040066c1c0a2d0001" + "0a2d0003145196440dc78535f4439577801001fd348500000101080a7e069e553740bf250a43116ae9c7fb384dde95580b51f982cee5" + "b1d7" + "2ffb703548c3f9a9220c5434ff30c1d04affa45a62c111fa45e4099dfec3dd946df21545009889b77dcda48b80de4903c13b8844a6dc" + "b31e" + "0a2b6992d01f76f64cd621ef993f2f09cc0fa9a825ee8c9a92e9fff812924111ddda77b58b3e288050e4a9d3372d448308f6a684d4b1" + "ef22" + "217d33e79aeadbdbf3898c2c1cac791fd568013eea27e58dd4ebe01e5a66ec6189a3836148125b58b8428542cbe45aaa0541bddcfee7" + "0041" + "53ac8da5a485ea225226839f168a0b256f91d00a011afee18a23596903ba8234e100d97b99f32b6c8206e860b0ff4d294ff73d9e67ba" + "6498" + "385b8ac567ff3e18d8a2a2a79eb3668c1572d5a62cd7a5e46ef6467141dfd7cb5ef0f7307f752f4ff0cc57a2a8c6e8d0792bc9aad7bb" + "2b25" + "4902613899ab35977f751437a0d583584bc7f37ecdb5c1281a2a62e6dd4a8d7383144f143756f2d0c96945526c54734065f54a091290" + "324f" + "0acf452f3d8a8820753f4886c342eefe159738052c2de003822c3473b0bf17cd0953b1a9b37a1bd13b311cf7bdb295cb6978554e32f4" + "2222" + "ae7fec12c4d28775ef2cb53d7b99e33d344fabf01ee23458bf63555b4bf709c05a3f24171a8c4ec8c370eca990fb954ab9ce4d86445c" + "7e24" + "16c61a8d5a3bc2222c22e31a08bea9b7d6505ce242d5f7c2340095ed0758971080c235807cac4e78eeaf343a9f24e6a1458e15b26ed7" + "2d2c" + "1dda1eb977ea197ed474013cdcf3315d3ab51d783a6c46f5b412a69d285c79718b66a4b655a36b3509420c4ef3ed49a2895123fc6613" + "e224" + "d6a11663d8bc40b8f831b919b901d18b518ef2fa3ad41a0d7f9b11a2ddc388a612969816f7c980408e8a64e083cb3fdcae6f1d3e1aa5" + "3ef1" + "19de5c81d11ad9eff93312a8cd685773a794e9755e2b339f57d483df2f9022a8be8d2b883df028506e411c4e422b2b80330169d68927" + "c0e3" + "d9202a88ed1ad0cc561e00875e6829263d1386cb6cdf0f5ac6b13f158b012a59d06931c82148b8eed4b6159bf7f68a640daaf15a94fa" + "4032" + "0896fdc31af5e1204138ead463ed2d2ca5dc2f1e3c5e64f8a56f7de2b86f8ac075b89aff42c9544e9e2c064febb3d4c56ed1519fdaba" + "9669" + "993fc9387e1e21146ee0da4a5cde5be28ed92bd6391925ce90deda54d79e7ab52e125081659f74af8e06c1217b32e84222c5081f20e6" + "9c82" + "87d09a2bd3c84724e14668021bf59ac4e6e8ba7a28c089b4f5e65f8f86c63592d97bc4d16549e034e44d94bc5f2f4866da35cded24ef" + "3b92" + "57a8b8068eca7fb9f032430833141996a7d9a17bfebe05b6850bf4c044c0373e96d8b72088e93801817d988d5db23ed3fef99e35c680" + "894c" + "6cae023ebaa14197ca6da0c4735fb12da676a714077afb155aeb4adcaece2df5d5e30b54dc302b51d3b7b54de5eb496f1bae6681b745" + "7c0a" + "05bb64c4799faf3d3d44486027068a1242e6627bf14595b6f7f02d2d2991cc0d97667f8dbf2394703c9de80ab67adc11e3af7172be95" + "75c2" + "ba5fb7d0e260b75094fb0e051a8303b92cba15c54300bdbadf46408bc3517d0d1fdc626f68b237c5d8e6590411d2f91674ed26cf0ae9" + "f736" + "66129e0f0ef79fc949a28c3dc1f4f19e311ccebfc1558c6f8e8ae57711e48a1288ef06b1a02bfa36a56682770e93c30255391656ba5a" + "a449" + "df16b614c28cb162f2d4d42aef7b6bca4fb3695a0691e7d90e94c4c10ca7ba6cc6de567ecc5f73aa3386668a32aaea932abbf6778adf" + "0cf0" + "59754a012d1f9ceffaa982e3a6b7bd5abac19cef27745418b902a866d29b3af0334c7b57cc30e5dd89f0ea9bb1e02a73efe4ab2e32a4" + "dc7d" + "c6b551cc803f0ed04b8e810bee82041fa3f6768a2119c0646d10ccbc83c84099284693332ac2882f44057e80042180042145000578b5" + "0740" + "0040066c1b0a2d00010a2d0003145196440dc78a79f4439577801001fdb48a00000101080a7e069e553740bf25484806cbf464708f9f" + "23a2" + "a08c13a3342490ffe0a6aad4973f63303e44f852669a2ea5c07abecfea49111c184281a096edf756aa96f7d87fc0f43d8292e8aea230" + "d342" + "15dc876bb4ae46b44e537ea28f62b4705c9f28f6c0c310fdeb9945051583f32eda754cc01106668b054cd4c5d408f711d440458d5268" + "85a9" + "7541b254ca03a4b486cab92c1509309c0c80d5dafbedb5b541067157c3064059e4f7724278162ae3f9e1372c958e4588dcbdcc8dbac9" + "8087" + "0f54e2a04f350ed73d0c8b11f41f77419f08e5c8baaaa68402f9d88390a15ea2f402d527237fab9ccec2263efa27a5d3d476b102e18e" + "1737" + "7fc6059cc3fc0b81841de591f6c0c7bef38bc14656b9ffe8c68037bf9e11b8c9263fd6f03c1adb4b4a41fe07474b48a72ab1b3c801e9" + "e2fc" + "5664fa17e08a6af66e358ea128d6c4829fe8ede92f4b9d1f376f94ee0d13a4a937acf7e64426acdfe17df5165b3d5dc92b572fcafce2" + "6f0d" + "7ea4f411838fb072fe86a9c612463f144c51cb526acb852fc402ad018e0e8cca61e39197957190ea34822c052357ec09ccdd6a622c82" + "99cd" + "cc609188a66d141a0dd77af8985cbb960098e976fe85d526a19455f79c4d52b591594b21fe7aba09021f46e31d2d1b709e101223ff75" + "b9aa" + "17e07880f07152a9f6cca18501221eb6c1b31fae6ccb95260652cc2a74abc590f141608148419a966fc4f77f402f07f5b39a3844e64a" + "afbb" + "7ada9c37f9cca718547618ff38d6472d06e4d3838e7ed06bae797cbe62be020d66a8b92efc17eafe9e4c04846d9aaaf304c54622559a" + "3102" + "7a3c7ffbd318a80c16e18adffd147bedfd1febbf4f9bf7ca7fe59b4aa84324835834cc5ac484141f2526ec7bf8ffd41cb91a6b4fb562" + "d3a0" + "cf5dca498406237677db96b300dd153478137629e33d6be4547ca5c58322e2e2cdbe2554ee3517b6d55c6da370dd7958131786c92091" + "3591" + "9e372355300a34153ededdf490c125f68399edaa8a2d8824d088b2242406aed1c32879a288c9e57aaf3dd1ccf1049b9c8ecc013b899c" + "d25f" + "da3ccb0ce26c06afe6704486f5ccee1250f87816bdb728451941b4bc23be69a78bd732c3fe5fa5ae74f97e0c39c2596e1f7a63118bc2" + "a05d" + "51e6139b9a18b5e05f6f41e3390c9d3650bf3d1ea2d64ff3f016a841523ab25b6a489dc033546d0197a2be0e5c91b35b7f86d4480fbf" + "db5c" + "ab57b0d797a7570a626642b672130d02b8abff928cbc608f716a3a61fe0178b3c7dbaec2b8b785ccdb4291f0bcf468940f5084959e6c" + "97c0" + "93b968022098e44f72b733e86664e40af6c06b62f39816ecf61a753266e8b5ed55ef738d31ace2e5067043a93241ee8ec22b4cd7b71d" + "eaa5" + "dbdc9f058254f3ea3884ba4fe33c3e2835a73f28ddb02131c9ac4b96fc334a9dbbd9aa954916cd83cc38d32ecf5d151acc63590ea97c" + "5e36" + "7d53f8c696e67c86dd00a5c69ddb3fa6c564264cd55436359c06c19895d6cdbe18798f438496a01441a4917f067d73bc44c31bfd75bd" + "1afd" + "d6974f888413bae0100567d2416990b489a23b9d6026fa1c80793803493850e9f4f103a48c9a0bc95180fbaf49deef7a14d7e0770eb7" + "6448" + "625ada8074666a6957e2c5ba4bf9e052e40ded3aa4d09e0e26f87cb5330be34d29f1a5b2474e3b06bdcf9bb73ef319bba4b5e04707e8" + "7154" + "48c56af97a585b7ff4b40b186785ff2f4d58a6c2084904a68efea64cc5b05836ee1dff82c42ac4493c57ce1736da3f87f4336b57132a" + "43ae" + "1aeb96f847d62eb36d2644f5f66b7e23e638392c9319fb0d05849340e772dfa61170a6dd6f64f7da9c42e984cf669f46a3c2efb2e964" + "401a" + "d47fca051249315b0ffb602e6231aec6e10bacb09a2c1ac275c7614a740d87345d54440b7440618a0eb4e7b0d34c38609844057e8004" + "2280" + "042245000578b508400040066c1a0a2d00010a2d0003145196440dc78fbdf4439577801801fdf0a600000101080a7e069e553740bf25" + "133e" + "c28380fbc86e6046d00e227bfeda912f0734082420811ebaaa0d46b71ef639010128de548cfa4bd17084ad4cfd3412c972384332e606" + "9682" + "2ce8c8bd03697b1e6db8257b516944122c04150c3d8c4226dce12997563cfab8a999546082a26cf0e2f5e30b6a7129d89d7c6ece10d0" + "9115" + "32c5e374baa59e7c9572f1494a1ed1b7fc704a616ff47a3fc472691800d2e466b2cf58fd43c8ea442e30e9e057d2b9bd8832c9d80528" + "17e7" + "34d76c146a57772a6b9210ffaef44e948029745999e1e3cb315110614ff1fd96bc8e7dbededc52c6e46a0a22e14c64fe453ff3b08ada" + "e778" + "810a3dcf06cfcaf132faebf6eed0fae43c92fde6a7adf592223abc50d78f87e0bbc0cdc58f3bdba8e0b7bec5d192d8f8f2a5122e5c4b" + "6ed8" + "4273c637035aebaf7581f97308bf241a44897fc951631e6a72aa207850799956d4ec069b8783bdb5b269b7cb5ef46aea4f1c759d6fda" + "4ec7" + "fa31417d491ae465255619f9bbd02a34ef5fa1ffc1d458d0ffaa4b2f85c3e164a29407e4405fabf1c3744e69a08ea516de6d2cf28e94" + "9c12" + "5445fbffdb35745af37023d095d86d701347fa725b32c4c14af69b080d9d31babb072f75d51bd4bc5c798c35cd1690b7f92ff7dc81a9" + "afe4" + "202ae5d331e022e993fac68bcddeadb7d81dfb80a867c2f740d70b9520baae66cbe4f465ed5df3789cb073b06c16a3e1bb0c31fe2e44" + "9223" + "dfff32d474b90756dfc3f3bc9798baf9ed2c5d9556ca45e1d4573248d4768a1a29c3671e84191b6569f0589eaa4da7d72f953d45cfc3" + "80e9" + "367f0b65877419f71008ba23133d4ab43a42ba792e9e2529de020e41759ead35a80401323bc161b2f970df16acdce0a8d9a2547589b1" + "3684" + "66a8ef226c100b848718c460580304bb365e936533dc868b73ae5d21adf9c0b836413f7aa0f41b616497975798d4effcd027da635deb" + "9067" + "c7352383c81aad06a8f4dde55ae6bc5e9b8dfb0e73aec189f51e7d691985b753c8474a55a8f0f88725ea052232b60600b8c9e5ddf4ff" + "5302" + "a99d283a9de75065ed41851ee6631dfafee6915602b9dc9b60aae7327113bf048ca581182121c3e0c31b78f44ae61201ccc8a6286c12" + "aeb6" + "38caa3507e61d36a37deb3aff8c73e8b73c03519c6cdf5638a4b5f238c3f8663ebabde48a086d450c9bb237020a9a2d989e62745f98e" + "fe7d" + "5954cb58ed05d138aade6e8d24b0562a7f8f26d382c8e7c22ff4545587814d41c3898805e808adfcc4a25cb2b75629bc01a17f395fc7" + "249c" + "b83891fdc212f7c69e5021758931fd5c370b674caa89f19aa2fa9dfa5f52e518a88bfc2c5ae5dcebee4944c86946d0d4da2f5ca05668" + "ca19" + "9b011a822502d5a0734db7b7d17b3262a0271a01279af7134562635bfd1310a123f240a41e70a88d5daf1d40ed51c6f1593f44d47291" + "a865" + "564eb2f1d4f52694a1687f16f345a78a4e15d5789fb60fdbcb361249d647c65f8b77fb8e7c32dec2a6e4d83513ac4a1d76efa6d66078" + "ca46" + "265c1ea6d74f5c9674239514695a2d6d8645e114a6ac7bd4897b8bb880fe1ae70e4f333cd0e07cd495a4b284bfc8a630fed02c578fbf" + "467b" + "33988e9929893385000c54c6ccb559a9feffa3ca2cf103ece9ab36220f8319e25d22de3185534c090c72fa37a6c168b4c0691c3f1016" + "6412" + "8893da7a5a549533ef720626dc12258ce513428f9f341379e2e83e235fe09421bd40a94b1b3bd56d11751b5c4e4d43c3df92149ac07e" + "1881" + "1995512c1ec06818ba493c169047d7560462d2ae6c823d225b0ede7955e4f4db22f38bbe4c0c99805ad7cf7efddc1dcb68ff29ad655a" + "dc5d" + "b48a9635c460a63e09656b7d2a7ed3d8633f5f0baa94f5b5a97cc20d21536cfd23b663f54fcfcf4d85fd155f783383d1f54bdf178bd1" + "8141" + "1ef644057e80042380042345000578b509400040066c190a2d00010a2d0003145196440dc79501f4439577801001fd2b940000010108" + "0a7e" + "069e553740bf255a0048245205b9248caff7f8b7155c42d1d8dc46e1f2aed823b2495eee0cfed00fe5cf3771fb7ecf68185661a71b37" + "99ec" + "82bf5cd3c646d60b7bad57b04685950c72785a2df47457a020a8b0a8045bb0549692f595465c207776af259d754ba23e478bcd3700ec" + "fb77" + "3592e219b9d4a631536c28c09a7072e2ef254a007bddaa526e95da594dc188f02b3419b3674b7be02886281b3dbfd35ca9f0c34bbae5" + "49fa" + "27ea5287df1a2a4e9becf0c38b1107b5854493f27574687fb7b7b89b714809e7b7bbf9b69393a430a01721116bcec2de4a1363d2336e" + "c4e9" + "08a2c8abf9bf6bda8197c8de793692505e44b7ac6d1f962c71453964da38413720b92b2b4c79ef20a0ac011246fb78ee914b1abbb905" + "dd10" + "b5c3d169d045d464b821431df1c0bfe040bbcb2402e2fd02c24be8d464f4eddbc5a43878a4709f4ba8a09761ae6aa8dc53f181ac2648" + "24fa" + "827001461625f289c5b25b3fc72ceb346f0abeeb084bfb8e95042efedec1bd84400ff8b70505d2b21a8456b2c21480ea8d5832ed4c97" + "5535" + "7693e848558710530333aade318759bc02527432fa9d2277bd275d0532d00e4b82dce5569e87426556298bfdc58fb823a9cbb82c612a" + "f06a" + "966d11221bebe6fd536dbb8ff173a93f6b8c11edfb1782f8afac8d835fe3dd3fd9ae2295c40f510d38d2f39e064136e17225f50ff634" + "9268" + "2399e8344b1cb7066b98f1f3f99156d9cae29a42d5f3e6560350ececa558f648a73674a0b04493e458f8b075e3e39f2c14d79c0a330b" + "6561" + "69b84cec386c6b18c73c07102d3ec4f6db7c2020ffe81a6487703fcdf00ed786542c841b37c38f5d15f1c146fe63df462a8b14608946" + "dbf9" + "d5a51b3c6974837db4094fdf3956d0f7bb89db087b8a6473e4556f5e120adaf50489b3b6fa59f17e6d991585f6f0b1d4722d2ee5569d" + "cac3" + "22415f316082e81f34a0c4d4eb698120470c641f2589415b9efdfda8f8d5c2e7885d3e427d44539120fa67403879e9090af2e37c1fc1" + "eea2" + "51c56f6ef9a4738d10d27190a3c9628887409a0af60e7b6904d877078201f925034c027e58d06eaadad9d30e7ed842cb000475f67e1a" + "f926" + "ea7cb95e2ed990b4a92bfe45be1a6fb5856885f2c1bcd4ecf7d30b234cf4aa57510ba9ea5615d81f4f5233e1ef8113e461fb4b4f33f0" + "d641" + "e4bd72c34fb822b7631d31e44d5ce2fa0e0daff23ade30a173fb21c9ef1ab72d810a13b979c50108e7a616b3571f037618c5e19be218" + "bcae" + "df349a42174dd455ecc459039eea00123f8fd940d320ec1a3a6e793c8fa8202a36ec661caee265b99323cb67970048b1b0c92370339a" + "b5a6" + "3f92ba26513e17958091af1c950c2c29d7d9e2b21d70d96f19dc113199a748efb00af71e7bfc217d7167cca345a3b2751cf9afe643ca" + "dcc6" + "3c846a78ed2d70a7ff54549f9e753010a88046de11158d2f201c42b877fa8d871b1ed8cdc7af523f553bbc4b754031bddbec28f081db" + "2396" + "c6af0300e6b9b364d04198998c5cb3d2d816b82bb72292625e8df7d8912426eca4ae7753ccabe938c174229df27f745c9163c547bd5b" + "562b" + "0f2df75e73a4ff56bddfc660b77d51decc1224658a88e36d587f4e3a0bb700c4efba3b2b015a3857c25e50603ee93060f8dbf262f38b" + "2ddc" + "23e50e63f0d765de27feb4234acf5024582186e017f54efd8c200690bd04982b0fdfb98c2d5142db1d7db49e677d0e2022d801bede20" + "d590" + "2c34644df3f81e777043ad47b4996b11a4727854b0d20d48adc516d26372d15293605e439c4efec98425e82d3d9757d2b95e46ed9d6c" + "d0cd" + "581d986d55b396c3c7b5af049015b145eff9316829f4ba82636c0f5cfd64e6d84e3304bc22ad93c7f419d528a990b66ae71a563e2140" + "d6b2" + "1e77cc6212e46abb03909c44057e80042480042445000578b50a400040066c180a2d00010a2d0003145196440dc79a45f44395778010" + "01fd" + "ddb200000101080a7e069e553740bf2560716ece116fe44e5908e49c6dc7d0344b8b66b78ba472d996cb49805c3b6180fb6ff71fee8f" + "e5b9" + "9e322aa2b28b2fd5b890ddf3b773098f92fe06e1fc68dcf47f18a952d08cbf8410c690843fe10f3dd08c3d55843c7de21f785becd9cf" + "9cf2" + "88582026f24399c8b184e3590087d9cdc7e61f660e92f9d242ac00e401012714d82457be817d6e5385b59ab4b593cfee2f8372bec443" + "9575" + "6f2c1ce4c5e9bd86fbf2928aca6a96e05211993d555186c7d34ffca2fde8894e78de7ed273af95e65ab0205efb8a43b6919851ff0722" + "f160" + "eda4b2eda8214454315cfea01100db6e36ad2670f3eadf2c04b2e5e023287f95dc7fa5583b4d7d967016678f79438836989786b3e878" + "02e0" + "bae34dc9dc60d50faf8eb1717c2360c4b2b4ed2196c57e1c87493c5b30b7cba943acf997db34b58ac7ed788adcb14575080716303a2c" + "8d82" + "02fe70faa498d1d1a1da0751a853e63e966bf4fc06570910d36df079fed97f6a7dea4b09fd1c7464500d14e8be5b36620b5331329def" + "6ce8" + "ed7eebc3fc85bc316e4b47b8b4996172586a4cf41423da8361e8f3f1eaa2096966da7a086752d207141f098b1b622ab3191b5e59bf12" + "08fc" + "2e63d52f712e1201a78e6705a7fffa1c4bae3bdf336a852cc8cbd2124152fc6cfc4d690e28fa5f3dbf667599fabb3a3d524f88030973" + "ace5" + "6cae4c57cbb829f76e189fbc3f743521e5fe75f782b0619bc313e4d789e2b0f3f37ea443bce572eddce722c9949527949af00b40ba30" + "a332" + "c6fd0dd96aebfca81e29c5dafb73d99aec483ad380d92e08798846ec67519661a79ef63e0c37c93208823aefc4a8e6f313e4c54c623d" + "6fa4" + "f6bc3216110aa3e457d05864f0fd89b3f2da57aad3cdc05c5e4dc73549d80a61cc3b1f5171aca79b35c1f1eda3b113fc635d07b89233" + "c46d" + "577a1a4f8243ba23f5c2b57be08ca992d14e43b81c277ed19b85b903269dc877f0d18cd69c7ef7b7ef8cf2f461c5afbc9f9b679adee5" + "8e53" + "651f1bd28dfd3480171cabec6aacf1919ad803fec869345bddaf8fd4534333ceb64c028d0c17645dab653263ecd09aa9add9639544f0" + "559b" + "9413d98df6ee4ad22ecd690bd15f6881f98afe55d56847a1710cb020a67f22f567f7dba73dc9c9c8e31d1733c1a0654f5b49f75be9cc" + "2cb6" + "401aba58906c5b1b4da97d40bfbecfa55fe09783b560b5cc3dc28f72f43e167c5e8dcc9836515111657cc7743674e8c6d38595e8f186" + "0a57" + "ef23a95e58340cfa3e60c784ceaac7612de4c0789f9baa2674387270d1fc2fa522a5246f4e6bf53ab935a87e5f790f29b083c10841c3" + "720a" + "233f21257eeee31a8b987d39a16278b302869afb3340f9dbcf07dbaaebb88a6f143adcc1273d44997a12c52322b65995c086e5574937" + "09c2" + "990142ca039aa5de8ebeedebc482143c2154953c79a59c0777238479bc934c2a35278759813df1daa70b1f508560c1aae54633bd8196" + "4949" + "e6283a646c810fbdabab67030e7dde8e2f3f8e974b63884e4a62ea58477d79dc2599b98fff4ab84f6f5a7312e3f4c71727b00930782c" + "59d9" + "366cd99e38f71088d86d22863be20e88fad8319608a882ad8b2a4b494a7a55fce46de429b5b511091f7ca2c5fdf560ba937f00093d73" + "e77b" + "aec14ba65884374e4d73dec271748d1da9802d9f06c2d4ddb8faedf50964f7ecdd10d850f9e62a3fcd1cf8382b0f3a101d68cb807329" + "1c46" + "9b16ed8ead94a0f21b663c712c299acec7fcd3b3dc0611cd7722979fabc089a03bc9d5d5459d08c53cb479c16360d6c5a31681657806" + "f331" + "77e34e972eea75fa843a68a8f0570acf84118cfa65293b26f4ef6c7393bd2549c1a946e0e01fa8fe6a2e94fd04bb24ada5fcbaf41245" + "20a8" + "8c13f270eb548e079937495dc505ddf5583c9cba44057e80042580042545000578b50b400040066c170a2d00010a2d0003145196440d" + "c79f" + "89f4439577801801fd7a8e00000101080a7e069e553740bf2517c92d2556716d2a974fe272b0c77803f94a04931bb9f4381f66da6e72" + "4817" + "316ecf5e9df3f873ef661bf3d97f0d0cf7f5811cde2881a3dfc33e25672b9f48043fa4211c856bcd0ad44179a979aca031b48635ec0f" + "7d25" + "75c5fdd1d8f8c6f9e3d8ff2b6179947571bb6258c0dbe931fc33f2e0eda5f4a5d0d4c80fc71adf3c2843d890a21390685538c46bfde9" + "cbea" + "7ef798973412122b30a61d7744103d813c58dfbaa93634fa390bd1adac7961c613021824a355a6ecfde1be8e3a75f8f284ac65fdf447" + "d913" + "c595e09cf38659aa9d1fca56b83a7d616abecd29b757d74acb442cbc4d3fb5d51e35d05de2d9d0d1aa6ba7a8723d2839e65ac6d8d87b" + "7fef" + "9e5d35373353dd34149f56d42b534311664f2ee1a612862f5190abebe4b19b30fc9c74f2aee0c85383fcdcfa1bdc00fc6caae32dcfd6" + "118e" + "e503e1d1c0bcc5dffaf546c6c7c5db9276be64bd580f6c4e5d43c3c9d71c536a37e46b0b94977fba6a6b26e9f99ad8a79d53d5f0fd1c" + "f87f" + "cc80d591f02fac555de83f9eef31eb95c1550d377143135fa9b1af56c4a37788b7221c4326ba6e85eddbfed8cde6adf29b6f469d25a2" + "158e" + "a7f2a42357fae6b4cea6c563c90b0d38a482645bcb5ffb4444e938c7964a267443d7e4fad058b528b40739362f7964bdb452700cbde2" + "f2e6" + "22530b87320dbe143ab63f080f8fd3e0604e8a2d34f032bc26cee5349316d71a0014fa6e42498e3da53d3139598cd45008bcca7146ed" + "50a8" + "896217435a92c319696498d63693fc79f97f80a66ff0c8cdb1364ad1ec81b381fa05f2983113320fce491f3c405465ffbe9f93c29530" + "aa28" + "e5ef36b5c6f4ae2d0268eb8bf2d3064c159651936ad0711eb4d93b712d1a433e220ca9c6a31a99fa3e15c5788062d76dceb279b4aa22" + "3dcf" + "8201bdb63f4cf4b6e89153856d01c3c681605f4112a9815cd4bf4676de50749cb9a34360da1e8c82f170a5be5b30405bbef03498bc81" + "99a4" + "f602981894537eb9bb18034871a3d9f75ee0351f33950a7a90ba4cb9e886dba1778c5601517c97b43de02821f9de2d2d0901c76176c5" + "270c" + "309cce1472c8b3e04fccf9c3553285ef97ef36c1fab2d9872a5eeddc4b1d6774a86ee42ba8d03d4d1ab3497b99eef7681affaee0ee05" + "f1ee" + "368325b53d14685998cb1ceaa7aa83b2a206eb788de6612cc4689b337b5cb30a90db6e93c0ce8f1e87c64749bbfa94b3062522056051" + "1200" + "fc9d850d8349f3958177a8e7c0db6e918b712190658f56e49ddf771d535a3e10ee9d037be680364a55fa36dbaf306a4a69ca57ef96d0" + "c99d" + "c09559b8414d08e842cbd7315233b22868d8e28d1876bc9118a515f28b8ce8ba1a87ea859f6c0839be57fdc0fe5ca7da5e7eafbb7610" + "be46" + "e197f0d4b76cc88fa7bc31bc88cbb7c2b6860fb6dae60569f96045314ccbf7f8f9c2db289781cde585a2fe619f6b2895a0744e83a4a8" + "f454" + "75a2af469e7cd9e29ab5269f372fb4a1dace9db078e55d8e4292c2d82a6486510e9408045a92375cd6bd3784d3189316118b0b893973" + "d696" + "ffedd2f21a18dc75cc57e92601904ac070ac2638de0b93c9f415f36b714be6132b72ff80c9bcc25a2892953814fa5d108dc71e426a8d" + "0810" + "63e4f98c7a498370e853bac5d7b0e34a1664b9977954cc54f72c9e117be2173589061f968e515a5728f8e9116c04d1311fbd4a99f20a" + "2c7e" + "7e30403f2e5e6af5f1edb8d16903ba9c10b7e317fbaceaeab34e0d363814eb8e73fabc43d12d7a9f59dea1f2310b4c2edacc0dd6a3e4" + "72cd" + "1c34deb20610ccb2a3bb83d3a854a04f67167ec9902f8be601ef580d7944c53da98527e073653f23827cb2240e7e1a50b91c61e36559" + "59e2" + "b27f585a8a6855250a8b7020f8bd3ad70733f8ac3b4a4e5b763b09e25344057e80042680042645000578b50c400040066c160a2d0001" + "0a2d" + "0003145196440dc7a4cdf4439577801001fd41fb00000101080a7e069e553740bf25f3b2cf587c3a0d805c02749c1658f48c2179d4af" + "9723" + "dcba9ae7853969f0c2c1d29090aeb5a93d152e3375b31cb07e670e54473a1f099c8a1e4dea6bb1398d0ef034a455f73139650b9c81f0" + "c4f9" + "7186684ac8d7a3a3382e14068503bb5c1d46e41d1b8aab4e0762ec09aa0a28431aacd2f358ebebe4bff753cb5580ca577f227252bde4" + "e191" + "b8d752e77ab5e1ca81397067c8ad4cc42305ead8b8594973e129378b6fcfebfa2a2434bd4639bc2dd08627da8df87489952499435258" + "7852" + "747827f21f267ac867f0d5b187838759c8e21c4ccc5a62687f7abe988efde19378f19b91213b0602f0d780ccabcc5c66892e390f14bf" + "8844" + "6722500eabde9c406f10eb81cfd09ed927ad0641a416dd7218e4ea765112dee1923b8e6fcf849fade0346df045fef523c4f94d010513" + "d689" + "d4f1afad86cb164674390ea9c32cb60f653e5953ca953c3a1c9c580f90eb2d67f8ab03152be8210dff0c19cc08959d1eb30b63728ab6" + "07b9" + "0e0cd8715bd2921ac587be877c43686ec21c8a56b8fcfa3bf8c9e933f5e83ae5224ae09533adb9a8872abdddd3a40ca00ce84c63dcff" + "36bf" + "4d0d3ce5a49e26f099bfcf0f8ca3c863f139cebc48d77c4fcef38336901166aa3d2ec52eb5e65a8f49c7b601ff93c61042688d6bf00d" + "7959" + "5daa89ade8af0b33d3ea809c6697f37e92c0dd42b45f58da940d534a1f39cf8346c528d64f4e224ac9160f398769017178403cb4287a" + "3ecf" + "aff1543503acf9b2d33ba69f125678011565af6f1259ab7dce10badd517818fb26320b0f27855d2bed0a4b870c8241d9c1cb566c4830" + "988f" + "818ca2a09c0342f475982ecd79c6cdea4f9cbd3388ce451128b59a24dc34dc76065ae17ee02356c3f73bb8a62731eccd4dbe6c11b366" + "e58c" + "789cb7ff1fa412a4d3c39adc3baedd2e75543d5af47de582099d443e382452a4bf6f0b01098b2df6a74226017c7a857d718233fec769" + "81ee" + "aa51809e70b12604bdbe58140d683f4b233a7816ffc3c52788ea9a62074d180cbe55e06f5b2bbc64f4448020ff2c690a01e460105f28" + "9de8" + "efa237bb36e732726d065ed45b364e65aadcad483e4432ab36017630134b6c2e15ccd01d77686750f4716c109421f4dacc5e671e0a96" + "a3dd" + "a032fe2dba0d24c8b3b8d1c7b735aa10949bc77cc8fffcfe079e121e5ea9a395b7ac07144dee4e17f25e0df419d0089124625eea887e" + "25c0" + "749bbe1f6a939740d009da408a3abfd8765f776bccf49ef916da7ae6013318a0869eb9a2ee7dc5d18fa616d5aa47a89e12f0184327d1" + "4a70" + "f4e0520a10c35db213c1e176a5b8a28ccb9b3658c125f2afec473d74ce2a6f271895abc1dffe332843d552706f1723c478ed247348f2" + "fff2" + "cb2a1f590ff1d9527fcda429811bb120c32b90b6f3c9d3a6b22ca7996d31c6898f02fb481f58f56057999543e2e9d296bfd41f55f572" + "7bde" + "0b465a7205ed8aece49465ff2c5dead5204ff6eef34c79fdc022c9494bc8e37a5d51228f9029240d308f157fa29586aebe81f1cceb08" + "05bd" + "fe65545386acc385fc4bff3025592fd86e0c618b17de2e20fcbca26de823ea2fa06c820f86c4ace9e48199dd5d1960292812adae90d1" + "f4ce" + "f699427fca67288702e6f3428a7021966ba1f580a2ec8a50a8db84571e0243ba87137841801cc6341a5523aa2ca780a2adef96d6ff99" + "f817" + "7257206c1b8ff6e11ece5694bb8e4dac6a0c2d208ca67fd7db35e8cf53b6eeffc2841c8c6a5293495c28a17b4c2c3bc82e4952d0b220" + "68d0" + "7d28419c886cd46cbb4a67284a9b684fe94f7bb0a31caf435395b37fd5fb322bf8ed0fafe8641f1a48e600749aa04e7514d6090c4e6f" + "1b69" + "ff270ee8d8466e9bafdb1b463c5d7b87d4f3b82cc9b9207fb15dca35f072b520b66bf5bbe15f44057e80042780042745000578b50d40" + "0040" + "066c150a2d00010a2d0003145196440dc7aa11f4439577801801fdd91a00000101080a7e069e553740bf2552d692a9ca74e85ae50d24" + "09cd" + "f21b939c0a29d33a7074e3fb25c32bd63c1c1f35c35cf2924399741e0fb02a1c82d73fd6e911d791f30638db8a9c7da0839369b939f8" + "c7a7" + "37057f67c3b241e6c0096c3da948772a03ca0e750fc9a4f3bc1cb622bb841bf190fb2d6b30a00e87bf61446233bf8a366152326610ca" + "3b16" + "033ed60d552a6dbdea561904312e2aa9654f3fada8bf615a445b95cbbdbd62ddfa105eda2625baf4e72dd6bf5985aceb040bddeadfc3" + "935c" + "6ee17d515c755e22c7d3fa52f524319f5eea821fada5ac6c5c680198733c23890d1ab36a3e37aaaf64a3980fe2e1fc9221a725955f6a" + "e787" + "d93862b9769b8af4c41dbf0f35146c77886cea4203187d6de68dd59d773bca6e57b067e6f0cf9020900acd2d5b5790288729d0552da4" + "38b0" + "0248c0d34f668461f0cae1d86330db2a1711dd17da77cef2466440dec9edba3b969c57293d115a5c0437f0d051d2ae6636c01975b9bc" + "ea21" + "0eb56166af777dd58096bb4d2e70e400e56dcaad8ce168b4638aa24982c23db4355b5034487df21850422651bb541df5adcfd9077434" + "a5fa" + "7968683100f2080da76b4914220cd8b62a467a8f4b74b16c52a08b9d1ca92b3baaa136e73b290dc5221965d2f439e2c473259e745c81" + "fc09" + "f341bb36df316dc11c5727b649eb53fd6b6acf9fdad932cddd68f2e196f683afd04da86ea6854495ae45b55eecb057b66dc450b8f18e" + "b6d9" + "f4002137e71df5aa9df2fc99b5d2763761bc828faabc0b204730a00fc318f78d6d5bc895192cd8df6d4d7b23a107aaed2583783bd120" + "4714" + "90c757187f8edc7307dd011539f7fe17b8b1b22bead8f1d514452498deafb1d3af7e3924bbcdfd71e83050efab05e5d63665d986d17b" + "592f" + "c8f251f7e6b12a4327944b92eead549df5d6675eafe749430bc7ae13ad389954f6069de6e6c8964e2d5434e74e468c46dade097470bb" + "d58f" + "d61dfe0c42bf5b926f6af174fc08e7469e7bcc52b9757255c1f95c44400e4a8573e9a36fd2635d855b392bebafed0d23b6688bffc1b2" + "23e8" + "5df7133e30b50486ee5aa953d5bdac02f636218e3fbb998230b15f3dd7cc7b162d19b9498566feca3aa5ec62eab0d23f15d2d760f962" + "be3f" + "04cda32fcadcb3982cdc91ed6b33905caa7c256c49311e8ce8218d813e7404ee4495198eaf1159c0348c7503a5ae9bcfe07a07c48801" + "3e98" + "a63f87eef515fd8ad1628973064f528381aa61590f91d0ac43f4094c85ed471049c8581a83dd78186517d3b111451370b8f18d77db8d" + "d7ff" + "072ee97bd9b4eb40d4f34413380512b6cba71a8857c6ca819d438cdc24f2441a00de0b90b1a6ae2b9cd8be6411fad125b3a408fb825a" + "8615" + "5973a0b1320f51a84d7fe480af857336873f6c891ec789d48db0c17fc813a061bedfc5d2622d05f31fa888f3b9cfd88e3f141dd81b37" + "6a93" + "1dd4dc0e8ad635d5fed4d327c463b498092fa6864faf737fa18597cef2b6b14379378f960ffd02e1535b90f6e7a244cede14d90d93b6" + "ceb6" + "266bb7883ab72d70aad7541e2f2182fe17da7312f4052a6e86ffd92bafd1d951e6d9c5ddd164e10270e5e627c1cfdee474155a9691cf" + "7c99" + "c90db11abfbd6317ec27585a5bb6c35ac063ba0bcf71799722922e0a8cb0cc4b99bf0df8d38b567ca2084b34e1fe28ac6be3499fb352" + "e03b" + "9a16a616f3f234b39dd3708932adf988fe29c18cb75b00fdfcfc98a9109ccd0835efa04b4b71836cde77d42b8dadffd2b4974100b95a" + "7da6" + "5ee8ca976db21eadf3535c06fba169ac0da12f8361adbd2c359e6da9d897dcac6f21f0902007f698a909d7d190fa9ae5269c5fa6799d" + "d2d9" + "3bdfb6ea7de0c434608bb6d79b34763172233b4c026263959eae59294ccdd9b7e2fc35dd5a208fa076bfd7b87c0f1a44057e80042880" + "0428" + "45000578b50e400040066c140a2d00010a2d0003145196440dc7af55f4439577801001fd0ed500000101080a7e069e553740bf257687" + "3716" + "8ba911b8aad1e5691f76eb44590460d12a085172df37c6c0a31bdb66e760dbb39c20827550480a32c6bbd4203c2eae369eba003c1804" + "7e80" + "f68c6818019fd47799737e65d762f24deb9d244b31f981a48f676be9bf603599ed5b1b2a323cdaa6b0266db1ce6c4e38baa974c79b91" + "b931" + "c2e74bffa3cfaca592b2f5907903b2607cde85d5e629a4b9b298c6c382feabea51a3e1529fe4c93d14c19efeffdb259317fef77fa011" + "73ce" + "ef95ed3f90dc4d1aa10d5d51d4b87b0caaa004163d1940ca93185514dc59d03a2a38fcbb81efc0aba0db4cee095d5b5987c42ea12439" + "1c70" + "a425ee833af4ff8f4bc14f097bf8e27d6e82d75875896798652bdc8a10774d12dff4db2aed1327e44ea7586674acaea77de90c5943f6" + "41b8" + "c6573ff1a9de67ef957e93f766c61b5872bc1f6c409749e0d12b17451186a9ef5d122c726505e147b0165ca9bbbb7d3e92a9b3c5988c" + "1fa8" + "2b2ab63572c1be1901d120df48d0edc08602bcef396a9f641b8a29315d374e8c37a40b1024503379488db2088811ecab79210e694ad1" + "7f14" + "6b2a111cb967e111c5c9be1add0b76f1e6bea3c1d4ca359e517d6a77e07b6b043a3f93a16994807ddf3b9f5cd35048b358382beb5154" + "f609" + "9941a778e6b91f5ac804c73e7c9869016724797eb58af2ce3365bde93daad532ae72e8273d1fdda73dc1ab9cb6a8d142a536f83a93a3" + "d95a" + "7cb8aea041584ff65d027e9cee131e928bc4507341fe6865b91ed6b9001ea9fb4e648b5e68a99a3707d8af931c3b614f7e89d7810614" + "272e" + "439a74f15bbda45eb665e3d15cbbb64b3166c051927a59d3521116e9025a9a6ff61342584cdee17fd04d62325f20a37f810085aba498" + "dcf3" + "67e0c30d43c9f0cfdebe06f1d8552aea59fbecb8e62728f52f19059761474b7fe9095c04f1a271da3e74c907daf6797578940eff63ee" + "3b3c" + "0ecdb255652ab316ce985477a8688f20d56419fc93f7e752d26279b07ba2a6937468f6fb24c9310a6f13a9c4daa763d9c0cc83c9be21" + "48ac" + "9fd50c2d752c9d5c5cbfc91c793a35950b76475d8ef615cf888369bac922727e6fe2c76c2b12fd855564a076ef13236c5adc3f4b1852" + "ed68" + "b19f21b4b92c5c0a7839a331bcbc3d0e2761523d154e1ce2f76781056268ae11a67d831ff6f96a946280bbe67096f1c02f94074fcb3a" + "73e7" + "dfc46ef6c55e6c5fe5ec311a718892e636d91d4889a54511392d1c46207d6dbb26f10e99797504cac4ee6f0d11a7a2fd24bb7e3be746" + "8555" + "8997ae1a3b518e26f592d2e74b5e545cd83fb587e0dd248fc76bf94958819e3c5675d5ec54a7f428b42aa272d155b10f4bd2f2d23713" + "5934" + "b5c7e79d321e31d63f0dcd5149ff95fca67ce0dcefe2d25a3f825950d19af464bb648582d408b3ca36cf9302b3d6806bc52c82176230" + "0f88" + "984b5932cd65423e349bb2c19503dfb768e6df5f6641f4a457327015f6bc1281fb0d28cd5bf51d5fb602ac56ce4e79283b5df2fd753f" + "b201" + "900b1f78b2236dee983d13178ac4c46812c9166078d5fa0f059339fbfde05160a0065906c31db6a22c733e3a4dcc3f07e4d4aee0bf9c" + "74ab" + "b29c8a8d22da79621c815c607aec65065505380bf9421445b19ece70d1c9f07f83eeb8d25831fe5961c9247fc75607c5cdf426fad8c9" + "05be" + "8e253d02f0563255efc4b9de89773ccd38c9fcc81f43811661ca90c22fb100b87254845c7c5e5415307ea4355f8ab55b60b3f6a42edd" + "e203" + "8b0890f15b2a8ca73a8a4ad30099c0a779525ffeb972ed7fdf53a599e1e41fb944af96f1c3302910d3b30cb18f8fe18c54ad31e7201c" + "8847" + "817df67ff2346cb34826fb12482804ccefb46297d4f3f1e9a29f3cdf1be901beb5bc28457838387b33d49861915ffcaf9471520a4a88" + "275d" + "44057e80042980042945000578b50f400040066c130a2d00010a2d0003145196440dc7b499f4439577801801fdd59300000101080a7e" + "069e" + "553740bf256e9caec313cb79a644cfc509c6025f557f000902f99ccac1aace4083fbf8478d094770be36356e2dc5bf235486d148cacb" + "73c6" + "db8d346e74928f238798e9597505e1aa58ffd4b1acfbff1c9ba3c0a73496bc6c77f114dadcbf928dc5a45dce358c2faf7e8ebad82c91" + "1dbe" + "b4dd948a1ed1609bd29e37339e865f471e1aad7243c743937d74ef4b68dd1c140544f05a6b3a75dfa1dc0641e26cd4aae81d73b7d2db" + "0c2d" + "00b83a71369e1e5e38edf5bcc2cdd93bbb2083d80c8b858190dc8a6b5072179b996e4bced210dfe54865f32c8d21b58ec3fd6bdb2f7e" + "ff12" + "7d3a16a0aff9aed04bbcc9c568bd8c60282e511695fb7bd63b070a81d904459520aa4868e81509a3bbeaba5e1bc5ddb769b0f5a0407a" + "a7ff" + "642e991d370c37419f87caecc34f7c0e9151624067a8d62b6d18f9bc4abe5bbdb01621eff39015b160e2ede70d70bafa4867bd302b14" + "0756" + "063605ce2648d08d7c1584d0fb1981224da8c346d33cdab4497bf8f2ebb4631cc7cedb1ca4dd2d31afd164454579d307fb6e60ef5642" + "2e7b" + "c82648be02dd60b0a5cae483a432dc7a9f9e339f3f86d84fd3c5745aa3bbdeb67115799e74ca825d4022bcc2e666502c26083f949c37" + "2a77" + "52dae5f2e1c908a4a1702fed188730ee537493cddef8cf7feba102486be1ce966ee34c627b5382a45ec00bd185b0d2f7548ddd421b4f" + "a995" + "13851c97c4a5bc24f7496a903f226a3d0cc89b3faea32af966aa7b64d7cd6e985c8d78a96081752b14edc0de777dd1e7e77ea86cf827" + "7eb3" + "250edd3e59424b7fe31c2510bcf3b04aedbaa91eed844122f113cada3b73918a8c6369d054e9228cb3a413e11bbe8ebfadb280f1de41" + "be9f" + "ab9017133af8e01ffacba53b0a1b1d1ed82a55620e10f3c6588dee193dd88efc99a3a13fa3c8c510cef96329fe0c1847532c9697dfa5" + "1b2e" + "7dec0c89d4a195b15386360542145f800f34c6504ee068e9be48e10834a6d684ea5dbe5b1005fa6abc39b1783ad5f3179428ffd72507" + "b30b" + "9b7d3ecb60d5e4a84c3e3f23c2d89b9269c9fe6004dfae042cc86ba495ed4160c4adf2509f24abf90ff578b7a007beb732c2c7d40305" + "47b0" + "c0f8d9c4cb480a8c2dbe0af3429f59e2745b31274319b37fd68a0e2fea9144203c6e6c2aa440248f3d9554d062acf8275e7788576564" + "6d87" + "f8419b03e1cd33d025f67a8bb05f41e5e79afba5e933d1dd9117b32e1dd06acb2b8215fa09429e150a0e93c10670c227e835ce9e1bc1" + "50aa" + "a8a589b62167bb1bfbec431e0605a7194cba5abde9f15bfe116d480e76c199cc36dbc8262b0f9b37228f6da5d6815aaa33c53757d656" + "fcef" + "7f51cb25697e0f0589518479a0b0f06e8540b7b79c8b0c5f84cebf76a7bafb79c09cd70608204d87a16c986753b4667678d34d6b4727" + "9915" + "49afa0b872b66be6fa343e1861914cdc5d7194f1ee38997e58aea1b1a37127ac4aaf552e42dd29c7b2ec449888c005beefdd50b20a6c" + "5d23" + "6c66b2bc5712ac2d220a7a6ab8274f3a3b8d7fc9897b0a4d7d2d527067ac27f004e97e2b5ec70629a75d418f67ca91d5840985ad3946" + "5a1a" + "8c5563f51cda8ead83970472e98b48f9113eb7d0de5b234da2505f80706954ee8fc34411aaa2643dca45ab654a11fa205521082d1c29" + "a766" + "c9852431f53e4b9991c70c595887d297d66190907b358cbd8fb768af59421769cde354df070f824e863cbe9f3d3092b8e936e2af5b31" + "cfd9" + "e5126451ab1c834aaeeedd0992f1631086bb8a96426e262e3051737636bce015108f609da528d2b59e244893404c85fa6b04944697cc" + "5cfa" + "27d2ec47c79ce923dcec7e5c87ed1bfb341b34b7bb9f432b183102c3f250f11f5cfd652c307d82b251c761a073115cdd4729aff76829" + "c203" + "f6986a9bfee364451744057e80042a80042a45000578b510400040066c120a2d00010a2d0003145196440dc7b9ddf4439577801001fd" + "afa2" + "00000101080a7e069e553740bf28dc9a0daacd7eee4b1075395cefe971f9827027bb282d9c943c6157634ea85b762d06d5c3f0beeb08" + "d5d5" + "c6c64f52b4f97981c0351817cc7d43a357cde71d01e990ea00111fbab88104de2ec10a950ca8d2732ffab94d159223ebb629c1712704" + "f5a8" + "2de15a4829ed1cebde8236c8c85e6eea7d063bf623218023b5573e144d3a3d3e87dcfa88039eef15b416566b49f32cd28cf0c46fa70e" + "35f5" + "50c0e588054e083b9d0e2ecc1e5997f31940aea9c6efa56a09f44f4646a4e9a41fba6f271dd5081cbe9c18e4eb565ee4c84e759fb746" + "0960" + "fe46a4231fe85d81137490371ae9791b0af720133b112f892bb1319655b58c41c208196a06afbdb8b1cb9b1934b49c7de6654996b365" + "44fa" + "1995c2146d9485dbfaca268df19e5addcdb081fd62b975c8c40e8d32a894ed9b217f08bc1150288b8c388ec89a03e38bad24ff081b33" + "a7d4" + "cd73136574937e55b1988f3a7feec09717fdf7900013fbf90b9d382d56761fc9935fa6af177f87ca6249cfaa06a02884bd415fe97566" + "6ba3" + "78142c17e315f8e67d71ebea10f43690693d8f15930acd4192c282d8ae3b230518457246ff635b418e9d049e7f18ca25dc45972d0b27" + "dbd9" + "6d4a45f59018830d561ce55287a43beb877f7dd07838ed9d65e5b06143b367340e24a25974d66bf464c6660ea8447f2fb940bca34845" + "25ed" + "6d2a55109496a16b3fa3098702570a5017b6a0d92d8339c5a9d86a93a7eea74f06da506ccce3e582860ec890923df9bc0f0a06b9f3a7" + "d2fe" + "ad9f473a950b5ffe76b9ce70896adc1888290e3dbae0432b86a4c77aa9100f2a5f1914164f108c58b38083dee6d13163ec3d7b11012b" + "025c" + "e35987351c157c5d7c9e6be31e6227f1d1b0b725a6188f710a3fad726043be62b51cf2510bb42ff3ea4f276190833689ee7fb8e054f3" + "5af8" + "cd70118d9edb9bbfd578275725380f286faef76e0e804d719d7fd0d7ed70869011c81c2dd4e72bf4bcbae6353e495b5b277647b156aa" + "88ee" + "b2f1cfac789f5a1476a7c7b45971917cb7c4837299798d901dd97716de35bd002aa9d6a96fe06578c03c253e5d79c1aa8de8f2098c14" + "6406" + "0eabc3838f17bf6ab482b2bb8273017d45efe6fd61d3c45cd5d99bb7879906ac90cd7f310db5c6dd659d3cbb39cc83c14a2791b797dc" + "b4db" + "3f6e09bf154c26a7b99055f05571763ac1b596908b70100f5f7bf9d213c109d52b532d507f6c6b2100ef9fe2a577d1208aa168247190" + "78cc" + "abbc2e1165bcb0dc6909916c10edb5a84a3f3ad7aeb38f9950fc63793075ece33ca006c9f874a319efa6e8d05986d2912f5753a5cf79" + "2203" + "29b5518fe9fc40b0c3fccc70e8c4b625eb04df59fe4700caeda2fd6ab5dfe5704c7e39941a60641eb694d62116c96ac6cd18f26aa78c" + "28f4" + "7aa26f5ea47a33403909a26200a5146381c7d91d249236f544532cb98ee84b687683df1aa5357aec12ddb9c87f062af6c7710201f0e4" + "1bd0" + "deecbf5d6e4848afb1925afe219c5371ed191c525a0e90cbab8c5e0fd56bf01cdd1e6a6dd7b260ade0b5c12ccc0b94de2930901879b2" + "997d" + "c30a88621891c21870f4d30064080183b0b86b19a0b71ca7f7406f6423a46c54e0d345891e2d0e9fb0f17bdd4756063b58b997a9f557" + "145f" + "adfa68f75fcb9b97e0c896b2accaa040e195e3ce11bdc15cc59b222e36cd6bcc8dda240ef3b80d76c3ca6d197f65d0f22f1a1e50b51d" + "9f7a" + "7312b97bbc0ff7a18d8cb11ca0a471f219885ef7575c6fed15c0ca110c43e2a7caeb10b9b5a87a1b404a150133f3c681ee30f16bea14" + "080f" + "967c4dd95a0078ef105480277c36aca1dfd713ff62b6578b6268cfe2e764e3967234a6864505c4545615ca12096b9befb66c2941a30c" + "1d24" + "ef52a888f5dc697c96e98081aadd6c7218b744057e80042b80042b45000578b511400040066c110a2d00010a2d0003145196440dc7bf" + "21f4" + "439577801801fde1d100000101080a7e069e553740bf286d06c3cf89c41d28071a658d0bc2016c84c4ad8429b6c8d3c3ae074d8ba2d7" + "1cb0" + "5ebfcbb27f1661ceacd610f0a02f6f01c9939470ebd5d3e8c7ca4702e1d1cfd6feacd2f992cf9371d40173b614bef5d98d5cf65feb12" + "35ee" + "8d359aeadc6e6a09d4868dcffb2801558a312bc46584b607ab5a55e6b0d52d643aea790e2b47ee62c58ec9fcfd2fe214b79b7eaefaf0" + "6bfc" + "8928b7d17e164e2e9baebf3ec0b7d483f17ecd06a33b04eb5c70f3d77ce07bd4875767e0eeed24b2b002781e99f6892fc35a85cf0dd2" + "5399" + "4543367652a9a9ef0c01bd4622d159cf9950a274abc5e869258029a55284db9578489d9209af90d2b4e7557d3d8f587a375cf20cc0b1" + "f652" + "5bc020ec3066ad96c93862453ddcf40cb30c7adccca32ab36d57ddbaf453f3f8ebb06960ec3b60a32d6396aedf5fd1e6b331317dbd7e" + "b903" + "bb82aa03292c7651f733b146e9266e064e0292c5a7f98e936b688d1835a07fc2387998aabb6a5bdd5054de0ded8b67107f00f174d3c1" + "8b55" + "4c11939ff59f3d758949d187753e055656ff9bd99b29b0ea77483a288fc575b51238d3d682657d3dca7f1a42effd67bd1be0b062147e" + "0ec5" + "fc823df361662aea3b51d47c17e7dba5d360abbea2c69304851199692030bc57cb9136fc638d258c8e5cac84dad40c76ae6846039284" + "cf6d" + "b17be5097973154e3296e71e5e9b712dce6db02cdeb5dead8e5a5d0fce93ac403f0633337623b60753381f50a4081d5242a8c4d7eebf" + "c016" + "66dd66cff50667d08e2857786f6848647dc248f71575483cc40bc54227999c600534b4af250a9b4a84917e5d49cff1fe39f8affb7df0" + "158a" + "27b975fea7ad900447aabf7facca9e63a72b5113a73547c443f33184cf2dab6cd7e49f1bc4c832ce9f1b44477b93d10b6ccbc8b58344" + "6705" + "84f2514782b6c2d79b974f7b583960c390fb442d9e1d4f40d3445d02263a096c3409f69e303d452ba332c297f4edc999f7c1f37bb1ed" + "b85a" + "ebf968052c882c31ef7ea9b55905a76b528eef0012292b1dbcf287f15bb95b4c7fe8f0c68341370cc0ca907a9513f7cb6810089f99af" + "8c33" + "7d84b8c3eb9b2c3982557258c0d4e8878a46260cfbbea44b195f2c08425e4788becbbfa77c5a61848036a69468d1b1ff12e8a164e110" + "c4f9" + "224ef92bf949d48bf56ab326337f37f97c8e456ba50478b9ab07e2efb234a59878c341331c534199c6d0abfefca520082c738b5a2821" + "0d4e" + "f4c55edd5aeadeb13ff5d4745a3ecddb187aaf2b6a86b538f6bcfe79bec6cb2c74a5cefd3f6088c68cb53e9489d32a04c51a05776094" + "66e5" + "757586cfd44c90719920a8e6dd62fb511bd013d4d49b44c6fede8bd3bc952c461df78a87a857461296a307b78449a18490fe2a02a574" + "c54b" + "9054dcfe10ffe737493fa69cab8312c8b495001e6b7d379956b71389abbd0d6d4d9afbd235d969f483df1323753073901f0e30d15368" + "c45d" + "3fe98c853be3630fb7b80460524b6a2890daf4278a24fb292e2f7c9f78243593782248c8b2d12d90b5db5d9a52f3eedb7c7c57e5061a" + "6c73" + "b00ded8432609ab28e4f39ad69cb8583b5343680c021c82c6fb9ff63491e250ae0211b6626bc9d24d33e80421d1368ad0223c204602a" + "1cf3" + "5e472ed07e1c1a92c70df114fbbe17542c1b665963883150218d2fb5f0843642b3f550cf66731118cca1f9cada1612da9c039a51f4db" + "344c" + "60e431b3814df9191381fe03d1d4f6abb34408de14beb783f54419777e3c874ac1b72c9826fcfc53fabac402fbf568e1c09af793b3ac" + "0c52" + "83b4c143e31d0de01b17ac376b2519098385240e7471cbd8c9c1ceb80c0bda10076475586a90516739fdec68502afe7392e953e8e2cd" + "0887" + "ffb7d264f2f4705a15b78f98fb2185fca056027db3505cd231caf944057e80042c80042c45000578b512400040066c100a2d00010a2d" + "0003" + "145196440dc7c465f4439577801001fdfa0900000101080a7e069e553740bf28624ee79d2d8b044fd685ef055dac14c406b8e5fe70b9" + "d78f" + "d075d246f2899d48e8f2658280b037d34bf5927a91e5e1ef2b4707d8de0f4ab3e8ce35ede8bef2b5381b32e79add6b418815acbe0fbf" + "d4a2" + "389277a32931ff3d2f2d0e7938c18839269b77612be3d463059e1d561c7bd60c97c922fcd4cb492bf77b2832764bbc9822b09c36ed32" + "ad43" + "4745e7a89e61aa19f1ced30fce83b9172da6b1dcee2fef78d806f1fcf8fb76be3fd41afa6cb040d5c40fc3d75aa97e74e812a0f92088" + "1999" + "4c782788a96370753b89efbdaf3cab58ac0399bc6d59b98d5f755240187ef94b4d85db6c23936b60446c1153ef6eaa9d690e19e8b321" + "b2c2" + "8181ae4e4e338c976355efd6780fd8d1527b2ece5e018d32bfa89a2572e80bb1629b52cacaafcd45721338ee6a692caf3d5ade4f9c92" + "1bf4" + "8b82cea7fdcf26e8861ccc82a6abe77afc43a3b52b19e8e3ea5a6dd48bb47c614f11e131c907fe3eb4eaf62d37759954f59bdc3085d2" + "2e00" + "9fb7e1996f5ace1e62000fbf8e63b4ba6e0395876f42eb485df9cb9946bdbbd42f7b6454badb4f0043c4649ecd3691a3563e365c0e4a" + "e841" + "43d757df3504c724895b222d8568a548b628b7f400f09acc5c3037e866863e8a572692038fd3a7f0b4648dfff431fece1fbc16c9ab66" + "095c" + "a07d08e86630a6ce3966a563a1549fb9b6ea737123fb0d6be4d8b60feb46d584ae11a7cc6df98ebf15d000f39726cda6db3c99b6e57a" + "4594" + "5090996d58580f85e936c85bf41997eab263937d4cb91ddd3e5e618a160a2f34f91273fda45051ad2a2fdc585a2120e5d09fda04010b" + "8a4c" + "b54636a9d8de997b9c0ecbec8e82701bc1e9d18e219efc39e4dfdf6ad6f60b71487b7ab12b61fa74e0bcb2356111baa0c991fc4ffcff" + "9fbf" + "f957ae62e39a6d911fc12992cc2afe71450014794a2b134fc37e848161d01526d2c11dbe2b2581f17a912f0ee8d8e57e9bbc98e03393" + "518b" + "b72ee83d1180ab999bc6116d1ffe6222f0f18b9bc1b3150b52f5909633737a06e1ade1c239caef149a66602f7fbe3e49cc407b102295" + "68ab" + "b3f3a0edf3f19e21367f870449fe433223e0bfb63be430b5e3efca198f1c23d4b6c2d7b90aef2c584169ef22c209a1170f1ac58d1e60" + "cbbd" + "6ec6e8a80235ceeb583174a8cf946d25d9e53990587c68c12f46dc6713d9d05e3ad3d781e61e76129e71e9ae5e70d0010726eb58d6c9" + "ccce" + "3bd86a41161b6e572b1fc5d6e4cca5a41582867df1b72a580fe1f9d8b4c9ac3eb8932c1fce041ecb8518561477c0f2d3c13e13b035d6" + "4b8e" + "c7d659620843b474f271efc8d98d3ebb588bd6a01fc964f69e9521d916976bd9386ef63f331047bf20812fe6b514d27b514a57a6451b" + "26ad" + "7f26695aa19a6df807ea93e095daaebfb20a28ad94a2635a292de9a22ccf68f94dd4c74540db868fb8a33a061f50ac77b093683dec34" + "929c" + "7b1da1e0a670d88cecb710d2adb03f23aae11fa578bdc615f7f5a661d6bacd1117da701a5c641f0dce7273f756ab98699e189d88a151" + "3554" + "8f2c9d8581d86099e8e7b20cb45f451bfe1fcad209171be9095b948d2769a6caeb127285ad4f5405010915c3500db6b978fea1014923" + "c4b7" + "53121aae1e05513e7c998872b08016e889ea191d721730bf4714458785c18f0c770eb714e50f2c3b1b62cf79af864d06a2633bcad338" + "006f" + "cde1e772203e13457ba8aeca80d93bea34285118c98e2a698def66d088ad5d36f4d0c22058b92a208d77072a7ab59c4fbd3295869b79" + "1cfa" + "7325adc219aa617dbb49415c62740745497919f21989dbfe3efe27e948a581aa53b526722c1b3269963ba00c1653e9b92c03969ab1d8" + "9ba0" + "39cccde7e61bbcbad0d25de8e3b1e1f7e59b8c6d837f660bba8f911ca132fa8e211a074a44057e80042d80042d45000578b513400040" + "066c" + "0f0a2d00010a2d0003145196440dc7c9a9f4439577801801fdc6f100000101080a7e069e553740bf2842caeb1585d908b950a399221d" + "1ba3" + "e96439e689b9d46d98ae184d2ac6db54a70e5658b4bb61673aeaca38a8a25860e11cbefeccb9607dbd567cc9f0d07d59e2b86c134a19" + "d426" + "888813c61fd1fbfcb829838f6954ed72881277d00a5c85cac951789f0e0ceab2986addcfbf6eb9ba966031ab5cbd1277deb6438f8d7c" + "836b" + "72b83362b5295b4d1f100cebc6f99bf2acbe2319623b5849ea4f73bc61a3d8bb969749aefc2627be25f0cae587344920ded4e6c3127e" + "2177" + "87252f8346858bc70eb78eba0a42ea5e889ec27112edbc9fc22af418b7fd5cc72d92e5d324220d489ce03b3cbba4bc42ab1458fa90cb" + "8eba" + "9bf389a8597a938032e86d4f2252bc8194cf52f8728344eabc0081bb394a01af9f9496f4bc202132e1b18b5311c38c029bf7c362b409" + "c7ee" + "149563cf5526255ea17f776e63bb06527874dbbacd5e6ea362ca69338d1f704c43c7c84fc876f93caa89e3bb0b2952e92a21112f984e" + "4743" + "e7405560005327443c82bbe538af313d4e45a60d56bcd4d5db54660ace09483205299f274556014f4e99f5c1e01bff35e76319e72c22" + "ab8f" + "b40c1a90a4ea97a71df7f95e6f35ad78bd3638e77c14623897f4525085da241e4ba28bf56d8695080487a17f483c27b775686605f285" + "a18b" + "5945cfc6d5eeab78890f8c6f4d2ff045bb69e98cf5df1f2ab3b199f6d3b17c470b3b7670b95c881e63b7c175e53d93de6a0e1530a768" + "8bf7" + "e192f1790cb3192480e1ef30740b7a3de3f9c48be91df00dbf8e6d0f4368b9ff3b615a2ce101e09485d5f777138dac0e98bfd6df6290" + "fcc8" + "3c7a9423f7528230807e7d15367e58a2989970ecba4d1dbc9eaf324a2fddc9a1d4e81632946016f062e7026bf995f0644e875c286425" + "a9ef" + "7383ea1ba0230f58da1d37d6ab706f587392bcb7079a7b18251a60e61003d8f96a294cc90d15ae4510c155acaee644919ab87b520263" + "d04b" + "c661cc554ce6cb1746ff7820ac2ba6c48f469c4aee62f71d345d60db57bf4c62e94ef6c2d821101c7a6ceeda060c1d09121f282a5e8b" + "fc5f" + "b7d473119eddcc7707afc17e012a551e7535b9e55e0b68c7bdb0dad7fbeb9ec5392cabd684b32f667cf4374731aceba3a3f0f52d1bf2" + "7a6e" + "30aa995daa58c8dd2754a3d8c365cd7ead0071c7334b2ea33e2c7449ce163771ef55cc78c718afd01907121c09a80145df03fda69042" + "9a97" + "5b438d321e5d81f38c6d17473878e6d0556596903b4ac4d6bd54b26d96fe476879bd9e857d88244ff7d7008ccaf1660314b17dd59343" + "452b" + "c55651ff49538c6e63577250cafcb0b1e7957e85b39e3c2286e8de9da442fd73bd0031b7421ebe3dba8ba282adae227ef69310975a9c" + "fa69" + "f7a53139de8b7472b4295f4fd43a021a0a40f9675abc8ac669ee6739d102c5c8453e432198980b512d78ffbadef5704033d18a2c269e" + "0fce" + "94584bd08ddce5a28b5d4e3c67d2e665af92902de88d29f6ea9ae78e2597a2473de14c26af94a66d6ee248e1313018ea3333eb7fa34c" + "d53c" + "80d6a2836592a944c1dc6317856f440283dee92dc973bb6d9adb68cbe4af46469e0df7409e298367f7e654e32a7a465b6848b9538471" + "04db" + "494aa85a38881a00d09b73ff303165f7433946529b2a910fb9ff96b1ebbfcee2f146457e62dda5fb76f6e57f672759eff30c4d281c47" + "4419" + "d3e4ccf9b55a1be3334fc222d624f3b24eb060f243430878dab39242abdb266d5a4ebd68225df80f315a9e150ad0a7269f2a50317b73" + "5188" + "aa4390e86b54666b84f2595770e098529652bb94a2e764e62915192dbef1979df378012c712277135bd109f7feb09e78b6eb5e62bad5" + "eabe" + "20b9aeaa803c5467f8df094977029ddf728e0ef7ba48bc4cd3f07a3f403482cb017e8a173beb6997d514a2497d44057e80042e80042e" + "4500" + "0578b514400040066c0e0a2d00010a2d0003145196440dc7ceedf4439577801001fd9f6d00000101080a7e069e553740bf28dc35f1a6" + "9967" + "9819b7ee5335818b7d4b0fa7b1c575226f757fc87ff49f3f49f609c30295377c1c76f7b452bd33f85363439c89329ed4e2b93f8d7b21" + "9b0a" + "1707f316c5913004c8a02906a257ed767eceb3c4deaa6ed49885efe7734100de2672524f83dfc12bd5d38681c68ac6dd2667e418744e" + "5086" + "853c03a2ecbf71bc0a6fc18d6afc4147444191ce93b5eeb933c862f61873ab7f5fc52e7e7d0ff61331bed29162f16406a58b99a7fc75" + "e7d8" + "9597d8c8453f1ae3cf38fddca569c365b921138d314c4010cd0cb245af7e89cc3199bd1cc2addf8fd19f1249041ca066c62d88347707" + "9628" + "c225a1e149e59fa9db488b3922841b3cade0f49804bb006170890bc736fee839b995bff2238003182fe3d2146188f0615491a563b478" + "0687" + "651c2180338e7e48bb7e5bd066ce0f323b6efb141b31402b65b5cfe3b8262a19766808c48084a8fb9a0cc1b788dee51503823340359c" + "fdc6" + "a01716ff5b08c39e8afa707aed1831fbe2f374e91064ed1f5c8fc0967cd0c1639d544c06dfa42cd5e9d28d15a1c9d09e84f1a1cc6645" + "2bff" + "5ece41874d84a9a9f36885453657cd5227a0c1f6521e6b981a0c5bb2d8dec353ba8845683fbc37451b324568d4b9528fdb3a0450de5c" + "558c" + "a0e7c4d1de5efe0d5c84fee37989a8778cc01406966e9f0c59afdb3f945a8d4bc0a3eaa22b863385870e69b2809ff3642afced72d86d" + "8aae" + "dab8620724b083ed63d900db5f7f248bd79303dbda488135a9f0dcce26407037d5b598c5a49ca676eb34bfd14ba885ecd05353fd29a6" + "32cc" + "cb444e9f5dbf5c16c91f9b16f9c79080391e6dd17320129c8ff2c5cf39f9228361cb78ee8d6c3c43dd45019cd7834e38dee9732ad7e7" + "d602" + "7e736639c854861be96051ec86d17888aebc638b08fe3143cac241a968a6a40c510fba5640339664de8bbd24a92e60b11c55911e469a" + "8cfc" + "1bdd462a77b7eb0a6ae71907e26cd0d991618e15a30d82719cef13d7897b375de55c9c8741441747947c4d7d905b442cf703d8c14510" + "aa50" + "472491fce4b4629509c01c58bd5006507f695382cef4f2d16c2f50b5f7a2b0e2968a5971847cc931a3884e65017f6adfb258a59b0265" + "f84d" + "ea32d213cd181937d9d79d71973dae66e4dea5694ac5d421f52b29fe4f54eac644cd25d046bb3c8039abd312f28e2b7f09d6fa1a68cc" + "6084" + "90ec19da53f65dfae5ad54e1226b6dcbd78919f972f2c5c653d15ea5879e54abd1d18972c31ff83b5629170b50ef8e0e662e398c1185" + "b9f6" + "6cd2a72af6a44d33b985b3f67e848338c70ae699f32537aae27f3bfe3f0d8550578bd2881dc00f9c9d6d5575ff9284435a19e8b6d013" + "8e3c" + "4e235b18e33359417b00a1fb8bb04b043097cdbe187837260a12caae17c8b2b9a2c951ca09fe1bd5ca8ce4870e3115c4823ec44d53a7" + "97ed" + "2aa4f04ac5953086fe551ccdbccfba09ae766f48dbcd13ec761328fe8b2d7955963c39e4b688f32488e6a605ba51f1d347127b306c6c" + "34ec" + "964a77dcf0c36ec3f06dc52b7da8ab0b8a160d3c176190ac2c3f92af1aa8159d444eed22145b6f6067ee1e5d5eb6426539f77f39a8c8" + "86c4" + "9aabfa61bbf74988fcccf0f674e5f01b8722ce5de68504738c14dbb9d665b37c2c82e1e8e545f8d7074eb79f393016373886efc51189" + "4097" + "d9e42b88c537e57d207f4dc2d05ac46ea1a2abcf52630eafc46b0f8fc435493d5f66f58da3c153e46843c8204bbd771b96152b429d5e" + "5814" + "8d52c18780821db99fe87abe650efcd8b5f3a284e3648e77ef52ad0a300abb956875d1cb42b944a3809c5833fd21a70afe5c251e0e46" + "4dd3" + "ffe7216740f256d331b2f9645063e4e17a79a3905052b5976614de5ccfda74920e0cc6cb4ee97d6b57a82d82e5d79b0cd648df5facc2" + "4405" + "7ec0042f80042f45000578b515400040066c0d0a2d00010a2d0003145196440dc7d431f4439577801801fddddf00000101080a7e069e" + "5537" + "40bf28773e58cd4c604bab0547fa1511fc25d10833c651fb906bc9d2af420e0b82cddcb9f9ddb47d76ea9a4050506f3cded63b9cde80" + "3c44" + "b17c769d6aa94a011bffc68e6dadc0df9fb3e49f6a77ef6198893f9324ff4b7c90734c428cc91d9e2ee38cf32cc71da968c97e3beeb9" + "b047" + "3e1d39365e137cafedbe5c417b85c252741e58aea7463137bfbe80a25d34c769124d3be28304e95726ed9f7716cc027ccc5b56e0f4d3" + "08c3" + "685c2581a8edc263a3cb1b52494d4bf2c545759e704451801d00950fac8ec443195907658ea50720794b2282ca51a6bf020e595d7496" + "4757" + "e4b49a987d34fb2aef95cc2eaa241eaaff9eb6613f36d9e009bd834b10b4731961b49085b10e0928c8c5423ff00b677a9beed54820df" + "5f4f" + "ab8cd6f11db0c24ef400472cbb9ee9ff53108d082732b3b11b6e468d90e2f36d231e98b8e9a43008d87a116a92c208ee8797dba79d40" + "3fdd" + "96f9490ffbc75f3e76ddad77b016da732a01841d401249d13aa16a81c63ff985abbb442b464c111fbddae09389380123aad6df536233" + "e8b4" + "14b0159a6ca20ae0e720ff63b116a2405aa10990d801defa897cc96634efee38b8af62180e96b8be83d136a0877bde9051b1a2bdbb8f" + "7512" + "431754e8b2fb54dcbab5916d92db3b149b052b3388c29298868076c0ea714251df5f0a5f6258b96be3844727542b713a890a885d7648" + "1d94" + "c2a7eb6a108299e841ce44aaf369607c76d7f87dd599b1152258be3ffa81a32e5f7143be792293976c0978487737b61f0a7596eee699" + "f636" + "3670aba405453b24b3cff4611f4072f2a67598ac40c2c1b592371191f772b6552f67541ea53d695c39d26fe33693b45bd80721cd1f8e" + "b9b5" + "1e4336cdfa5e0d2f9709d5dbe26233b9902c21c8f653e4e41c88da6096e19a153157cec7dbe0540d1ee0aa32099447fc211cd579cd60" + "daec" + "9630072bee47cacb4e9115675f07716b422ce4e74399ef05178ca292acc0be45b5ab15707b5f97aa87e79652dc333f22673bbf632da1" + "ad22" + "70a498c38569dfb64582a61e7182c26ac8e2c57c43b5cbbb07693d04e935d78427c0aa8b069da1375e376992b59688abd651603850ba" + "d0da" + "c6663ac5cee8c3209cd76148e957a50558ae2c3b6b8572a41c8c7ac2f8ce9114a857463ba914691ad8bf5fe38292a42e56f204ee585a" + "3f65" + "1b4c0567b7774b71d732e1d8068ed69a4e674ef793c3cab6a551f889ed9b3006d34895c7a6401402d1acc1aa7270586e373c5e7a82a3" + "0efe" + "17f962ba26bb821cc5befe9c6a91987aad31f61e8f6087ff0d34be5e2573c8b4bf0f1e1fd2faae733ba6c50ef6aa4931211c2faa8727" + "9137" + "fb2b53a143ed8c41df2a79d4e67a16eb05172c36f779b9770a0944214a3d1986d47d3f3ebc072a00924295d1401a2df1dcf2a4b99b4b" + "9691" + "38790f5e03576f1d8682ab69096b2e6af2aacd6f124da1e1690cb68fec8f0eb794bd623137422d012e7757bc0d7f9e67387826529734" + "1128" + "297df3bb2d0cb1165b2d793e8b002a956082734559c148cffca5ec7662d2b1f374a3a2278eae010f24d1464e66bf7722b4537253ba19" + "798e" + "65d2e2e502560a50cce26265a8e87f27548cd0d19930d6d9ab0dabab2a63841de03c2654481183d4475ce01b9a67d4329bb0dd322088" + "8132" + "deb6d5beb27a8986fdeb444f1158225187f4eb5ddb894b95e509842d7a510ea55e7833c6fb0d1ecb8436963ff9fd07c48faf5c99c3de" + "9079" + "0e6ea6049d565c5837075c4f6a5c37f1c7f7a8803d42c413af43131a17e175a57e66cd143fcfa407c5db50d4248694fbccc83c3fb45c" + "9601" + "dea656fab0226c9a988cdd1069e2baee4c8e894d42fe12ab4846e35ae480d260764e2cdf3107d3fc6f7da4acfbd1d6d5381492b9a33c" + "5d61" + "923c0e4b69619e44057e80043080043045000578b516400040066c0c0a2d00010a2d0003145196440dc7d975f4439577801001fd98a8" + "0000" + "0101080a7e069e553740bf288238ea06a46c72ab564d017a4c08b91a1c4f7a782dd91f80607c02d2745e0180b44aa30a0cc671f18ca5" + "1859" + "d4e11557f879d1b29af721040f0c9cacc8ded1c50eb997aea391a11f8c0276e33ad606de7662923a5b01cf49a20dff8cf3023fd88c1f" + "be69" + "269b744a9a8e20a347236344073b14ad914f9f4221b52777f76dcbbd43cbb5a82df44d559c74e451bd2c24cadfc0b7e833204f131540" + "3aef" + "62a304f82304b84f77de4d48ce50b1520e5b786adafe9fc6eaeca13d9ef1a2933849cb9c56db14ba136a7c54e96acf37a5d9fd112f42" + "904a" + "091a46fa40f32f913c1d0b2b83159236bdc0a9508033c8f8b35064c23b9156813eec78cedeb9688aeef00ab2e3449686fc4d58774515" + "d2a0" + "12b2ac19019388e6bc1877711cba435958debc8dff210c17aab93292fe5c67b4fef6a13675781b82f70abc98b8de762d33d4cb938cc8" + "8f42" + "68758a69f6e113f565422e20f20f29d643c55d099d8337178c133b8a31c3f36ea232e30c98482651daf548522aa4b1df78de56725e43" + "752c" + "09322e5a4de6fb245d9c08435e6ebc471f2bb5512e36811e8891992fb3e27207dda17c249aa9bcb01e3554eebf5f63e7b148610bf5a2" + "38b0" + "df2922e5df520187d3f7e8a0b976aa31a084a248325ee61702a22d2ab5fa57e4b027ccbbeb505248676a774685ebe7831c0ebb3594dd" + "7f12" + "9dd0821fb94ca13f252d988a0e0914001d2cb00e8e9a23c9fcc1a66662ff752f409d4cfcb0c8dfd2793e8e0bd5f172438de45c1b13ca" + "1d83" + "58d1b903282de3fa4ad9b7011fda8ce8ece304794bd97359c4ce19bba977a6eea89049d86c7c1cc06228a715b23a748cf81696d4669b" + "ddab" + "40abb4c417e7c5c4c5a1e5c1efc702dc12b5d253aa044d8329843363dd81dd26731fb669687e54fbbfd8016c21265c775da0780fcebf" + "cc0b" + "c5e7a0e48c8c6d0713588755400a59effc61fd36348b3f00a71e31f64aeccf200482ddc39ec00f4a4f034a9b8b339a315e9561369a58" + "1fd0" + "b44bf858af9fa68b1622416b5415496e4e26b63548d8e69802c2d8622f270a25b266baca0d332c4b9846cb4a39b2effdc8fb9c75e2c2" + "6c7b" + "e6e7c23fd331021c55ef77c4a5afe768fad65af7751b8c30c725a087b19bac74009741d3d07680dca2623d8517aabc55af6ee9954823" + "c29a" + "56c0131d910b3473f267b89b04f029b06b1276624ff339d956db6780a3929a607fbbee6f3c353b8ed10ad2dfdf16b2abc0c015b2eb2f" + "d002" + "d565e0630bdebca945be1607be386f57f8f35b39c6e46a23c1879fc1d932c23e09999ae50025b9f35659fe17d931e6800eda82cfe6f9" + "b8c0" + "3c96f5183b07be34261c4e7f7795e6d3d52b42dd7f3c954e326781fde18b88d06cd518c31eb1164d7decef7a5cca3464599f10c733b9" + "2286" + "fae8ad78f077100924ea39dc4e631d4ff7876d66c714f03904d1ae49e1e29b72b9a9d15ed838b1278311b22433e1c0c3186f6f7e23ac" + "91cc" + "49ff91a4ad6db8e67e9947d47e9b9ec3905e524dedfac7227b99b3376361b5991ef1f74341bb4e6de8bca540efb3897dab85e4cbd0c0" + "ed99" + "fbe68167e476153476614a58658643eef6eb970be282248e88b3375457754a790c2ec5acc5fa42b63e664541f9f8a9d3d85fadd01c71" + "3bdd" + "064d53fe3dd83eea70c7a7073a3326088dd58bb28ae4496a02b209f300ab19510219c602e2cddc3e4e3b2b7eeb8bc872661ed78cb1b3" + "6e90" + "230e0a0b93de2680a59d3fe4075c13a44b68c84dc6c8b63cad59a7819600ce828e88ac467458ed9ea7cb2d712a3859255f9faaf01178" + "0ffd" + "80b18ddc712495b7dabc84d98ecf76e43574bcd7eb8b80d951c8ee63cd4ed27f900b245ceb5eb9bbc7b6e3fc984820ca6eadc2a18732" + "d827" + "5f66fae800287e4333936c59f43386bd44057e80043180043145000578b517400040066c0b0a2d00010a2d0003145196440dc7deb9f4" + "4395" + "77801801fd139000000101080a7e069e553740bf284e062a28de595a65d6cbacecc766fe4da6eaecda5f19fb71b08c53240c4f66a2b8" + "e124" + "18ce5030863e900d3c369b2f171d17e4548ce59d15bf298b96d18e2ff2d6383b7d72f682ba95b1c1aa12227a57322cd6f9d95cd03c43" + "ff00" + "1651aac8f236385de69a8caf1618168d550cd605a9852c20fc7a2a4e01d5c0903397fa0cab0c54b3834b5b24eb2f6a48ecd0a37615e8" + "de54" + "eb924e238f926becca30b06dc6756cb44187a39f3695f81f6134afe50a194e5b1bcafc9cbd9ea619e4f5b709e8e0470e649a019972bd" + "9db4" + "2569a911f63a050c6fcd715c727cc1591a16abb4fa804e2fdc89705ea39ff8c65eee96e15b69eab6642e19072ce73e75a36fc4767158" + "61dd" + "c4dd435f109edbf7ed051216d9e6afbc6308d71c2e7b5687254370d2555380d2843d06c9bb8fade9a68ed47072255564b2e90dda2930" + "523e" + "043c632967d19ece8cc2b833f7aa9de09916e077a1a491b51c532839646928fdef67f9ec2e9363477342879fb839d0cfc979d6c71fd2" + "0a84" + "a60e6fddd590cac6ad17a06f9ac4bd4c6f3398c9bd0eb145e62639aea66dcb9003fd9a434731d0545e84c8920f318e709ac4d6298869" + "6a30" + "0e1e840bd7cc19f0e9e12212015d9958354c5d26edff1ee5e6e9b04d3ca5defbd364c70903ae6d20fa5d5ddfdf681119666f5aa1d9ed" + "ceb9" + "17266dd31c749ef137ccbbdaa0381796532257c331067ed4b69f8fb3032ebf763f108706fc24839a295de1133a9d73de5bd0646eef0d" + "e195" + "5850a21117d46ea4d19066c9c9af7864715f2215d2ec5df7ed660de71e18909812be6a144d6fbd760cd5f90373aa010ade8b6074d905" + "1266" + "81c9f5b1b8d9ae697c6db8e859b5d1c2250046e767fe19033bcfd55f18ad89ff18e19f41dd3917aec403a2518fd4bd19a7eef3afec0e" + "32ac" + "37d19ee0435622bc95cb776988c4381196773cf6f26cbd2eb26e23db6929cd92c6c022d977c1f7c27e69853fb7b11b5e46ba4cf5b21e" + "262b" + "4466844f61ad5fdda90d774b00d1d00d29ff4547e4f7d5b118ef0dc5236fe2de42a8639b9fa0b76af01e50b28eb671de500ad648264d" + "c1b6" + "a050dbe2dbc03a69f49c0f96af37f142376c1062cf345fa41079d82fce0d3576cafda35a57240afbac64ab7ef4c1bfab2af7e987e84e" + "4c51" + "7a1ca4063b04fd64ed2415bf7c57f29c55bc0cbd605844c78eef9187ee14213e05d370778108a5c15eda9faee4625f6d7565e7dc8851" + "c248" + "a29964b166192945d1c9f5b929766025deb1ada77ceae4589c112e086789af32d5e27e3e3aba24d6104b692505dfab07157afc7bc538" + "4749" + "10aebaa0ff429c831be046e383a019af8734ed4b81df2ffdce964fc4b81fe803e0215eae8c09c16baef7ba920230360ddddb4903a507" + "ff47" + "568618de4202430872ae4384d9651c8df81f2515c2527b12eb77f9780139fb996b35ac405d0371f236ecd9ac77b666d9e6211f9a616d" + "028d" + "0a4f85d8bc4436ccedcaf19a0f3013451c9c8f982277ab87c832cc9966a49e1904778491982697cda2eb9288dca570b61abcb01e6664" + "cc0a" + "b6716e316aacbf7a249595a65259b9b90dd594c4e197ece10922810f345ad0c3d3f87ba745ac0a1015fbc0182d58362b2247809d38b1" + "8c4d" + "2b49abbf3eb45eb09b3be4b395bb62c1962560efebbe3125fb9d7f3d38ef442ad8d61589613c38143f427e97caa32941706a28d426a2" + "9365" + "f9957b989d87b37616a6d7f74323a61a09690527c88f882ab0c56c7dfcd439556d4b527df3bde157c527a93d577f8a5142d0e2716754" + "c2c4" + "f12f6abfa12b842aeb7fdaeae54badc9174c9d47a93b5c25795cab8af2576a9104fc5798f0bbf07de22ee880ddbb436be765ee406efe" + "c7e5" + "a962ca57474919cd184cad8e8a23b86a951ea8b41e574ea7aa44057e80043280043245000578b518400040066c0a0a2d00010a2d0003" + "1451" + "96440dc7e3fdf4439577801001fdfebb00000101080a7e069e553740bf29315ecf5e7912acb807ca5a84480c7fe9889dc1978e13087d" + "b5bd" + "1981f81cf8f526d6ffcdf51ec8abe550b07b9d025d0b7dadb45c52f05d1c8f48e676b3cd3454262a33eed82f7adf6c23b7bfe4cd4149" + "17b6" + "0fbb9ebe2dc8d298f00ba9e7bb4eec362e11d3ec0ceaea534a5c8d764b3ce01f4feb89517c1c042e599ec27cc6670fe68638b1c85d79" + "cf3b" + "53d9560f14a8ee0523f225e6b3af104319412b9efac207207bfcc3317262f5fe1e57c0b20cdc189db4f90db708a607aaf0ddff5b1bb8" + "6800" + "4fa3d848a4b326f1ebc1a40ae7c49a74d3b7358b3125b5efae207abe28fb3fd79108d57476f74c301d7bf552ec487b2fa6f1507ad845" + "499d" + "8ac22902b6841845739f1b89b3ab32b031d3856f72f4a38561ca11c1948c1389f2e9378325b508d02fb1e9f9836bc0676dbde77c7b53" + "fb6d" + "1d8d4eed4ff375d10e1682bea72265e3d00eae5846f61a25eb683e6f43d77c770257fb031a03c7d8302900c034b29265f59551c28d0a" + "1463" + "b637aa52f31f726d86713f97d1da2452c3639c3ea4d6cf61ea4fdd81344e40a840db04c3e72a47fe414cb00eff11a7083088903d0e9b" + "7aa9" + "7deb0374d154098c803371c4a3aaae04e1f62bc2a6ca2e9aab0fa5e641242bfc6692931b90512f384756cd4be36498cf63d043b22c62" + "4257" + "05742f1fce701c8ed5743de0b7caea12a8224f783b65301abf613dba6daa4b378f113105d762cdf2ff2d1195317ecf1ece0b4940cebb" + "fea2" + "e577be41143c9b7cce4f0b450bf30f180d472da121d7c12535fddde0316da7233f6cee0857e455cb2d91740b5b458fbb07cb44f9ae62" + "bc2d" + "c0bc33cbd9d45a0cfacde2a762485c9a1d998d9e222d8f307ac8e1448f85ed66ec6459e17970ad04b6d7221a7fe7a33e996367097bd9" + "710c" + "0f2d857f06267ee17e170674bdd4298213c43ebc55e6c1440cd9b9d9145c4122aad64e401d41968aca9885139e78e56a44b5f9a197c9" + "3f5c" + "45c618dc56640a8af88bfd7123a6f43b00ff1f8b3dccef88fb86a0ba0ae10991115b6487b27bba7782a78256b76beff580fee0023a3f" + "df9c" + "a9afbd639d5ad2279d9ac1840425c4b15fd167ceec431dd8fa06e0ab7158203169c459e1a3853432dc6b9838d5416fdcc23508a70b8f" + "63f5" + "2eab63e5749fc089bcc54dc378838650b618945550b2dbaa058783a8f6c6707d64a8b76b4f556e6b8e6d03a7adcbe25eb3ba01dd21ae" + "60af" + "313946963028798afc4b3ef69f0fc93a75e9715dce0550916413c78c7ffa744911d04a58a2367450b8b29680d9c75534e6e18ae52303" + "0881" + "8b483f20e2d5469c6d275d73cd09c1674c188c6c39208f4e787e93350637299c68c6918326752030de952c79b8dcf6996de74d81a2a2" + "9d98" + "ccf0c6585fc1abd1157505521c151e6c54809a6b809ab76c150d395b641dfffcbb389b68c32eb1144f4f95b4703124aba8837baa41ef" + "08cf" + "f415154201857ca7bfe6f62418f78f76b51aec06d90ab8b8f677aebedb7a453e5d79b514a6875162368f065960e023d498b436b5a3a8" + "ddd1" + "42f907b0453dcfdaec7aa6862c9245fb47e19a9a20246da583b7f58fb4b2dfc8e3bd826563ef3e06125f2395f0a6fa6f16507c67a70d" + "5508" + "7824aa0cd1364b3dd7e7e8913c6d37e5113356b4b13fe0a1b3e842b85db90e28247159a150a20c7969eb7dbf3f389fc33c6c48ddbfad" + "bd37" + "7d5ac7b557e37d880bcfb486c1bc3c9b69b5977afa1f5c6806178dadf751edddf044f8b636b1411a70a0ed53486414c181e9186655c0" + "f41a" + "19b20f170a7ddf8b63f4c58f35c3296074ce50380cf7fcc192858ce157c8913b24c9cc0a1baaef3821f0c3342bb12ae2e7e2a3da4867" + "97aa" + "17bbe3e13b97c261bc0f852a418a0a388262ca5f986e462ed4d26f1e5dc0cefe8cb744057e80043380043345000578b519400040066c" + "090a" + "2d00010a2d0003145196440dc7e941f4439577801801fd175900000101080a7e069e553740bf295637fa0a13dac10c147eca8c0f11b2" + "f4cf" + "011a7d538e331861b680ce8a37cdbc13efbdddd57cbfa9d02947e481f91f6fb2b3217c783f94718027958baaaa12ff58a1c3ae6fcefc" + "7a2f" + "c99016206b1600a49713968e7e827332b286fcbd8c90cf2c69fab1e3b1445f4be8ab4938caeaca406f57ff6c86cae1d7b671accccbe0" + "c1bc" + "3a2790df221d462772b17c237ff509ce956f52c94b41acedac45940b7541d2c7e92e2e9e96614078d5e0345b186db21627b9963d0c94" + "8159" + "8c51e4f157245d3cf8952ddf9efb5569e588883ac6159648c6cfe9af3ceb6d407734053b58f54a2c598f7c081c98b1ecc0c5dbb4b75b" + "c78d" + "14e2c2dc2eda9e035b8f5a944d764f86169dca70a071aa665a60feb44a845302ba63132b1740fcb02d99846050313ddfd091917978fc" + "7d54" + "cebd431e759a97b59f72bb0e8696b3684da6515bdef236d818b781f75285a3969c54bc4ab89df0ece93f7defdb883a01cf3d3f7abf54" + "173f" + "e3c66dc2d3ee83a476586dbb1e2091ca678f01710a25cb4a33ba9497bd3b12d0e7edf793b3837980f7c828f50a7fc6afa8f8a13b6f8e" + "b785" + "f7bd81dabea4d64314579900ee18cdd08d54bd5d21bd3a35be7992a8664a049a99e3a01514a7e90f3caed201176588fbe68e6867fea2" + "f118" + "e0011c5eafd29b951939f22edcd6387b15685697a87bb9a044eea21585c26edc5518b1d7d861469a26534686a323aa0a7bce43619106" + "1987" + "ae951a902c59a000c06922f01184e91a6fcb6d187f446904ac84e04d095cd022e94cf6c2a3497c4714cd48f2591b368cdbdd52c74d50" + "ff66" + "4a67fca3ff19459da417fffe61abc001edab8e6355cd1a133984afc1643f22eb7883ba9e1a1a5fbc98b9834ac0eebdcc8308dbefe85d" + "77fa" + "4c8c224c7a549b7e555eb2aa5c2b7cd4ca3f012475ce57e6539e0c7f1f763980763910444dc8fd59d97e56c48e85e22ffe77c52cccb3" + "3592" + "ecab6e706bb29639ea20e7ccb6e809988c60ce73528b474befbdf52ab5e3d02d9b252b5c421b6805c69451c9436551ead2e37790b721" + "c861" + "ececcf603d03546afe82e0f5d9b1e3595c2efba648235adcdaa951d1cb154ac0353285c71653754eb5564a476a2ef431ba295c0ee35c" + "9ab6" + "7719934316dcd48a5e6402e6c856ddc450a94133f4aa8263cdeaa3b98dffa9482f2f0b82d0857eb4861c6ecb7af37d7d6906d51e8da9" + "de81" + "c818d0b74aa7ed8ef40fd86d7637b7f6f07359649b261c4feef1d8c762bd0ec36ee2d0f015f5c23147e138e370d6776c08fe4558f462" + "053d" + "dc66486cbf4a461de4b6d3f48de1bd5adcaf73e5d6e7d7029a45e1bb72c97701dcfba4a552a8e8db7a5607f27a95b321c4d287591747" + "22cd" + "69103b4e2b2dec512e928e95b6a226c7ebb678a88acdbc6b12ccfc05edc553e019824699c3cd50e86599859a451d0b4b8d5dcddf7990" + "f1bd" + "40fe7868f01dbe7f622be7cc00544e7c8f2f043f1abd8d54d349a2ae831df5dcd3d1c8d71980fec3fe0757ec150ab46c591534224bf9" + "e81d" + "af6596f30ea927d15106d427ac011a94a9639adc1ce9640f228166fd9805a9bc26ceafe48df9b0162c61e08a94eb26457f7cbd1af6b9" + "5b7a" + "fc589e6a75c596fae5c697dbb2273bfc6ba93301a670e4f4150c59eb8fe03751d2fdb0e39bd39a895b442bf938236b1e470874c2380a" + "d708" + "b292b80d231a78daedbdd1cd7ce5ac7dff62ba755e1f8e40a6486729defdbed283d4eab803854d460bf2800220b54105105d016d6f8c" + "3711" + "6beac8e77c0337e751a6ec9d4027f5d82dbcef9455ba5a04d3e7dda839284256ad84d293e3e4d3144dcdc00fe9ba3ceb7dbda904d896" + "59e2" + "2358d3cf158f29f932836d80c77ee7a8119754e420421614529e7a844ac51d061c517c09e1b93d832456a744057e8004348004344500" + "0578" + "b51a400040066c080a2d00010a2d0003145196440dc7ee85f4439577801001fd2ff100000101080a7e069e553740bf2999d3885c6324" + "3399" + "7b96e9cecfb5b2a1af1d509ca2c655d074fc1db853a0b48ce673bb7a39e55e853cad990babfa3f9a4a105032985dcf59f6abb471f671" + "b428" + "4cc2a0d05f2bfb58b711288863a4c30fcb9407d0550066131704d03e94779531d360ff07bf376431f831a5fd3510d75ea6f96f365a7b" + "dd59" + "41e9c823990cc8176976004fabac63e21b8b490ee9942fdd01031f73c33674d308c59abb6e364f8c24fbb01a6dcaea822119493da057" + "2429" + "6ecf7eaccbdd916840ffecafebf9294190246ef775447d9717af411c683aed30dd00618836e323395d6841b1e331f8b1ca6acd3ceea3" + "3d74" + "79e52c7aec982375fd5a2196822f1af4237c91a850e248944f40acec78f58292dd9a5c7f2819af99156c6b612795cfdf92315a355de5" + "1cf0" + "2c1e4d26c36281b06e61c0e188d1814d35827d4268bf0c3cff9fb1b48c6e9e1746acfdc2662d97cef8a2de62e85613989c8ef817b79c" + "8f93" + "b6836f57ce9ee2cb7391c416e026a55690ce5a163ef5e8472ba59f87f00f739f8d2629a60fa28c363594173fb4ddc569b8d13c059cf7" + "fcf6" + "b6dc017db70384cbcdf5695f13f50b4d6d6a353b9fb0dfd25dea4dfa4f5d3b189ffbcbac49d8c8bd94112c065c22b03cc0c4ca8f50cc" + "399a" + "51a00d0c76dd13a7540615caf5885bbc78e17aa9e86363a646634a5bcde41471ba9d30792e01a4a5af1e186bb011af5649dcdb59b44f" + "3c67" + "cbd76f1d08f63d3f51a7f17047d16235decc310cefac66efc5d3757d17016262167a16fe3a0d92c2866a3233cc3e108c493f971279a3" + "392c" + "aef8e5a50b04b6da0fbfc2c867de1292c94618675ace3c9c1e766b0367530217ad10f7a9954b65726f77ef4e3ead66b793c865cd8716" + "0c0d" + "f27a4fead65979f49b1cd972ebbead93ccce5e523de4073229a72543c2aac4ab0e5c5ec2efa60be82c8f520e1e4eb0e1753622c1eb04" + "66bc" + "47fdbd33a02c6ff4169a54ffd04dcca7d274d949339afd58b75ca608aa450de2001f7c6ffc5200346d4bdb52ad88a4b096c9e73172c9" + "bb85" + "8f7b427d56da11fbcc045cb9756fdcd19740d815d5975c53045cad2e9ccf373a613b06fa778db3ae98af38fdcc46b8dda3deeaf63a16" + "99a2" + "4882e6d5f2174ad4b5c45d7846e300524657346e0cc6f888b88d540355e945cd37709016b33fe39cf0be40f1117dc4e60007c2597fa4" + "b539" + "d866af371cab103da7103919ab79f5d1c6bcae2ce8677864bbe7569cb2b64320dc75d3fd15ffdd289575d6bf8276c90d5c12d4e335f3" + "777e" + "e6094a019330a1f158ea1575af3923048f74faf56ab26475ceca937a380d5eb00b00fa4128f0a10320ff19ecf66869f7d8b1d1b15f0d" + "9d84" + "469208ff490d855691046e2d07d6ebe116de9f33e389ca584f80274ee23be41a07425aa15b79c3e1178a78811a445f45cbd4f418481a" + "9257" + "8d1a1d5bcf0f6e0f6cd48a49717afe12cc22ddc6052bd63be67093064fb8bc41c88f7df1e43b68d88da7495c41e2b19a2e32b15be4df" + "1aa0" + "d35dfd9bf923d2b37ce9b9fac2512a4b0b4fba97af256ff0e6240298da8442057fe5fcf42e6eb6d4eea7dfc39cec0f2cae9d215bfcae" + "7569" + "3b3e9a993f63a2bded024126688175bbfce19c886408dd4938064c64c7cd360fc7fbcb6a332de83ea9b2f5e9d07a8da140ab3ddee20c" + "e2d8" + "a14180e33e60ce39176a20f1aa6801782859e3e7130d5332d6e43b55044476259960fe5505612d32b8374746986013849615cf99f497" + "a4a0" + "4e44989b6020ec51e1c2092a30ae77f98501c37b7e43c10c9a314fe0035cf64ef7c5d5c4c1520d1a6bc81eee9023f5d9e351f03889b1" + "ba8b" + "f14b804824020c1e7a9f844af9407aede9944181b7eacaf92a8045bd4d7e2650e13c261db7122829b8ed2597ee16fbeea884c7104405" + "7e80" + "043580043545000578b51b400040066c070a2d00010a2d0003145196440dc7f3c9f4439577801801fd4c2600000101080a7e069e5537" + "40bf" + "2994b610a7ceb3f6ce5d8a410fadf739699f62207c9e3ec403de9bd13d8029b12cc03fb90cc1be5d50c5f7ac63c5a654b66fbba0626d" + "38ae" + "3b31db9ad0dc4e1ed2286ba98bb10e25b18b9f75099b0e0674bb2e45c8beea180b8d81cd96d77d1794b4d78fb9a65ed0a95126883bef" + "e779" + "0bafd4dddac0ee1228e95f588b741871d2c5613e6e4baa0f7abc34829b70f20e134b17492821564d9163f170bf5008a4de2708dba56f" + "0c8b" + "8097cf50de6c7d9484ea19c6ee0f4fcd8af885e9b87a265f20276e405898908931c2a7997932fa06ea0fd3f40a88df5df19e1db69a17" + "db1c" + "a8d0fd3eefbc3177a62c4a23b5b34525e61296ec0edc8be9c830dce8ab7f135bb3f4f1ebcee2a8f032a9fda5c3f622c999f54ff5fddd" + "7a45" + "0255454c5fa3414fa27cf1783432eaa313016e8ddb0d638169277958342a596ae61ef1fedce40002ec048c4eec16e3f2e33d76289c63" + "bca0" + "eb0c35dbca936e6b0d45605ec4df402e1e4265baa1edcb5b90ec35746a497bd5327011ecbb3df282bedd27d333b0fe7691b9a7d4d206" + "482c" + "438af2dee0f26018af72c46b0b553d1ef5b10285811216ea0982fc6f5302033930d6b7937a169e7025dc5f4c3c7a27f076054371194b" + "9d0a" + "c6c445bb65061138b8ef01bb3d410a56f0d679dd3ac100f8f3661d891a727a6b8125d1321f004d00ea859734c09aa0150f962c497d77" + "25ac" + "54c271672721af2a4856d8643c7f472105f7b25c404f9ba2a441fbf8cbc4f2d0547d164c663b5e5f7b16259c711212d7c2158200d51e" + "b719" + "352636cb4844c301c6f14f95d9a8906104a980dd0ef4a4ddc5e2abda6185bbeac2261294e046f7bafae165e0dbe65cd9235cd3760c78" + "a147" + "0eaae69710981b8afc4941f8444faa459e736d89e96c7d686ce56ed03e89a097f317c2ae793f8234f7c1d5670697859c2c22f1dc652f" + "e369" + "8bb45c86023c78190427ea96d00a601150b41d16ed02fdb97644e4019c352159fd4d6c42a2e33c4d9c368aa1414b597d2239cb74674d" + "e5d7" + "5e32e37d3e012dda4dbd31f04f144a7ec8b5547d94efa8d47527f39d9070ff5789d99d08959da1b90f94bed1174b76ab8d4658c4fcee" + "307a" + "679993607df5c18cce02b1ce749ef51622a8e037c6173b80caa60ce664631094daf4a3a4ee6826b3e6d60a6fc3478813d2ddf12fafef" + "acfa" + "2891dcfb0c0105759cb57f97c02caeb415227aecb9421b85c6c12735418f37a370f416a265197639ac0653095f43a0ac9e0bf29fa4aa" + "e50e" + "d4d5be57aceb0d684ca1e4160674a4330be11168eef9e6d8be6620694e2d9c4485328f05675c02fef95527df63214a31848f75ef8b96" + "ea04" + "db77eeb9d06dc13d7066b444498d02f5daa8beb31c33f0f3193af1610194600527982e0d833491b04a87ca24e9fad38560d66fe1c793" + "4711" + "88931676bdc17a09525a89d4faafeb86276c395c5a31ffc648ce1b0d0b172d01d5f2c003da53e7ce4773506fc9870a586c1d939a02ae" + "b15f" + "b246e44a8e7f9ea9299fcbb61220d5957db2d1dadca8c95a42abb88e5081315514a5f5300edb51de0d6cbeaacee831c85cc47f3db65a" + "1746" + "7867a059145dd92e23422017f4f8d79f702e9b6372613c968ad303e36732a44a6ea9308ef241debcbf5392eca71b167e5526bca2400a" + "202b" + "2c63d6d497af01fe59ce18cc498ca7d83c84809fcc87152c95666c190476f1d5d31f32eee8509752bdf1629c1655b2ac9c6aa9bfd5b7" + "00b6" + "1bbfd2a560a6712d423143ed533b53a6f42033a681751939a0a4f5cfb170dc5a3f25974477e81d3a9b46e4c192f80d1dbf9281384d42" + "c436" + "db9150ab76ddad58a0588545c97c9af3aff9b3ded4e393a8c1d20eeb6c10f47d85e22a708efa77e95996926e61a746daf517d1531623" + "7899" + "dcbad145ef44057e80043680043645000578b51c400040066c060a2d00010a2d0003145196440dc7f90df4439577801001fd5be80000" + "0101" + "080a7e069e553740bf29b669ec746dcd214a4efbe2f661b3e24ae00f2e049a61a70af5a280218beda8d935c681da89cf5b289f2f5f26" + "e19a" + "67c33f03b2e92d11717c4045a1fb4eb0bda1d33c693b3b0b6e02f7c04d4e25e04c4bde98ec8c8c9ba5a96588169cbc4392eabc1d2774" + "fdac" + "343f9eef267432aded780209644deed9034dc31de67dbb85278ffe26143847f5938aee38b95e34462339dda575e5d3f41a513a64b1db" + "92ab" + "05f64624cfcdcbb9860b45a0205fe823080c08e356ebca1572d05a9dddcad42ce3cff44a9c30f54bb48d7cadf954d1e5f3b865ce2f80" + "d7c4" + "bb944ab00fa52796b8ca20b1f7705983984eadbb217180eb1320b5b6907daa36c52df041fd899105e4e80ab0f14342083380c666830f" + "cbd1" + "be96c4e45b3e97a574350985405aaab76aa60387b6ab55769d5577d0e7b9197361d356d2a0fd0ee8bc0e47c175a8a7a5d693ce2077f3" + "7396" + "2031dfe888feeb1fdecd4b9c102fef968f31bae23e8a875f37ac7190f39606f7081c7cb57eb1c1da914bbbfd17fcbb21a7c545389e02" + "538a" + "88cff136edbd4230ab333269179df7dc9aaf1e58fd3d6fd0b147275816f3fc4f17bd8a24e5bca70e4fdb84fdfa08340de9fac62e8f60" + "ab1d" + "157c64ec44252342f07d6fac74c9ecd2353b052907262adab67356bc907cd542bb82edd64667dd926f4cd7731ebe74d32fa7f5dc2822" + "2be1" + "5f5e228ddd9b362fef36e1a8e2b061b8b7392054f3d09c314e78b6092225b599f6e5582d669d2772317c1015dc980b01bd126b036695" + "1f2d" + "5771d6752ffb359534c65e1e29a084fca38be8fe98d62ed173e57f5f4216f4bc7e3a87ac4d220d125acc6dccf074d162fee73d8318a1" + "db7d" + "8f1483e2f5763f3f821fb1f1bbe8075975f4ca7d3b7b1f0226ec5cf928edb491a9a2f7b1272eeec67aefea080a85e47b9481fbe06487" + "88b1" + "554fee8a2f2d6010bccede09f7d547523ec786a34a96c5742edd388b2a31867ce916a3605f2ef0dfe931a45dde384e7521dec6631197" + "cc77" + "2f36ef88a8006504e5640d9521160bab97f133a8b0d64a7254e8e7aa96ab013aadc0d87c4293739af495064f938679961792302cb089" + "44e1" + "153be15dacf495b4db8ce770f35ef7587c15f0593b580ef909bc65cc430a93bb1161c740fb284ead922f43ec86e187266778fee6c784" + "82a4" + "e93185309461e1e7950154ba623d112e7e5f9985a5e259f46ef5b4d0515145e35b2d47d8de2b2e7a7507a28bffddda95e396c3418f3c" + "18ca" + "35fc9e09db60222eba4bc736646919ce8b4e5f2d8be3c3cb0c435f93f9b26f4d4c68557814760b5959210c86794074a0b013caed322f" + "f31b" + "5d920a23fd467ddf3aab09fde58e13066ec2e3e700feb19353a9d4ed59d02bdb55c1a65960199abd9604acd5757187dc4ecb6d6a1916" + "825a" + "c36a452a1e7f67a6e558e40d8d3eb2ec22c51315e7253b1fae27947e8127ee03b849c5ee03a846f46988cb748470d4a48b1a470382f4" + "2bc3" + "5b0362862233a775d8f8ba716e2e5e20b6a35332e2c6c70df96f5359a050d4bde214c65c825a218720f840a3920e29de4f2fa2f47e3b" + "5d45" + "a63b5e3a46c99fcfaff7e8a17b362874d9f10334f8eb0ae17a47f731ba0d251fb20afcc138233970f8bb470cb5accc4c14fd7a809c6c" + "043a" + "6bb0c892d16cc0c12ac9410b627d911603e955eddcfb61483b6983980e400caceea156362aefa28b3aaf73089fa4a2451678d2aadfc2" + "7f98" + "e95816dd4eaf3267edef37a021ea037f277bd4a8a209cddbea616ff1d9217c098eeae145c56846519d6080a9ddf44cfbfea63114a5d8" + "b2a9" + "48087291bc6e550b053c60baa7ff4775d2b1d79eb59e6775437c7ed2ad3e7b59a64d7d275ad52cf835f13d272a71ffbcac646b1f9ba4" + "0203" + "cf6c24161b942354647aed19c36a44057e80043780043745000578b51d400040066c050a2d00010a2d0003145196440dc7fe51f44395" + "7780" + "1801fde9cb00000101080a7e069e553740bf29d0903bcf10fef312552b5f2ba18cc8bd95f06b27a1050da328a01e6a3bc9366efadaad" + "fe54" + "bdb446235f346e47114a890e99f43880754cf871a3243ac764541121e147e5d8d374a17b897fc12c591ae0a2bacd6f61ba3df6525a08" + "6404" + "04d6e4705adc09f3c79330619245baa95a2995870d1eecf71bf3167f98f9cc535e664dfa4d592dc8a394d5dd47ee3efba60132a01416" + "a2d2" + "ec89613aaff587bd4a84eb55afbd6fd9aaa4a574318c4b2b3d896bea3277c7cc7a55ab40b468426aaf442feeec9a24ce34dd4cc28741" + "5431" + "91f32553c9cc38fed9f3c3ade6eac9e0c85d57d306718d491e802a34185e139c8623cb33cbfb0e6f29530517f644693904f2e5dc4bff" + "5207" + "c927ef5ac8466737289d4d520225598f3fcdec49db5b5c85231b6dc8d8ad5f77dd4421714841240548aceb1ecfd7a69ecf61431359d3" + "5c56" + "62bacf6a5a4666dc49fbbd7d084b99914d93515837a4d4c369e16dce95aa1de43408de8d07c524a610362dd674a0b2e5c53dbf65c433" + "4809" + "4a9d16b21bb4656200833f1cafa260bf299765b6a5f719459b7a694506b8a495599ddd02b89ac26ca0d480b388b9e4d3f82dbd5dfe0d" + "a5c3" + "90070f5cbb82349d265fec7324445493f9ba01cc18885f53dc24c9a9c29aa3b1630ee6bbb0f1d611cf5b6de0eb550a259e09c4896270" + "2f32" + "26fa1e2084bec1bfeef0c033259b5847ce51f355cd3685d1d67dcfeefba797e8a37e04bbd21e8cddae5842898fd91d005659f97872af" + "03a1" + "daf963ab6e82a1b5f5360bc541693beb94e0bb3c1a3a8994fd615d17e1fad2143f81041ebfdbd281b6bf14501d199fbaab651cf23e67" + "7cef" + "89e042b5421a33973bb9d0b886052e48612c1459a743b1e45f055ff4a65a86ff263c6260a2effe8c47b0e4d40b07cb6eed915efb3b21" + "eddc" + "530a1baef0647d41495e5bfdb9c543700d0425fd27f96fe1975ed87543d5856a55992c1ea3047df10b6df065a0e2c39e466154e1ed3a" + "d1db" + "d56285f8aeda7901242999c59da9eaf80ca1d7cca0bf2eeef36e7ed111c6a46a6da46ab6847b9c5278ec801c426de417095790bed467" + "8eb7" + "ff4344249ce5a3364ca32946a8a17a9776fd71975b91e13acf8f27850e0dfc782df50206f7cea1e6f9d637efeb95f5e37b1e1f46c837" + "8e65" + "ee91d9a39a684ae3cf88d31fbdd83f48ae7a294647398a52a5d8e151b6343894af7e7a09cf4a32a6bd98adc6142262d299b40c9106e1" + "8e0f" + "e5a3d276d6fa337687376cdc12afda1144c90ce71d5741d30526841343b6adedbf5b4c19e9bf11e6f5f9bed2b6da1b32a678f844ba25" + "fc0c" + "279320cf2a51f5c6d13b4d5c069b1175002b755a569fd150c27fc0ed1cd4e6a59ae256415a801971f03c2f687fa8334e1d24b3722be0" + "a027" + "d4627a77b28a11296c6c23bbf3d5db9f04624d3782c45887263cc5478a2f77153d47d0eb4541d4ec2124d44909e4c416276e1970c446" + "9567" + "247cc68b3b3093cffda619c56f3bf0598e3889187ba9c8ff63268de7fc45c6c31d74650a7af58d547a343a7e7cb9c7acb162260bb05c" + "8202" + "296bc760c15ef48f8d2fbac536a3be3711841270ed1e5f8176571d8cbf57124f6705cce2846ec085f93484af36626a2f61f798d2f143" + "7ff1" + "ad233890313233b9c834af67731c627df837f260f21d67b974568119e2510eb814db969f8b905a62c24a07b1c9752aef518c09de46ef" + "2636" + "9869941b5469688918276d9686cbd60f04a3c6c3df17dccc9e7aae9dc3dafab21e4f0aa1afcb5a3e49ad710738a1287e1253c610a635" + "20d3" + "0951f0e822ac899982c2c70afc45584e1f7b7e9d68d8dff0562799b37b8310cb3e51148b59cbc292508750d193c05ba8e400a429eb62" + "4d67" + "6ec6acb7d9a2ea1c08e13913aacb828db5c3f76b2ab92d44057e80043880043845000578b51e400040066c040a2d00010a2d00031451" + "9644" + "0dc80395f4439577801001fd873b00000101080a7e069e563740bf292dbe203d8f53f094dfc68355550b1f11402d76a3ada1f58bb9e3" + "f321" + "27249ba3563b4218a802b63479346c63c1ac7eb1f4060d061154ff4d6011cb4bf1e88de1256e9e013b3732c70a1e98d176e6a4389f91" + "eee4" + "afd6e1454aaf49689db0e5a05303d315df6d45473238c03ace96656748e3da6be979d88f731ba9574facc86e1aa1c2785e73b1f8c8f5" + "35b9" + "3bcd7ef83ef3347e21dd95c5eb5ff5eecab7805f2a40c18e6bf8ac4c59fb4beea4c913ead55b2c9136cf106781dac450775f5d99c12f" + "e45e" + "76e6fb73dee9118b324cffb663a41168a38c9d6fc76326eda1d7d0b4483971cb86da593d3dc38f57c87901611596ee6e78ccd992a3cd" + "9a69" + "b6330738015e9c4e655210fd59f217229ad25248f7d141366c13dd8ad3865493eec025889c759266ae86b06ab443b0e504016add5ee5" + "096f" + "9fc575db6278cfae764de449e0b3a7c32bedcbe1ce936e02674178cfb2046956d124bae5e6f1681c314e66656bd6efe39009aa1868b8" + "fb99" + "d97adc6d188cea51e6269b08ce65eaf37728d0c0d673537b557811036aaed6dc897c03c7e61008acbee5709a63f5221d9fc77e29eafb" + "d0c4" + "e774a73cd4a4d045b15b2a0b0fc11ae858552fb2dc12164d85d26e7351d16696b198c8a8b6ee480d42923403c5ecc5a78819280c55ff" + "e054" + "eac68df9a0d47e8245832eada447c2b45b82211743cc0134c9a055768f7784eff34b65d41634339e52f5276bbcf53c78e834808470b0" + "7552" + "b57eb74675cc378f55e822a72e24d44fb895913dbb333054fc62a07dff2824627e382e1bedba9857558d2b1590bbe05bd2a7ded76925" + "2662" + "c19149db70bc50bce8c69754648fc3aa853c3766b397d739c55ce8d0a9e11b6273685f3ed0eeaad7b10a85dc95e3bc970858a332c806" + "454b" + "2911ded18b0021d4a7d003e4c32e32802615452c8cf19a4416403a3c91cf2facb429ab9a435d2c38321564f4f2487121857f0b86ca81" + "997e" + "577042beb054b254882f961c8c0561ce554ddccc736c4771e7583c68423aee34664b8734223ebb1dd7dc1776262f99c4101323e0c167" + "61f9" + "16951efae256a3abf7b6a1118d01a22ffe0236d8829266b171e181902d31afc3b1b9a4cb8d4a4378c47abba777541cec86694a862e33" + "0919" + "3ce69f502e7639d087d3241e3a244031519cf236fc8f7d67f0b4f31ac56594361d92af1c685561493b47b34edf54134489e4b3f3a0a2" + "7b3a" + "01afa4adf68efacf78c7beaf865d4a2e93b470bb78996dde7494d524cc8aac288627c83331d37ee1dcef7f79095e40f153ceddab746f" + "57c6" + "069639e182f0e853862cc8e99affb814611e4820de8ec63e283d22ce8530b632e4f81646ba2d04edbcf4570c30c94855b6bfe4f30835" + "98ca" + "255d6ad3f43e2fc3cae88fd1008542c4826b6c3d663d0679d04a9f82ad98143879cf4d490b6d539af84e65d60af3b4c2a9aea97007d6" + "4229" + "619627471e80d8053ddba4b902b7cb9c728d9cda71cff5f08dcfd5f8bbf200e382ec214f7645aaae2a0fc9242bae43a7ade4c54841df" + "89aa" + "11ac36022f1a65d67b7ead52966f133b3a1f2adf9e2e03e4d0b79354a7b357529e583939f6216453991ff1a5b0f9989d7ecfc84a95d6" + "473b" + "64ab34bf99d3036aec02b55e89d8f03fd47cc72f7092c276b3ae963ad777f4c9b54bd790d715554a9166af4be901bc126bb20bf3a5ea" + "5843" + "d5733205333754dbd923f9e61e6c050443055c2387b76f12fb23342a9b51ede02f11c08465a564a639f2b271bb5b15c4cf2ca5f42e52" + "83f6" + "aae798e106a8bb2fb06fd11ffc2bd49d3d6966a8e6ba68eaba4a63d2cbe6132b020aa4f66ae5676ca62ed4ee5adf00e9c30882ea2c84" + "9f60" + "e7e1f45e301a09663a25ea6ea54aabac87eb5d643209122427936e08d5beb4de44057e80043980043945000578b51f400040066c030a" + "2d00" + "010a2d0003145196440dc808d9f4439577801801fddc6e00000101080a7e069e563740bf29929ec2f558974d56ad9ffabc9c298cf3a5" + "3ec1" + "3ad482bced1df977e5dbcfe03caa6d6a9df9b920cfbb99ad7b94949775089de9d0d465bf1276f185dcfaed8fbdaf1d8b185dc37941f8" + "08ac" + "10ad539dbe65b3dfc66209341a09400795aef32a18a3ad379e50925c7e95d24887dbf102b23a854f700aa97fe987a5bf802c29ebbd8f" + "febe" + "8e877f6c498560012bd49d274c1d88f9d3e9fc99259ecadc4c8649cfc9b5f9ba41aab69ef5fe02eca166284b7c52800d86d294aadb83" + "0be9" + "c68e17ab5a33700b913c6f2583eca229963ca22503ffb84f65a9ae4b371689fe053a48bab4dfc2d08ded64fe42a8ff167c67aded658d" + "997c" + "d6e6cd612359b03e9640d4f6c5e677584fa5819f525e7d451b3f1eb86e6579eb0eb5c7ae0ae95091afe46ab8eaa66448fff0fbcb47f0" + "6557" + "38898019a0c2d4d13751cc751a8151351863cd9f8ada3bba626d81a21185642d5f29a25892180dd450968cbfb0e9da8091f1fb91376d" + "1d57" + "85ed5fd2bb37980a464888475440abb0125da31d7944cd445130395c3c6d2e4bc4f06078e43e3e0f159860e08da0a17b10e317ac614a" + "3a66" + "d23dd4504a0e1c5116d2828582bbcb8cf4e65b1fd517044bc4dcca56da14a82bba111c3175b69012ba5c54e4a97e2a0e6d56eaa273f7" + "ac50" + "dfcfa37e3876001b32919f476c39076b07e2226c4b69e2a52df4089b6931c5dac0ec37b7ebf32bd6a635e46296a4c89b8696d69b5269" + "cdfc" + "d17806f85482c987783485b7d8252d14957407803604315468a14642012f25b9c7a3ca5b84ed552671f496867ab02e06c74ec6937614" + "3b80" + "843afe236810de9dc103dd1d513b2d1e9ddf28ad19b1d11a39f5d2f929135303c44a523bd00ecd94e04e72229ce532b53174a3e0a09e" + "81c5" + "ccb9466bba528c90e520115ad7833f524a6e157041a70924c661bc32945f066ba904ac12ee2d3fba17c3ccc56bbafdfcc09d7d78e226" + "75d4" + "2f72bd2e3a8a029e5d203b5338e532f8d20a4aec761f8eda62a8c2f808a5532bd037b804d753f8b813d6c2c81e89de91fa2fd6fc0162" + "b24c" + "eaa6a63ede95ea4bef89da034700022df24568466436791f4709eaba6fa0ca8118a9fc82b2e204745c7e3284ff2c30b879a5bff7cb86" + "d3fe" + "3000b1a2bfc4ef85d6fd16a44f34bc09e4be46f75012e31bc0e07584df63ff2ddf090efec41f83e450153e5cbeeed9ce232892c88fd9" + "ebe1" + "9e675e7ea4cf4c96089b6382afe7e0cb61879e17cd3d56986a92892cc16562e8cfa7ca865029e42d83b4c3229e3a249ac976ae2fb0b6" + "eea2" + "65edc3ecdf89781db7e8f51c377c4f4ceddce7c552b3d9de0ec4865c436459568393e154af99847030df189eff0210f9048fca3acd2d" + "01c5" + "03f5de0c045eca1221f33fce88dc4cbc81e6835596fce338cf680f37d8b070ce3a0729e4b7a4948f6516eb54587d423fe962f10a5980" + "42a8" + "47b32ed8a0159a82dcbac2de5ffb98f9b5474cff3562c37f4662f7b98e048520553e39fd308a4eae8085b36c3117d663de983c9c16bc" + "16f8" + "ea86df1e87a19bee0e35c550a54a9d02c0e436aac234eb4cf18b8b96adfe1c11be88c87c58bfa6e4b041f1a2ae83cf8a7ddadaf8eb59" + "46c3" + "c7600b87472e53eb8c5fb51e25af3594d7697236010d7d8418a8312285991b4d26daa77f0728c6efbac1785c052a1a5b106b54f3e058" + "e280" + "89753c6c2f1a48ee5fcbe5e4ff5215a5b0836584334a909d979696869650bfb02d88d677102d0cbf17374b4092ebc7bd7dea966c35d2" + "78b6" + "1f1592d93dfdbdaee9a28384c2dd246ba3928ebc1315c6a4acd0148e02ff92eed414d57425bbcce81c82b5d361101229ab631bb868d4" + "da4e" + "651671e9831e7da32df66a6717fe940e02a2d8692949447b7bc788a4250b978aecb2fc8eafd834efe944057e80043a80043a45000578" + "b520" + "400040066c020a2d00010a2d0003145196440dc80e1df4439577801001fd54eb00000101080a7e069e563740bf29718574ebed5b64d8" + "a8de" + "60435062eb1f78d629ea21823e0e1845519632edb478439c8eab87ed2282a48c52c5c69b235b910dae08925206c8c01953f42af09695" + "d012" + "e0e504880b8f9cc26b1ffcdbf9ae695a049a21d7dd0f7b74323b7861eca23b4cc1fe039f3bd0037e61491ee7e4a3a634a30a608620b3" + "12e1" + "f7c9a92c3d1f8088f4c5d520a6ceffa2f6109bba34e559ccef55aa5724d6588a0ea9cf3335a2c93a3f85bc0a9df1b14906132dad7d47" + "c627" + "10bba4e867799aa72cecc2139a4f7ffbc6768aa7526e4b994d16c772ba0d57c94b2361d3ac6474bfe2acd09274bb2dd76f297dba97d7" + "47f4" + "d3ec5af0a7f0b58af03ec04a9ce06f765ff0cde59941b78f7de22278aae2617286c7b366acbdd284600a3d8f2dfec9c64d730a2d7955" + "ab9c" + "7ab7d052734b78b842f1ddf10a885f573e7fa072763f4691dddb3bdb3fbeda12c58fabd2b3d23adf9bc2023502567fd5eeae278067b4" + "ce15" + "72e3b03debc6f33152ee04621e6b9be0411029b724c2ae4d75041f0973b946a3c13799549ba8999999a71b992eba1e0d783b0e7e1444" + "4e77" + "61f8d838405b26a85c7a52acfe264d8daafbf3db4c409035a2e17a54280659dd05495bc9862ce9a730e8540fb7bc9ec381e6753aee79" + "afd8" + "0518468d1bd796a611972e2e03a1cd8017ec1f8d43fb486837570256406074768524b663505d87b5c8e0e103d006e19b68a39c57f5f4" + "9185" + "0975bdab2f76d553429a7f13e7d9f44656dc51238fc80b808e651c6cc36ad4f1853e6adf38b4a2eb4f9c71520e28de15192a057d0827" + "e511" + "ac4bd44d7d5451a45905d2b6223e3c03c4ace8fb71dcb81ad88b5d55d1ed6f5ad6b362898baa5a18c861f780b5de15c3edf769160236" + "c1ca" + "a09fa2e1f5fe2bd304d2d050580d9997995c185930dc8b580ea905bcb9630480eaee113ddce3b4d7230bb4a9379159bffb13fba111e8" + "4a70" + "136db3c36f5727381acb137c897635bf4cc1a75cd6639218848928d14b2141fb87da913f00ad96e29e3623bea306cfd100ecd2fa41e0" + "6602" + "568cc66c08f875b507d1f329203fed52242e9313a1a41cd856a980835aa228be4e9174f9a993bd7c17d4055929c1a19febaee324ffa1" + "1ac7" + "b5f5f5389833923df2e127a98814190c37880352b749f96961170f197a9448050ce469d4d123387861d153a1a5679627dd3a892d422c" + "c358" + "a917e545240c62f629551b03b66eb46ea514c8063b4fb10e2ea13c0a4ee49674ac7bf76677537ae6e40f9cb999c26a94517747902c73" + "2238" + "ca92a53fe5ddc419faff02ba1d69ad18c1f84bf4da640e070cb7be2961ae5e903cff878a85011f4022941c15f686075fd002b3f7923b" + "636a" + "8f063c69418719afd6e1e3f57c3b248e015b65fd098bc74e6c2597b303330b34a6a9af84ef4a6cffac039ef7c4436659d90e4a9d947c" + "0e78" + "bbb3541ae6b2ace5b306a70f5820518c6d57d0f88db541677622daacafbb813698754a2b9aff432e8d69b03417f65ec99fd07527691c" + "e09a" + "04c4cdc20bdddba95b2b0413bde8132fb2c86f51c80e765a1de80894ac917a256a43bfd9daa405b61704abefba8552a7958335124de2" + "64f8" + "7d6c33bc9ab0e58f956e17009fb65e651146cc26415045c3d5cb1ff0ecabafbcb3cbcace2bba521f87c8dfe7ab39518dd95c8fde5b6a" + "678f" + "503eb7091a74d724f05ccfb3ca74dd29bbecb303f9ccbafaf9fd65803a412b24fa04274686c8fec46eeefbdfa887e04be810f9a45e69" + "c498" + "fdcf0b0d38b5f3666737c40f45cfb9834fbdf35bc464489cac532ee2e2aa55976087fcb570b2651665e5ebd8f0256a8e5f79a104de47" + "f18e" + "ab47de490db10babadaf7644d4a11655b6130519236baed6979648cf938cd986308f92cee99c84ff6a714245bf7a1c8c249a44057e80" + "043b" + "80043b45000578b521400040066c010a2d00010a2d0003145196440dc81361f4439577801801fd36a200000101080a7e069e563740bf" + "29d7" + "4ed4b9cb8f6eb78e6a5a2f600d62de02d87041ef90ea250d910ebe5292faa225bf8538fdc501c736e098bb59bde78080541448280357" + "c03d" + "8c9b3225f6bb3c01638f947ee50ac24b4d8ba347b4eab29e0af0229c00c830f4021ef5c958902d39d9b6b92351da83f4819311d799e7" + "0049" + "f605bfa7846d9f0d72b0a604d267758941b42a6e01f53fbaa597917d08da474a1596176e2151f60967202ac1c66803d3b0d6222f9459" + "0082" + "21eee6e8deb0b3963e67462b2630599c726cbff75b94cbea43b6842d789cd8d5bdf655f2cab07a3f0a73cca5ea6791fbc615ae9b8c1b" + "e17d" + "59878494c36c8bdad06d169b7f5e25476ec1d48ea7e644dd5f461adda7e70172f5f880745ca86161e100da6aaa7df18c02b479f006e2" + "9e74" + "a4b08e1916af4cc7a4f31cf99f2c24cec6b5076442c434d9da48664cb84c4f4bbea9838f9c5a5010f89dc9f55773b93048d20266c035" + "8270" + "9bf0a0fb7560dcb76b9429b0d1c5e714be86eb4f48070298615ce17798c72ba4bfab01562e06be2f22afee7663c911ba100b8ce91560" + "f2f2" + "8fcc980751863d7c8c8adae78a4d28da74249da4ff36b37c75069a39073f3119bf152b9145114bc84792da79dab1d1094e83e299e7ee" + "e8bc" + "1f887c689f2ce1d6bbb4fe7ddc9ea2a4a6af97fd7e616b11802f930b080ba495084ac340f8fc993a709e858c6c7fc6d30a97f653c654" + "94df" + "e6c3a92b7e9965d6b56ee834736c2bbbabf1ac7e9a1733598e58746840d7da4f2b8e482dd74e46876bf44d20744be5079555589714bd" + "7156" + "c79789988a53bb68624a5ccde6e1a282c9524bd2fbe178da4fd6404201cf6d6f292ffdbed6c5068332600752c057eeb8572df9d986d5" + "7630" + "d0dcc6e3b128ee6834db3b7aea39b48b3638a78a7b83b341ce542f524b1e708b88028a2e3101bef6e195ca6efcb67e50c49e725a8447" + "8f8e" + "62309d0a662486ceb49c79ffe537357eceee0943f38e946bca80c568709589aa27bb5d94cb7ff97b2d71598dda626d03d395b4af9b5f" + "25bc" + "6ecf7b0b1a7f93407ea07bd48a43109a065bb13b4d39f731fbe6f652da97ba27dacdef55b1561ea4328d119151f34bc5524adecdcd95" + "e4ce" + "805c1a1ce61745dc1d19ed6d1f342cd2bcf2e0c697257ee919402990682535db6f630feef1e13adcd3618d32903698924752f037c27a" + "7ce3" + "3b61e8aad81102f93d918a4e617018fb24975f2355d1ec330e66634f5d22a52093f01beb324aa9b3ae5a656339ad6bf6df46d51b1e9b" + "0016" + "b9151de788ac452fde4d782726bd3ba79a4691e9157e298ff7965b42d710cdba23691bf71b0fdffec7e8fdda176e98fcb2340dde1b6d" + "d5b2" + "f6a917bcdeb5f20027c419990031b6467500327a9c53c4c9649f7d7e848193623c925573394c58a4cf8833765d7606cb107ebabf00e1" + "ff0c" + "cdee1fdba3d6836bde91644aa4536b8ef22cfef44271def7dda8f3ededf94ca0b9ed58c076f84afb8dd06e54e812266f6dbb27d089af" + "91d3" + "391f57b3860d390e182fe19e3f5c4ffe58d3e1a27fe7ad8b3ad1c9275fe9f1cbc4ffb5944151cc85b66fe9e261689cd60c601cd6065c" + "e851" + "b1651cd486a8def61f76cef33a8a0e94967a21c84a59cd9d4952836ef310ceac3b5247dcf3a3a009c227f5552055969c691636d7f7cd" + "187e" + "4c7beec932ae6bc4edfde8077b9c2027ab8fdd29ec93e196a14069ced55929d851e04cbda17bf05a6446f7c3d8efa29115cc6269e5d0" + "37b8" + "33965159d93b70dba826383eac0eb0e63b8a7555318a8fe3c762121c4dc27cd40abfd45bda5fafb4bf6ccef802b53fbdb026ca46fd3d" + "4e42" + "4307344e530d5800bf7d6d995d0c65cd8bbe6e03898b357e70353a2ed94e2d2eb9da6a0912c1023c5ee8bb1c0252cb7d363969ba6374" + "87b0" + "9fe2be44057e80043c80043c45000578b522400040066c000a2d00010a2d0003145196440dc818a5f4439577801001fd704000000101" + "080a" + "7e069e563740bf2ad9f7a221c7a23a4a0fc879ee05e60d34923071cdf2adb5878769992dd41fa3e38fb55c566817c7cb40dbf323895c" + "58d8" + "6eb30b826895add563462db1df28a9a3e6440a1453a6dd23b7553f17248cdf9f55a5a871fe5aa0c840a3d360092cbbb4b229af5d1f87" + "c37f" + "ed1f63210fefe0e108e936335bfab484b8cba7b53c8e1768713b6f9058a724d34889fca6d66f9ba0da9ea3c2bf896fd3f70d03798b1f" + "8c3d" + "95af94d25ab56beadae7fb46a2ed94f83b84bf6c2dd6d07ba8bf469df23ef8a264a1cc11ee0cc39c3395f0765f0dc3019bee13482c4c" + "af09" + "b7990d16a0740710fa8032d2ef163bc4544f207c54e53e7ce95c5aff5171633b14c38caab7602302413bbd15d2afab2bfd1cc2d3dea6" + "8b47" + "eb9230d40376ce444e3daccdf2d50cb2952f3068ee64b8d61a239dfb6a47db466237ed62fc1c45fe425abcdc8effacea50d861cb82e8" + "0568" + "8a6723c2cfc3cb7499cabaa4edf9a13e52c2e8e220d5c7e49fa8744d476cc9c1b648efa09db81ebb6b3b9e4a76eb82c9599bc7c78b93" + "9697" + "f4dc3c42612650b97f8cde18cec328c7e46db1fdb0e8e777daecb6704f28ef44b8c2e89d6bdc19dfefb1c3041b1b00c7f969f8837427" + "8072" + "d8d87c3d930ce4065534df86c672807bf300e3ee9125fcb9ab90028fa72f3e548b202cbd93001516eb7eea36e302942e0faea9619124" + "a5a0" + "6b2c56b33a21a8d5ded781dbaacaa6dd769432fe96837e4e7e12233554a3a7443972823947e7a68b2231b276192248fb78afd6d0a5cd" + "0b62" + "d0da466b84bb92c404c98a3fff6e043f11dce64d3d5c1f4a4cb4f47fc0b72a88292254820b5e1eecac7eb96500e584673659f93ae187" + "6caf" + "b16e98c1557175594737383a725d4b27daeba18dd967d7537e3fde2ba7bb5b8a3ba5d9288745a3ae79ad074cc6c76d9c590946428908" + "6eea" + "6d925676d7ec191122410828b532a18ee68320d7043dc28455cef59e3ab17567931fad6072857f28652052c69110e14718d360b85ec4" + "6a1a" + "3d4aada29669bac8efdb43001e10f155da0597fa2a9701c047ce9c14fe1d5351348b3c5f6fc6603b3727791812d65b546105e8c22f17" + "48e2" + "872b16a15faca3fde0b0d7c144382fddf7185ebf1e89695b2abee966ae6d95189310e2c719fed5c25f1851ca1a5c9d739a1bd52a443d" + "ad6c" + "d746ab11ef77af4ac38146152bf0cdf85f6f44839467ec69da76a34a7d47dcd21c8bfe041b33f344c0bfa7d36b247054c665a79cc33f" + "5268" + "56a91a058583be6957b5d4b72d1e4d12458cadcf1d0c80289fa5929201fb15e07ae0e96aa8533ef44ccbebee5f160e2f51245d8099b0" + "8ede" + "6cab162b2badb2b4534209a2feb4ad921ba4c956b11435c8b1d07cca4281b32cfe31c2f5947e1ab60420a219cafdf66014197ea8c592" + "6e37" + "248cd0b20e5e486066ad92af0451240718e4c4ed6be9cc4337195d81c55983fee698fbf24314e8cc8fcd45a028c8c88cb0f43633a3c5" + "cbfe" + "598fe277159a5508bc1040718c43c262522f94f83c710a0ee6a702793c320ffacc5afd09d6fb88f66fae4d0bf2596fdfb41ab30dafcf" + "cad6" + "8b559a85bcc0a07927b333b3990582baa7435245800c5312e9e77051fbd1c1db6cefd72405924915667c208e8f8ba52f227cb4ceb9a0" + "9199" + "ba274f05bf8d5d84f9c6303411b4261ffbc59d07f2781ba1f5b831b01caa9a955f5699436532bcddaf1d4bd947083e7367d64d5c66bc" + "7648" + "d6f8c18a07f87c2ccdefa9b61f7255a21cea0f85a0cf67f51995fe2338722604325ae2ab7cb891534cfbd8ad139fc8b2b38c1e04cef9" + "1214" + "0905c0e7cdac8ec49de78e617cceed19672c16ccf1e7de4bdb0850b35d2f73ee28200b7a0e5bcd8fda3d0531c5300743722968a31b02" + "d474" + "294851d1021e670f7fc4c21a44057e80043d80043d45000578b523400040066bff0a2d00010a2d0003145196440dc81de9f443957780" + "1801" + "fd125b00000101080a7e069e563740bf2a61eeed759c9d502cad61a76b9842ad6f4aa1013def63f72b4ee67fa6e999152d220f0481fb" + "265f" + "cd8d1d20e9c8ec63c4ee6fd1d03ccd73bb573304ee41fe3f4c3694d892866538a89922b5ef07f68734c3e5e7fae1adccd6e21894abbe" + "74be" + "af7de77b12957df7c74288403f928696e276e79b6d22febc3e42b24fc24c3c17d8c7135d5bb06eccae35a6881cb85b1b9522d4cca068" + "23f9" + "9bcf8ba48fa421bb71eba7bbae5f8252fde23b90894af2d999a7dca58bf9fddf3c54f698532f6c05efc7da4cba03f2095a4ea03113a5" + "6a75" + "cc851af1b8bfb64ec114c38ab547ff04c264cbfdd6181b41f694d771a48b2e689c37324989a54b337ae74db0083559921e26d58b2e16" + "845e" + "7cbee395813ce58c6a8b10e6b47be70b2918da7604422949c04317cc33ad08a90dd8898b647753075a1851eb5f57f9b6b96580fb61e8" + "5fc8" + "a5f1e6a1f44ffcabf0fe816ccd208fbd0845f72d004898248f9969141dab23af2427fabf0161563595b054538277ebc138d9b9e8aadf" + "74b6" + "bd1601df92e28ab2ebb3ff4a57e614ff8a16accbdb6edf87a5d2bf87d963e1d9c3be05c3a63676db8b986bb228b516aa095624814fd4" + "b50e" + "24147571a317564fb465c6560c1c0155bbbc4304df56a7c8532ad3e96a397ea87fe66f2115d053e30a41b82c09282c5af4d0ff543ccd" + "aa2c" + "c25c1359e6c40a21b6409e33870d5ab6aa15d43491c87f65b5298db0764d3a4fb095a54ff5691478c602bfb541ffc8778c0353fe48f5" + "d4f0" + "20236c3f2200ef53e879fbbc6d09ae6b9775ac6ddd5017edd955ed4d57f88d2f0e24ff9d04dee9b83bf26b2b204de167ed8f8c922609" + "b8db" + "5557b77981f106dfc997bb994efcd2e12f07a99c49d66641566c3acb0bdda02c6bda285eaf4bae694e9fd6d2b19144d2308186cd71fb" + "dd8f" + "6beae66f831bea7919ad3bc443fde441ab0c704a41a4ff372aa93bd8655ef81bddb0bed7a802fa2c2d38965b047cced233a42a78198b" + "61df" + "02b9020211cdba4acc46cb86308555c5e518fd356253057512c4da6d3df2e74c9416502d44cdefed408b63daa86130f946d00ebed9e2" + "c5cb" + "65b635bbcf4e97dfe7c1260af18f61ffe9f266e72a672230d5326f62d943fc1fe8e5c7d32591e2efbcd7890e1e0f9ef1d2f57e4d476d" + "2a6c" + "4a7126a3e5e340d62f0d0f75687c8ef364b22acd5e3fd3234fcfcbf62083bf760116fef01e413d6077a472c143abb75fbd994587d501" + "6f97" + "71e54847c1fceb2b24cca5360518e89ef7d10ff04c45a4decb69954b7b16062c4b65c46def639d8ed0864e6490a16f4d425a0d4077d5" + "78d2" + "964f034588c1d8bfec2cfa20cd96dab50e881343ec6402cc68b7a561a2e6ce12c25afd3642a4d294691d909cc556d9d5f1c7ae170348" + "a68e" + "d85efe4bdc1091da27a2180aa92d205a58e0dce800fa0ee38ac7ab546dd6f00791787aba2226798d1113d4abdce01404428c05095811" + "e57a" + "f1fe575c56461cd05805847b25ec792335832f1edf674cadd3f598fd2226e68f12e870934cfc11d7c9e6d8216f55d575e0bda5b64ccb" + "615a" + "0af3c398db042ad3ffc409c550c5f3433f9f36f2c43ae69497bb5c3ad69b5d738fdca25867881cf2716bc0459a7d3cdfa0f8de66682f" + "e87b" + "a0e3976cd759a23a8bebb473868988ba6872e9e44a77b4494f5fceae2c3c3bb2388a42eea2471e5753abefa22216f56f58ea99d602c6" + "10f7" + "f504e6caeb035f87335786d559f76ca7e36c97f8060b9d65c91648d0d2b9fb7496e2da9a95c96e986b3c3f024f73a7101ad5617ac93d" + "d165" + "bd49f941e1032e74c095eb8706a4c3a52def56172fc87b07a7dbe012cefb6dd3c738c50980bb50f2fddbc0dc2dd99f47bdcffa43a318" + "7d30" + "4ccb6c9b1912a1f8dc66d7ee15c8b6007941553d6744057e80043e80043e45000578b524400040066bfe0a2d00010a2d000314519644" + "0dc8" + "232df4439577801001fd2f4c00000101080a7e069e563740bf2a383742182b65b90dabf63e50d655f769ad8729a758555d93be9bcbbe" + "d300" + "f05b1e74fabc541ccee22b93c53dd4834f0b8072d27d1cade75922d65ee18b00e28f3860d61f69e7c7411fabb47abc216b9a148fb292" + "0e11" + "dda898c11b58236c74b8c03dc56931ff0a9f2992f607a81566479142b45f0be1c8c0e01337165b9ff9ea7d19aa6489d8095a82496627" + "5f03" + "061390e4575a173aee2fc0effa353b94ffe7961fb20d6d0c5bfb8e314a99f08b1af9d25ead1c0c34cde9884af498eba94ded1eeb079e" + "6121" + "86fcd6d76283073d30d49127ff400d8279037b90d8d800e19e064ccbad6fe7138fc22a77ead7d631f46248d44ff5237c7e4a26bab647" + "1f9f" + "02683d83d57dbaabcf8f6ac237842710bcdf186557aae87f5f74d178fda9bd6e8ddf01a1ae6ea723199c578a9e55aca2f119cf4e4e9f" + "a0da" + "b4d08d999929a20dd8322fa3066d6f4e21f25510a457bd3029175580453f635699ebc6818edc171cab2a2e1f69cb5946b2841269a945" + "a89a" + "0a19d25a687aeb45d65a070c5fe4b204307eb40f7210e0ff915d41956d6c2dcf766ac982933f85c242687968062b51edbb28f71d9b01" + "0b14" + "a71097dd976cb8f64bc76e642d7208e621db391c467c9ea1606b24a94e056fb73a7dda67a277cd21d0e82622bc5bcea9dbcd64f03f2e" + "beb0" + "a01d0ba97cb6b4323a9ae9f7c04fe72c59c152a1aad503fff3c6e6f4a07a74f8c705ababc87a635b9dc5718ff25b9ecf2716efc50f8a" + "42b4" + "d0debccc4550cae711c979b7ff7733583cf4aa863a2ddf7b1df5a66da8977d843d5543db8f14da427c3bb24adf6fc4864a16373fbc38" + "19b0" + "15ae27159deede377ec8a44d53df78c4c030bba77f496f96022d4f9f76c6efec9af8400623eb9dea142ce7bef7f624ebebd86e1a87f8" + "79c5" + "12dfbd9a10fda28324dbb545e65e9df45c8cf5bd5ae013b9a58bdf8817b2eb0dcbd3e9cf27c8c1496c45ef0df35934002e03a494a519" + "324f" + "cc62860380aca620e2976f2117ad655dbfb1cda7832d5dfce7e8f4275efae351660a6cb89fdd236127c301bc74bd32319643cbe67ed3" + "0d49" + "679c239b98002deb07dcac5697bc2969ec582061d8d168fbb834f024835923af0a9dd341210a253c7efe9d24c0b2de6b5f39b86e2016" + "e969" + "448ab7b9f9cfa1065d66686f116ad5e22f2be912eef22d18f39f356d0fe881e4b0dcb59dd4d807083cfb6c655af3025b0daad3aa80b2" + "835d" + "c0fcdcb4db3aef1384f52ec6433df255779665dd5dcdb7b5264dca783e38819ab875fbc185cf9e1c5c7e96c3fd72be24136df52e005f" + "34ed" + "c251e430a3870929308b108520ab9f4395c281280a32ac4f03247c2bb94bcd16263d509d950953f109caa25998456ebde28275c26572" + "ad53" + "879c62f592bee8ab97ee36660b190348dd1e7e851275e3d9e72e0ad23e0a9777f05ff97d74e677044a551b8e944e2167ab74469b8ef4" + "c2ce" + "5b08b048c8ed8aa8d0117145c7634fc2f8987b20dcd5ef523556d7ea9b3c144dc55d89beb11b5a79067826cb47c8c1d35571ade600bf" + "3559" + "3d21b82ac89dfe7981cb989d8849077b50217d85e28e6c635ae988dae76eed790b8d0a1a257214617118d73ebe7459f3ea86e4da8cb6" + "64e7" + "e60914c3e77f6744f2e29a9cbe3ab8f41fab89ec93095adf39301e8d64008e276727056a45724d370f0b38a504398f88ccd89bf13f50" + "63cb" + "e8a6a891d58585f218ee5af66bb0935f3d822227bc1122b9aa2ca10e179db77dd33a2aa361d7fcb3f34c599e13e2ff70750e09d39996" + "c624" + "9c18a8827bbc41acc78b6652120c1d70f01a545ac142e4cbe5d93ac10e53f0ce0352b21f3bfaecaccc86c3977bdd8d7157f65556fa44" + "9242" + "b9028c9fefc94ee338f02eb968f9e2b889768bc55c61ce2979c1807662e244057ec0043f80043f45000578b525400040066bfd0a2d00" + "010a" + "2d0003145196440dc82871f4439577801801fdd52300000101080a7e069e563740bf2abc8eee097f88ee74d36f77716debd7b830af21" + "f039" + "e2ba9bc4cf7aee7f412f1f8b3d1de32ab217f12665981578611d9deb5b620d374d1633bf82ac88fcf68219ab6879ccc32658208d400e" + "a188" + "089cabf17d8e7c3cdc9de0a1d47f86ab0abca36468ab5f782b4fc35f520cba4e9598d76d20b11f5bbcb3d4a894784dd547136587d444" + "4b14" + "d0292a996e4b69084ebdbe3fab2c20498639f43a1664d5b381ed9f5536b04f700629be4e017a2ae2982f7a5fecc43cb2d3192b694d33" + "e15a" + "a83ea4aa33746474a13a19e80bc88944fe2f6bfb164d777ec9b25e03835d50999cbc22bf4fa0b0f4c5a3a68228dcd77fce86d19ba815" + "cd0c" + "9d8d96fd85d88fe50b6ebf6c5ae25ccddb809261f9fb60b42370f706f5a8c9a65667239c3971afa2e8de418fa75c779d7de493bb9b20" + "d7bb" + "dbdeb89c043854968c150c28067e7d3de845008456c69fb9eeae146b37ad8f38e27ae896c0a461f36148fcacf4a61c51edd5cd4bf31f" + "64ee" + "07928d96264ca23b88ae1a8aa12d2cc91f90c66fe2a2ec0ccf7fd973e4c5cdb19e12bc464dad6a794b95b39273a22cc97dff073cc970" + "7671" + "8672ae1b71482fd37a4eccc27c6c1b76cbbefe6d5d375516a66cb511da4f259f96fe9e4ecb2aa2d1c6d879f5b03f68d2c66610db8868" + "91ff" + "06af9371fc3d777da820f30727b2ca16bbbcba5f0beb8b7f2fb4f6a95018310cfdc0f9b263f32df1741110f36bfbf2f0ac523f60ffd0" + "7bb5" + "b4155ea7c33aa0f2115751800ee564f798cd7be6a8c873772e5fbe6be502e19e5a02beb96c8385e1752c166b78ce9eb85ff4dad61edd" + "386c" + "938c21975c046ea7e5cd10d6d87be08113bf3a4ab71b3e827e700dc9e23b1c3e8d93005582fcadb3ac9aede42da2b4909c27d1a836b2" + "4f18" + "3f3d0d521feda4190b5a852d019405ad26891fb29ec8b3652e8d434663ad9568ed1e8e8ee5ad96411191af435328a7eb52055c3e07c4" + "8fa7" + "90ffeb10c165ee8272a41ab6b95b243381fb0ecaba9b099082d964ce7814c8d43861f05710e3353e0d144e9b524ce9c6026e880e2721" + "2d79" + "b563e5068a7b49d30930cfa1805d4494f0970c70960eabf2a9f87784a578ec2b387c7099003c45b1c1c9faf54cd543c2e0e287d8f258" + "f391" + "41aa7369ad28f2d27694dd53fe72cfbea38b5dc075d7a4e427d26460cd5e892ff08abeed5dabb99559c14c6214aca07cb218a537c299" + "1e4c" + "fa2c57ae800eabccc5d6ddf2112e1ae7895fda2816dccf3ed0e3932d443cac851b923bec4c0badaf7fb1338a818fbd6e6843ebaf3407" + "3603" + "8b9bd6888c897ecefb14f9ddb71f57d2df4cd29a180cebe3ea0e25c2a9910968f32ba2994ec1d5a7b8d426686c6f0a5d2b150543f78e" + "6488" + "2d5eb52affe50eaa58cee4ff8bef3fa7df8e3cbbb46e01c802d85caf1be1687555109f2f340873ad304639fd14ddce7845e4a4889928" + "7782" + "32c8425b445fdd5de1f435b2be356841624232628f64f1a5344642bc8c4a0c3cf06c70717bfb0b4098f5d4dce8b1945f21226d1d80f0" + "ccbc" + "bb33534223ee68298d4bc4cf840d88c6f34b66f111adc3a7bd93d05a339d72c6368df20b9043e6e48f897b9306e3bc45e322047cbada" + "3244" + "ff9dc4f179a470ab9bc7718d0227dcd7c1c49e0a842b07ee0f12cf94e3eee66232fe6c3f36f6f08fd50d7608bf5155abe4a15963d22f" + "b6eb" + "7c1502ebe1a6efa74068aee711e454e88c96a8f861c76cbddbd3f88c0e3beab7f08fdb8e68ff30bccbcb1de91b6facb872db3d85d49b" + "cd67" + "57fc729fb750669b3ce96f5d00a717884eb53f6fe0ecd152ebc1559765f4131526d4ddfcf5447341809acbecfc20c879f5e5762eebea" + "7318" + "2c6c8f3b067b472e0f8dbb11acd14ec0bad2929eb6df0d87896289823b4edabcb3c23bf9b7d2c044057e80044080044045000578b526" + "4000" + "40066bfc0a2d00010a2d0003145196440dc82db5f4439577801001fd261e00000101080a7e069e563740bf2af8d8bb5c75e5d9f6e1c5" + "af03" + "4bb3f7f0ce8cc0e53d7aab489854a02f849e286520dcf9371d9c9f6a43d5f606cb7eff2abc7f9fed697028845a888be0491fe33cf3a3" + "f63d" + "0bba4e880f8537c769e2b9c6a2724d213d43a680189a7865cddfbd0e09c5b2fd451e4915bf4ca30970daf3bc657d6148f8ca0c415c93" + "b41e" + "686edd7ae663bec135ab499bd23fffed97b87e019fb946724127664d2ac4aa87b084730778c3020866f3908c9cbcbef681c97425cc37" + "b30f" + "7056bdcd401925eecf8a1db0b1943add96b56827d9b8f3197765743ab06e449731b97742ac7f13e54917413f8a9e04cfbbaad9e8b3df" + "ca50" + "2d698558f58359d86a1a8e619df10342861d15e459cc89be3b20c806907f64e16a1115d592eed6532870184d6339b37ead8ea5475c4a" + "ee25" + "d8560de2ffaf2f4c71b02b67cbd0c676761d980a08a4997466b95f851e1866b5f890bb678d33268f5603cc2183cbb55cd7c909b641eb" + "42f1" + "3a7469bd6d09192463a0077415c4ecf9cd034518b31c8693b2373dadba9608e8018a957d4fe823cec7014c2603d77274599d1d7c21f7" + "25c9" + "bc24c6642ec9238a6020dc9fe3c4ff339e2e6b61222f2097bcc78e339e0e5b58654538756c6111556ba4a324b9fee69056d9f7c663f3" + "b6e1" + "cfae57e69cf6d4af45af1a489934bc7dfd8a904f1d017888f61e5787319b5e46ffb28f4784aba7aa483be29aba720414c1bd7461b2ef" + "e0fd" + "534631d7350bb364d6388ea5c3891c78d4b7c78d6a70830a62f84c489f1d8e0eb94a967d8f50dab8a5002e89a8178c9a0f24ebd848b4" + "1837" + "c66ba9ef6405e087ea2d890ab1373707aecf109900f7638dbc0cf48ed378179e39e048ce17c49011ee2a83f7dfd7714d05acb1f9a944" + "9b5b" + "412e1d9a624e8d1f246f2f241c5595eba3feffe9f8fa3f5aedbdd425ce29bab8e7688feb8f9dabb354a179ac405b9c5409792f8f1bd4" + "5be6" + "cebec267ec3de8d4550123587accba0a3e4afe654f3cf0f4ff460719f4fb40f36654f5e225bf4039b3523019997ec82c9ebcbb6110c8" + "edab" + "ed8b4ba076ffdf5e41bd4bacff701a5a16eff3fadc82b6b03b1cb3718045c92dc306cee78d2fb2a99e3c5ebcf0ac6910a9e821d7c26b" + "15e3" + "5c520bb71a0aa07e0d7889f77e78efa7086f604479c540be098f458fb32a05c56372118fb7a989266570fa7de2a1df2b715f9c41c36a" + "b639" + "4d0f3426fc6a0ab9436f2d4860ebba5894153c1d963ae3bab0fbc10a61ee6f05c309593cff2b7da1a3b3433475b084f6f4163d456024" + "d742" + "4f3e1219e3f23da4b82fb3ed799a5056198f957548a36b1bc382fd2c1332694cbef2df1aa50142dc961b6895258fcacf32cc22c20c31" + "632d" + "3ca400d4977624f7641385bde0d69c5303d95a58af76da8ee49875490091bacc5b017560982367907be12fd7ce0dfe1bc7c2af86fcbd" + "872a" + "14c4f3a4647df6523d31850b96b7d575ab94663ce7fd5311d2297d3f0fed50a66c26a5cc2e2d28d630ac0020985b3d669aeee4d954ee" + "419c" + "547a738f0f8fadd1fc12eee4a8210d96a635c8860d06ed995d3ac6dce96c872fa298bb24006502ee2aff91e9303d040b63e6d51b4100" + "b8cc" + "47c9dd9084beedf9d3b624d8e085bca2002f51faadd85de96b9eac0d62dc514cd3fd5ee9e046400bd40c7cd28f30ac9a1513069d69da" + "6c52" + "bd9949f7846e25dd66ddf0533ad3d9dbdd32bd47ac58523e0015ff98a810402dd00927857f820336c3b1dc1a9ac4a558134b65ad3da1" + "b551" + "010215e7a4a9b79d59c774dad3171527568d0c76bcd229251a541e89fa3b24a7a1b0677a63e9f8e864ba4da5694ab3a4a463bf02bdc6" + "6b28" + "a2b62162bc418c7d8037c326b50b12908066b1dd19293982795c7da3da0bdb8238ea18d7e2168bba8165cff8c424a61644057e800441" + "8004" + "4145000578b527400040066bfb0a2d00010a2d0003145196440dc832f9f4439577801801fda02900000101080a7e069e563740bf2a17" + "aca5" + "49d8dbab7599d78df435bad80ceb8df61524054df2d159aa1c3f023d13b8c7e3ac57d1bfc0f1f374b8d99bb5d5af44bf3172e503077e" + "ff73" + "d5bcba48f65e8e12117a26331fe1ad98a0deadeb43398f64fa03657f13f4a8a6c78e0dc495991fdbf4e6139bc8a6a4f5d09c4aae527d" + "2024" + "be258ef9cbafd0679667bbc211b3a72dd117779023522f74b8e3973cad0cd0c1084bf0f51de6ccd7599e644e9ffa8e061dda98a36946" + "3835" + "a31c3c9df1c7d35eb5dc45d58fccc621f475a082d457f7f213d40a963ee0fee3fa8e24371f229dac77872e0e2e2f66123b24062af39a" + "d61f" + "9fe412c1170d8fac7cfb2e6c2aba4ac536f459ab2f21ea8bf0ec60a0d4c328ebd89ad720f0f1ac9e362602672798828fa8196d4ea747" + "8acb" + "dd83a97c7c037a102ac060afdb659280b9f32e4d18df5c438c0cc1c0befc495068f373d3286ed08890af10e2f337c54d6891bf4e3361" + "33c4" + "f846440af81e4bb5ad307ecfcb624697c4b4a6592db3316404312d0264902be2d6fba7e7517ee74f13674a5ebf953eed625e1ef7a1db" + "fa7c" + "6deb0bdd6b9949fb2598709f5ea0b5cb766204f1cfae980cd124731b8ded85fb76961266316750808ba220dd6bec9aa7c9e1c0d21a2f" + "4f71" + "92e798255d248e0dfc6eb776180f0333de7f964701b644da5bb9459b2ddaeb26487e35402e0594774cd228e782e1160410be74498260" + "13e9" + "b53fec9627468d296546b07e3b6969e4489c04dd470993ebd928cf362577b20253a0b3b996ab7ea89efbde89447f46c6d47b7aa8db17" + "549c" + "2eef3e1e256d972f828d2fd8801fa8659944da4dd2cc26254390acb8a52ef9abf0254b83fb6da04a91dd1a8fe7260dba4bce58b12374" + "e7d9" + "8da9191f2fd106a57a298d8e8e26c5e20904d7f59ad8fa227439a4a78feafa9741fe47d0e84bcd1284db2fc73c0b249307d13aacceb6" + "3ba7" + "7dda62fb5764acbd12f369e68a3c1573ff9990760ef6892cb17962ad771dab99d4ed256325669b5888e5400ff1a5420004a94e29a77d" + "4895" + "a33b5b515971eb55d07b9aa829312b7d7a99dbac61b6bc2d32233170d0bb1dc0dd3686fcd8e8055d96b6a2927a08e06911b4aa0beb24" + "2308" + "5024629cb5eb9f4f37cee8046d524a5d27ef255cf7b41aa9f47567ae2d12e922d3743028438063f14b379e7d2aa47bb6795e068ce1f3" + "c235" + "327bc26d5127a185ef195c073841bcd38e63b71310c5b33e1b7c02c08e14c249d4079cb4903f9a344dd9be25f4a5030934fec7644e29" + "4ecb" + "3840414cb168580db4d2842936144e54f4434a7abc82d5e25bb19ee14ca62706b49179612c561389bb3c019a9e0c106a0404b4b87e85" + "e6c8" + "4f619fcac333c2a953bceea82a5de492c768d8881b1442df3295a4005e73fd767cf262cb28248e53519a178f0f5cf685c48a3907ee4d" + "6779" + "d767dfa78f009376aa2c3f64d614f97b798fc6651baa1b634598575d2fae3bfd7c148c4ee97aaac63e74d676c597b89d7f88f9cb57f0" + "028a" + "25253f303da6225395d13c01ccf551d787b775f00edf6c77f154f0b823584057416690ccd9f717538c25bf50c096ff8683d57a81abff" + "8558" + "73a1974c20dab6e904f21ae0c01263a1e0d56b32a73b44ec1a5ffabde0ddb499762e6f1769982c1eff86ef2461c92f0c3ec3c8e32c2f" + "1287" + "3633ac539b8df915b8fdcc3aaa1e00cbd84eab46aa78adb4431ab4201736e6375fd95a03670528a3d7baffb3d2bfc53e85f00f44ac5c" + "ab48" + "d9f1e820fd4044e44803030b9f08281270c618cac17d84b186b24446bf77fe3bf33935af641e40489b08da19d96affb271387b7cb331" + "b3d7" + "79d5532e0610b0385137418952d8337b39da9065b4f3332cd5df10a5b867364898f4e67813142054ead3a436c4a40a62219c960c7968" + "2cde" + "1f44057e80044280044245000578b528400040066bfa0a2d00010a2d0003145196440dc8383df4439577801001fdae4c00000101080a" + "7e06" + "9e563740bf2ac2d8a713e6caf003ce3421f81a4f705ef9245877213d6524cf121a548b2ca819a0fb2d5796546525e9c93129d616020d" + "535a" + "2a79ae034852757fb019461c12b95e0d13340fef3f1e893ae2ba471b931d6cb3b74635bb2167e3e3391c069df988379f76026808b78a" + "880c" + "b9a9e79651bfd08b9b885404e3247a98106705257d6227ec9b91da489515b605e95a5177c05111661d28c2881b1ca2a1d242a2ed6009" + "3b18" + "c38605ed0b1909bb747a50f07f5e60b6843e27c0892b2aa2332c450479e2098f95dba88fcb0bd075b81c16c475b6213e7ce8d23afb06" + "3e7b" + "482776ceba1e88fecbab826762d36ec746d34ce6141fd297f722e34e13f0419bbb23d7549dbd1dd9ba79124a24f7239784841bab69ce" + "bdb2" + "a0f9d7e66f6c3a05048a0ff94e2e0c0efd516e8698a1bbb8c922054a1c3098ddb890fae90ec320da0fecb4a2c9086b72a445eca1b71b" + "0aa0" + "b4655168edd4d3d6b91ae02fd1ab9ef4fc4fb51c28185d6373cf090b738bb530d081c3eaff08916754f96c0c0a39ec60c5ab7c31d854" + "9fef" + "6f6e4fdb08fcacc36df44a76b001c10c28d6f52ebe6d577f571028098dd2089a111d38ed654e243e52a292bdd5d57985804d9a3fd084" + "12d2" + "918d97b3299e34ad2c9cf57b3145f19529dd87a79165fa732f87ad2bbebc8c41d6e4e591d42c350407d2327b1a202a682d03bb0fa6c4" + "21d1" + "e355d958a158099a8d02f514ab71b7d2b1613596c3f9e96c98fd258a9177872ccb847c61ef0df5eef69e84da2fedadf36239f570d855" + "474e" + "c99bd9a651f3c74bb7d626ca52d57630eb3bc352ad98b300636c9d911e1fd49c15c178ca222ceba96f86a4b68f5ef65855c87f1c9006" + "1f2c" + "fae3c74ed699e1cd1a55fe4dff9e07ce1c285e1593f6e180991e1d37966eb0f84ae454e3915de5a81c5b9fcd4f88da53f1afed9827d1" + "c5c0" + "5052defa563adc7e6cf117c28e04a263a7f387a5755ddeae2af02169311e69c5bb24061c40b1801bdf75c233f2822c3a5a47b3488035" + "2322" + "6fdc4bfbf14eb621069db5f5bca88f8bbff16bc67b9921942e948ecf7644c3efde66abd9aa420163e21337c7dcf8d0e67bc757003e20" + "ce63" + "ae6bf92087372a58ff9161de96587564b0aca9e8da5f1c207f26320bd1eaf2665789657d08b675b80856ee122461b020d8a8fb871a4c" + "b719" + "d80db0ede0b58ada7e5b3a98ea0aa370fbfbb42617e394373fec9f2f6e8e1a0e991a8b7aa03b9b36252fe44382398ab9a5642e6b52fd" + "91a1" + "58231aa4ab7a06e3eaad2d8d171161c8c7dac00535c3adbc74ff222e29dc6b93054a0a94603545b2f001ce7f962f20190bbdeeebea86" + "1db7" + "1334ab5634314647ebda9e077e2ed79138dc58d7a45c6a6f3e507216d63146f739f50167ada612487aa709ceeeb04873fd3e16f392bb" + "6f67" + "9b8bf46db2e651ec07d870f60fe563a083eb5d00405225af9eef1ec879c1e0f7d1a8296724db3cb2fc7185c56517637d30e42285cf9e" + "4066" + "62cdd05a99dac47baf060813649a3e90a61c8e37cd0a43c92b63daa0835798dcf17237a6e5d53f13e7de66b520457af3bcfeebe6a53a" + "a1d4" + "e04362df88f6376b826adce4a2398d2333325325f0a54fdaa65e0bf02423afce6aca2dfb073d41f083d9a0ccc250dd4b1f3f645a1602" + "b6a5" + "4e051a32d335f4bd4c9d650615c655c9ef5a80b76ea138d0d3fc5d6b74317ee8d05c01b5b1280094b1135f1b6a41bd2e9818f5048671" + "c0e0" + "0405ac761ed1fd61eab78d8d165df82fd8382485c2377a5040cb902a04a732e30f87839dd2cfcb908562dbb3801adf95cbebff1b49e8" + "d5ee" + "f98cf70b4fc9eb8f5c2e901fdd8bb49f737cc2a96bcea6c58be3512deefa63673d5b3bbf452888987ea3f16143994100aed15c0e4ad0" + "eae2" + "70f9bdab75802b6874dd44057e80044380044345000578b529400040066bf90a2d00010a2d0003145196440dc83d81f4439577801801" + "fd6e" + "2b00000101080a7e069e563740bf2a0c21088d1e98bb6f405da69b7114b31cd39bd9fa9f8a3d560c971a7aaeffa0d559e519354107b0" + "d6d1" + "987a6c977980e2affe708ef7d87210e1ff4e2a6f80bf320171aa2af3ca7c07b67a20efb1990f161dc2136262b637995d0cb1098699aa" + "d650" + "146d15cad64f013d8e12ce6bfd0b26b88f7ac7dde43dd36eb5648e07dc908945cf2d16e68df090c078e2a49ac7296abe92b403834aba" + "392f" + "ca42e24e679d8f9a6531eb33ddb131ddd303ae55ff2be194a4d33517861c44194953c7e947462769f9fbc6267c77a5001c9e319fd757" + "ce85" + "53a55dda54b66d236a1adf94f021fff170e6c79f78f6df0c70d89216c4184e4420423a57c862fc978ca45a5cefc7c483a4a8b20bad29" + "5624" + "9ca443fab2db282e2f34e9780758b0fae6ff33276edb1b1319a57e962d380c9140cb1ab66cc6f20080682242955ff1c03cbf83f88ad5" + "42e1" + "7df00d728f178cf3a2870ada42d0e2554d9f531ebda44dd28779bbbc240917396369f293ffff3df2684e1c2b775ae5674fb65875a41b" + "7001" + "eb8cdd081ca4a0333fd22bd1f1167ff06761b2b905eb7934df662d35eb12505cdf8c56cacbee18cc0db2cd1da6ba7966fc3fc51d6ba9" + "bdde" + "92c2b1dad4554728b2605bf33928329089dc086727e99250026516a3b54d740e6cbd8c59a26c03bc77cf885271b53f2b05cf80ddfd58" + "f7d3" + "73503ed7096894e860a8acbe45ca804669e677806f1539e83a78b8bf157cc4119a6f488c90014d18ca0e3970e15e47467c4e40b62789" + "48a1" + "596ef01c5d75b828b1837e53a2a61d11545f5072d4f42ebf7223e3992207fd097f49e873a771f338c965e290d42d9e85656cd007b3a5" + "6326" + "5cc81d451f07c7bbb105929eb173a68f9db4bd83dddbe23204e30afc87e7380af10a342f13bc093428d909647334642bac17ba3b4ced" + "923e" + "5f4cb24c73b45db5d0fd0c8d4efd41921fdf1d13171dc4e9b86b759774e35529199e0b1f90136d798cd0cc24cfff715c5121e1c4952c" + "d5f2" + "fcb1755f8b3c3253c32f0b2e908fefae3174f2d1595c80226df7ff9cbb2e739fc0db3592ecd816207312327e6457c9bd524ff74b5013" + "122c" + "6c6d76da9dddf18453d1debc6ab19046aef0d78b5fbaab199e2e9743032899d07e58c67cbbc846668ec7d806d672d47cb0c7ec2e2841" + "a41a" + "091be88648752a1b0ef2242e26dadd564558700a378ef7acf8ab19abb5844cf7942777a68bc71a7fc534639a31bd574e94c1b9be0ba4" + "8b32" + "a1545d7fe9595b62c5477d6a731fcc42560d19fdeb38e99abde7eea8936b06f724f8a2dc772f4155367011c72d30020fc31fb201fcde" + "0d8c" + "c96c41261f66de063e05d1ff942b198400ee6e3c101bf01d79c25978b62dae925e1764e3ed759c2658f29b8385b62d72380092dd7df1" + "c650" + "82919dda6f6c9b588acbc4c5dc64006ec875c9b9ed153c5e42350c7de78a6d06f073e94da4ae5c91bc9402f5edabb6b59d21ba93d13e" + "80f1" + "eb11add3b98d91b284dc3701911b802d336e90d64a84a310a1dd97701361ef9ad13d0381739b699fbbf3ce0fb8edd526740841525c5e" + "6a27" + "19bbe2069d706fbe668adac924899dc107d64bac40f268fd46fbff978a828f581af05baacedcdc8294b4fed0b9fb73c7baf1571ccd4d" + "7e8b" + "c0544ac917fd7d8d5b64bb91f7d49aa4471f5c192fe488daee786b4320f6a78fc8f3ebc81537e9027bf57882ac64eb044a866fd9f1f9" + "7eac" + "fd87400aec986315967458ba64c60f5425f12d9c8087d564330779d88d78072194f55373e460e963e10040305baf47d2580c6ad68345" + "fcf3" + "864cdcf653acf4fa88c14def7378453f9549556e8a5f168a1633418cca8ae744fd5aa21be4defc17a4b150434d1eb3d036573cea6238" + "395b" + "577353d7ae7325a0293c80de065a0d76459b0044057e80044480044445000578b52a400040066bf80a2d00010a2d0003145196440dc8" + "42c5" + "f4439577801001fdb96000000101080a7e069e563740bf2a8f2196f53712eff2cde03ea9cfe3528f171e6de1d5b3e7e17812818a5384" + "6f3d" + "59e23f3a9ec7145c0ae86cb85d3744530ae190eba81c7454038ebf0da5e0329b5d79256687af97dbf754de8532f6c7ade76a1f6d1003" + "84ff" + "2cbd0bdf4f3105e6c232819cad5c50d2a99182e6e28e8064eed123d79e0932825a0e79e4a3d92c417422727ab6429dfaaf4c0d63da20" + "eb2b" + "cd2512658ede0c99f4a54b95bec187a310ed357944fef9df08e331e53c390e795f79248b6f16919488e4d93e4830297d2913b5fb81cb" + "9ca4" + "3474262c68f8d81bf97ddd0e9a7fe5a8cd7cd926bd7cc98581349e7960272947f06418eb85141372f1de7a989049a978441c84385995" + "aabb" + "0deb6bd3d25955c9a977ac63038022d1e91e260659937693c1c144ddae8fa24dcb6fbe5507514ba769d2ca556457b6fd320b58496c5a" + "af1e" + "861999d557e8645847029b3931f6d4b0bac580b2c1762d9a0afe1cb397a49b6e1340193802f870ad58ff2fa9978d06ca8ab530525f48" + "5ef4" + "aac91f7755a79fa0f50700c96d6a3a7eec0166d8caf75b9640c5bfe37086fa68067f428851cc66198fb7299e2041c3c4198a40d84c9a" + "96e5" + "3d552d622e871b6481dca60c9157259c6bb9ec3739282ef10510b7019f8147f24672a6744e397683565aff36f4fa15075e215228da6b" + "c4f6" + "450000c47e7335d82992c4cf4d5aa603b4e2d7e42c3435b271c55f60761bea760f822a0d7b0907a0c0f234c5ea32a9d1cfe5583520d7" + "f791" + "763135fbfe584f40784a8f4e8bb173e5afb481aed5a743ff4a4af73b609ea7e26a0d2a57bf248d0613bebd8489be445aa009a3637531" + "c263" + "942de771ee88501987ee95e6343fd6a03781bdfd113384b529333033771e4e0b18b92c182dd7dc7a2b2469d90639499f487dff034480" + "48f1" + "11e1355e824418e7aca1e976e7faf353077c0c77b1dd472a0762d90c4beff87df9a0e5b7a254989e66003b64459252f42393f52348f7" + "1250" + "517db4d13b2f33b5ab8830487100447e611fa0696057a2c928869081fb1c39fa73b874b4cc46ffef530a629d216ece981a16e95fe498" + "57e4" + "b32f4ecb0221cb3ce81b27333471d0d887929eeff3fc88aae10bb936c0f402ea1c1938255f579c82affef0a45c408adf154edbea09fd" + "b2ce" + "b95536fdd75b8d5ca0d81536a3c636eca7b60603d4a93aabc6499bc73a5fb01fcbffa9b6d86b837a9e43831f1ed888cc87cd1d74aa4e" + "526f" + "598aee7d0651c0655774124a1a80563f720558b0e50ed6549a263fe46d82ad51ce045882019d51da50b7a8cdbfc56034643833047f65" + "febf" + "06f8f4ca864e643ff907a2881c85386091b301588c0d8c33f94d46da0b58080da82a89a5611faffd219bd25c9631a2ac94adc234707e" + "47da" + "7b720256752b3451d98291d1b88803ed63065fb393e0cac127ebd7e5c70e607256e55f23b5a65d177013502761fa27d56a11a98468e1" + "a431" + "a39daadef7c92cc432ad163032ddcea91dfbf3ba69688019a9d7593f40427ac554f5f0d7c62557160fd14d8cf463ffaf51c6a75354ed" + "19ed" + "d7380defad7df1735481559553eca5b4d511daa3402ecbc042ef1de0af565a0ccac3ebc7ee460a6d8049ec257b60c7cd93b2009e48d9" + "2b3e" + "90ea83baf1be71cf03c2f643c50a00a886ecec90e35159401350e40b217938eaf4ff809ab138d9003c16b0a7d33edb5a51888a040f27" + "34b6" + "c4e2316d1eb1fd101fe634b8e3422c778edbf48555df8e70a4d2f6e6e7723b629d98b7a637fff947b04e95acc6340b595f76e5983b22" + "bdd2" + "1ab080852650fcf363b2e4476429c24b9d7db97052c5cd4d39a64bc717f8d55375566bb27f178ab5ca4a114370c139daa40728e88987" + "9aaa" + "114523d64992865db285dad985a90eec7cf77085d57d426c989a7a0444057e80044580044545000578b52b400040066bf70a2d00010a" + "2d00" + "03145196440dc84809f4439577801801fd4fbf00000101080a7e069e563740bf2ae5209c3e4e6a2203f125393c7a0f4936dbebea1b90" + "c30c" + "457ecd4663312c379e7252fdbcec9b020da65b7a9803f65e65dcee2cbcbb3d44e0f17ade7f1fe38d77ea29c35f35d685183076478341" + "92c6" + "0fba7966e179967b9c43de7d4d6b8933d3d4f9f2ec1f6ed723d55b73c0e2fe86e3e162e691347e0c66dcf86c002352662dbece56e1e0" + "942c" + "ffebaea229a07202ab83dd5eb09c1927693fdc0538aa22d943f438351a8991e2098c782396e419b7635cd2cd52047297568e76b03d3d" + "e0aa" + "67417feb631f558278aef0be2420b14bf295bace180545f3aa19b1a713f0d43d71a67e8e1c457d40a9ea95022cdaea1f033df9017af4" + "6011" + "f75547e8703ab88a73e182064ecf7952d2fe213c32fc91f5b70da3254c613d3e58a29cd27e8602634d1191d4f835731c0e0890239a24" + "b8ef" + "276d6fc8b4d42b579b87fe1b560010d20ef986c104a33d5e0ef1f1e20d347a4af55962820c3dc02bf74342cdb297827fe847b112bf8a" + "5d2a" + "a47fb3a445a0c4c5c6bdbb3a7a94b850f81005e2d2fcca7c1d43052c6ff12e707c24491bae3e048a124785cf3e4eb1d0e7b5e4f1f923" + "134c" + "ceb6f59a701ecf219584778416ba30fa256667bdd6729a999dbd37544e2e3a37c82cc10b3529cc4aeff3e61891b9c0f3b8445db80c22" + "a914" + "8d7d87aec5bbc1e633f77b1706641136f97645abe8afc6e7e9da2abfa21d61dc151a258ecfaba01a4c1710270445222857212cffd8f1" + "9153" + "b6a13360507f28cd259b4476295743f15c8a4f713ccd4b67ab6ca557aaa74fe235e803754ab5dbe8483f4262baba08c61ec4c79be034" + "2fef" + "c759b19579eed014dd633c2010fe77f07d276ea88438f5864b2873d86bdebf087493181e6cd61114db9606c9cf5fc2c7a3a76b3065df" + "b4aa" + "a7ad69c05f8bc75a5dddabf354903c78fd2ddbc16b7b7f00bf3583cf31337f0d835ef36e97515ea34e278eb4a65c3a449745dae72d97" + "4404" + "16438eaa2adb941eb6c727eb6ea06f6e21d72878c5d5eb2608524b26aca64e3b64961e446b52a7603759f97d499da7b15d8e596a4863" + "11e4" + "1c8c3fff579294bd417a17fa49f62595647b9cfd24a3e83c3e3e4b76a0f3f1f3cc70808bc71f8c383976d81012a56d9e1fee3b72f8f3" + "f9f5" + "02139c47bf39ad586b7fb4215e0ac7190022976e6640d81cd58dc90ac90c854cd100e58605a0bb47613b0484faa16b70744ba0e8837e" + "83dd" + "d5f7f91ce102c98e550a7721565671c9ef754618e098593f31eb7543b38eb93c2f757f5d2b2a81e4c06ff482aff877a8c2d7c18c7bac" + "754a" + "6de8b7c9e116887fb36a48bf401613ada5c57acafe8a8c90cd6706dd11ac95bc3c6050347b610bd1609e4247c43860db8a0906b3741a" + "7bf2" + "ee60bb07c415cb4c1beceb796ed423292359be278f1827170adb019294a2a4b863115d8961833ab21647a4c3360bf2e39d928974a3cd" + "8dfd" + "ccc9a1bf86bdd46546e246485122baad3e25628764f188721826ae6a27671b62ab43faafb15d9d8da86459371c170a2d0e0fac711f81" + "3ab1" + "e54c760600055c55f1d7e30f802666db4d7417ebacfa8290070edfbe67db9a85597a8450f9f9fd0214462b14c8b0db88074e00fd9c4e" + "0acd" + "36e8099cbf055c7614ba0b591e8497f1682657d3c674910d59b0735fd4ee728201dfa07ff9ea8854e1547598ce4bfe25ca4df1ae643b" + "f971" + "0d4c8cf15e24ef4e4f0e30486e678060822c86579d51d15c8c5f7d96de05f521e87b39e45a3ccba32d97f3dfa7377f09ea87649c374a" + "4d8a" + "69ca8f8aa4132e81e6c3ad3dff2093809eae7e65fc89e11444c0797ddca6914a2855d51da9a05242c90d8302c56507d0c3e090925ddb" + "3f30" + "6671e62864ac042aeb6252caf6e4889f40e5beb2c9823c068ef8a0aac5944fcea510ecd72c44057e80044680044645000578b52c4000" + "4006" + "6bf60a2d00010a2d0003145196440dc84d4df4439577801001fdbc1000000101080a7e069e563740bf2bf7174d901ae7e3ec7acddf31" + "b296" + "464a330426077ec425efccdbada85fe11248cd72830721aa70074f056fbe7b177817d846fe675dcbf77ccfc372a3afae22d8de28074e" + "1c01" + "fbca89dbfd449ad291f300949aa9825f98e28c4e9bb87208aefdbe6743b35dc54658153bacb07e57961344147477472f262e3c6cb95f" + "17d3" + "8bce243695dccab329d2ed8ecfb8cd0647b4f92df286d073e4699e7732f9eda64449d2a3c6168839d4015049a732691943a2279169f1" + "1bd1" + "a84012d2ce8ea05f5278f8ad5f355c0bb3ea9ba9eef3bb574c579bb4f3a0eca40bd3a87c182027d96dbb29083868f50038fba74eea5c" + "8e3d" + "4b576a4f30e1ae942f0168423dd44b049d9f20533782fa9873aca6641b9fe2069adf3cad7a5deab9d4c362d5616a57330dd592950545" + "251c" + "fffbccb8c40a5f8305f313d7a9e828922d3f7eea3099fbad4138f47358009a4501c7121bf9f2c1b115f5358fce37f76470c9e19c5dec" + "b971" + "ff31235f7750a240c85df33b9117bed8ef9adce5bae828ac29f717c4742fad20e5c9da7d9dfbfb92ee44c1371fe4cbdc2c3c808ef7f6" + "cec2" + "32931fe56d84f82c93d6a48a8194c88557d01d06e4b30ef73e37be083edf073455c57a75887f6498ab3123d8b819869f3c93a33744e2" + "f7b7" + "f2db3d0d92420d69e3fad3199fd2e0509fcc8b1863ab9235726b2ba69ddd6d18cd75ee17ed719c6e904bdccbed17e3c4efea509c5b5e" + "c59f" + "56ef8fc97f9ed2cc8f1f4e254fcbf19d040d06eca584f5930ab5bab37fdbba7726d4a010c621672f0a75736fc65ba8921f73177f79e8" + "eb40" + "fa31c2dc5b1ac40f95eabf5f7abf65c77ee59960c2c6cc975bb420751bc9b3a7ff00358effdf8782aa57afeefb1f9b227e0a4809633d" + "c37e" + "a9ac7effd0868d3f51ba5f56016c3fd31a8284ed220bf6c6d02dd8308a70dbbc65cdc24334043cbe612252ea8427cd0394275d11cd3e" + "d369" + "32a4271173c8fe029a9b80c7751995dd4585456b95425cdf29e8f6055356a1790bfb5b6ac88c7d82b7d78fb266dfaebc8bb507ee0820" + "abd1" + "1900da3b20ceb32d553a21e6bb6ee8a17d2d0dd2ad5f288ac681ab45620352346a06f32cb17bc3c8fab4928ae438f2455dd9b9066d9b" + "290a" + "da6f7ae5fea9ce10b619ff9e1e6dc1cb021002fb0192cf6d731855c14e88402dde4c20584c7efaa53c06bbc97f9dda127b36d8a9fa89" + "19e5" + "583ba82062abce95834727badf7ca27202e8ff92630153f7b74586ac1bc09e805b73d2d3387dd69952ce5bdf1278761aa1ac5cf68a67" + "9f6e" + "a453e7bea383c10f8522d42724794c6840197d92084fcf489219df3d8a94db5901278f74bb0f02839a682ea3f15e831783a42a4fb090" + "e1b2" + "63d8cf80ae3c8fcd26d4679ee66e5e7165001900c3994b8278f733dba27672b5105c84cdc38ca8724f327dd8edc87d3aecd9532f2137" + "fb8a" + "1a34b44a459f3a93c32ab76b1c2dc5641c66a68725e2fac5952254adb7188295e1ee58e8cc429fb9b2f128ef87a241c36790d92c21f2" + "0500" + "c06a95ac9071b4b817a75d5a23226fd267116d43cafa476f867e92ad6ab623d7e39c3cad1b0dbb37e3817995340c499111ce07976232" + "6720" + "89647d026c9e037a483e0da6d32829328eb771d9c34f63d55b8c79f0e21d0111fa1e398d9dec507993ad3de2cb4707af223793d33f9b" + "a326" + "f53fb3031e48acad90d8a38293910c63c2ccb5bf76c691d45bed94bb039d3b01187a7f34dc7c32dc264d902d2cf65cd14c477acdfd29" + "5dbd" + "796ceaccfb2999e8526bf91913c0663c4c13cb459c650b7977af50b605049f67315e40c96a592d872d769926acb4bae01b837ccc0857" + "7898" + "a095c57515680625457cbdb268a866f3d5671575ff90e56b7c7da15604f6cf6126c50d921323482aa899f8eef53144057e8004478004" + "4745" + "000578b52d400040066bf50a2d00010a2d0003145196440dc85291f4439577801801fdf27200000101080a7e069e563740bf2b91bd99" + "b9db" + "f617b69b6fc454a86bc9b0f467796769a4af16f1d44dcea11f99ccc51d14c814398f39992fff7f83f06adf882fb530c8d2fdfa3f97f0" + "15c6" + "0cb6078cfeff4589c9a5b6dae80c5331147aa99e0628d3c3bdfe2a9b5beb8037cf8b785f2ef27ea8cef333af8b32d014c0721cf9b050" + "9113" + "94820e9622627399115f429b9ba489124d49c6ea9a6d5a815a80ec9a3c147bf6becf471f5da05c4989efbecf39d0176f0a9dcde5e1bd" + "4751" + "90bf28d8d52ee6be08e9ce1e95414cb06d4a56626559f5dbdad6804c6ce00310f13b44e3452824a39c967ee1ff2527c3028505e16fdf" + "4887" + "6a65e8d21cd007e70f2bbe7922b708b6bde5ce137a099cea6d40c45659ff461e5c2b95691884d595a1cdde8ba75a5ede653f5a865317" + "ef34" + "c828b67ad19ea6bd102065c99768308246b6ec31fbac60f5f5329b91325e69193b00204433b84f24fc8dfa44bdd35375a2bf895a119d" + "ac03" + "7df8487b6068d60dfea0e374dbc137e8a6b1c8e8390b8eaf8c30d2724d6da6ce23aaa888b5c930e525ef80c3c0a1865aaa010b0e4af0" + "9f44" + "5f38fcfe78bdc0896720a23cde120c6d7479be7c0134766ebd46054ecd6be44f160811b5b3cf243dc5a9639f6f2686f311c602bdb106" + "1229" + "afbd26d185149b9c23ef13d8c15f93fe80ad84c76e6c0dc5442069bb3cbda516d93df26392d1fe11c5c0d0d85c614eedba5c87b79bc8" + "7ef1" + "c1f4a4c4ec5f04fc5c320b1790763323843f682731b8c9456e998075bf0fda6bab3f076918f7d4a2a8b52d08354222044781071e85e9" + "d9fd" + "c06c949eafc66dd730c6acddee834b8227115043a59b022fc1baf275895e4ccb3d73933f32fbadd7f87fda314f5ab7a9c9ebef7a8837" + "e53a" + "29833d1f581f1fad5a7b4df669cbeb9f975bda108b47f303982e4f47abe32abbe7cde1f27e19ed2283fce8d676f3d13c77cefa617654" + "c7b7" + "03f010f3a6f9af1e4362c097d642ba1c00ccf4057db7994e656fff20d9f689bb99415dbec1853c0c134822dfd691c78ade69cd9dc4c1" + "2734" + "4b717a1eaceb33e3667598464bc4031ddcd2340ea4be0d0ec1c4a25f4e2c336660cc97b2b7b7379991487f9cc032a86bca3cc5f4b5e4" + "09c8" + "3de197680e81dfac511dff3a838c489ef27eaa17b3212e77893c71e817b630f0aa46cded93d9bc6983ecd521d066bf8c1d95d9154c6c" + "6aef" + "f82364be2d0cd1323e41da7c4183e296e7d6d1d0cae47ccf590b181e6d1110e80ddff48fdfde75408808e9b1aab0613f904d7b2900d2" + "4baf" + "e6f868552f209890cb41e05405e4963e7e1e2421186de68c3282d70f60d8624dd235656dea407ffebe78d7c1920291a6f3d0028583dc" + "019c" + "05cced92ab04c3eec22491545cae90911cbb27cac3b976a90c7aebf8279e858d025d87d381af1ea1cfd6cbf440b9d7a415e50135f4b2" + "637a" + "a255cfd5f774dd47594225ecabaa367cc243c12a8dbecb075e13baaa2434dc7a4eb300698c88b1b4df751bf9cc2d0ca7966c6d6b916d" + "4b2d" + "25637f24e33ea2cded4b26b1b7217ea4e8af489527a44073f39a7cb3e4469ca78d376f41aea63404df307900e4a7db139cdb2e121c30" + "3adc" + "870205c70ae12b516536ac3be02ec395af7e81d224562b349cb00447ea98b946704b0ad83d6f5da42c5934555e129567bc3a850aaba3" + "5a27" + "226c36068060b186cc955247e87e730c4c990e95a91b3a337919276ba5b1c56489ce781808c32b7e7cf9372df51297eb48ee6ec50a38" + "757d" + "d72905cd5c70489e3b810ba0f7aee833d5ac31fc297d89e4ec49343eb473ba830cab6538cd805c46483be6d51f49ae3e9ae15bf74b91" + "5960" + "5a21d411d55e0ef8da3badf2f75654aeb959d0064e6fa3b11a49aaee055a82c205caa8385e170de87fcb139ed745660ac14a8a5e12d1" + "3d44" + "057e80044880044845000578b52e400040066bf40a2d00010a2d0003145196440dc857d5f4439577801001fd3bff00000101080a7e06" + "9e56" + "3740bf2bc15381330515055671653bfd56093f4e59f9b3b7708ea18c08dbd588ed5f18fff368955eef5d87fa5741c6b6f1dae0325ed9" + "7a29" + "183f22484dd9e6d30c1110cbe354146e35b0577b23a505bf533355be5c0c07453b63b73578fe3d40e585e7632b8d24adc56756cd2a7f" + "6cf7" + "e4c77d4f82cc3278d12db44f7414f9dfdff05391bed41c0dbca4ed86642d98d34809e5eb3963660ee1c4871f8206bb6411b00f4898e2" + "9731" + "45acb1d689d352aa23b1c21cce7081389942c895ff6f0a086b7a829d8fa57b721aa7b6efe4d0c348d70be2e6e1d8416ce322bd5750d6" + "b681" + "712829327ebcf0a9acef63844de8d4a7e92c0d908ad2b100c115ebfd91d468eb056876123dd04378678a401f72965629273194fba3db" + "8f7b" + "505165da35b997a4eafbffa0f816e15d1492aeb1ae794dfed767197447a75a524d69d96046d9c45bf90eef60adb73a0cd0c411900638" + "f217" + "00e65e30f3d0e922bbf13624d5d859a9a5f4252595fbbea7ae57df29628107c7ae58055e6f277922f7cbf957ed3123f0eeabbf024314" + "8a13" + "36cb5a8786e1fcda9fba3128e7e35eedb785e69556921c44960f3c251977003579c8aa4b205e219972024b4f47f6b758dee2611fe350" + "f838" + "e55842bfc2e10eb3ad7897422a783480c51802d88d50611797690a79d9c35c6ec109416ed9c5d9a92b539be92deb202a3fdfd770caf3" + "96e4" + "811b1121f9490c01eb60112a43dd132c80250b79f20f5a8daae65e1d40fcf90f2f5163423e8a71b3df6c9b21d68c83cc967cec96baaa" + "36ae" + "8000347553bae8b1b098f729bf21377f571c57d5800656f17495c05793a8567298f18bca90310d5a242a354837c84f8dabd227fc9566" + "b787" + "0ef08babc56bed0c67f55455286bef55cf35a6e3998fe6fcc97440751c82ae26c0bc706fab793296147067a01b009ea23d2646abb2ce" + "b3d5" + "7245cb46d304162b13e5e95c0586d1a90242e3626a5979b61433149d8bc42869a27f16ca7dcc92fcd0240eb76b997f45631ddc2319b0" + "5e59" + "f5bd4b15783967c4cdcf9073eb3c3c9b17fa7d27e74c61e8fd98dbc9f92d8621f2a0e54b977ebeb3d0a2de3fb77241d20fb4f0c2d137" + "2542" + "bfe003486cdcdd45e0327024bbee064b1fcbbfcbe1f6f3c483e77d4144128edcc8b28385e70242e7d8c386866abc5a7f0f161a0feb60" + "672c" + "c93f029ff619a33c84fadc3c1832a15e9f3c470f9d32c531c5d34d19839a01e413758deeabc243e31892fd3f62b0a3ca11acd9308eed" + "3832" + "3e464eecd2b1a6326b7d799a9c6a3aadfa2d74fe3a359fae773574c0937d19242cc44ff0fde358a69690e9dec48261ae3b14b0127737" + "f3bf" + "f707b90881e10864b81d98732833f7b4bdb9f42a1d933d3a10247c7e22021298a48731ac02f065545e6eee14e70ee3b809d84be48a98" + "6f75" + "7f229d82e3a841fcc1d306db1f2239d7c59e99c6dbb429f93ad6985df130599f3358890e3fa480566d89cea6a8a27fd17fbd5b03cc97" + "026f" + "59ce132a5fac9aeb67c352e0e8fbe85da4994af2d8c79ba49ccb6704231459d2ee3833db5d072466c37d0ee96bcc70d843ace90967b2" + "04d3" + "c534ab04e2f54a3d48f0f1a3161e8ae31875811d51ce9eb8208762e9438b697acf52ed5e4ed463e310d2108af5f0833f57bfa85e9e46" + "ebc7" + "0cf164040d5f619b9f4fb4d8079852a79a42144c1d96c25546fe6ccdb115cf5005dedfcf72f85db0454b3786d95dc4b6f02a58b5db6c" + "3578" + "3736562baae5412b0bf45af3dabeab68f208922312afdce0d92e4a59e83b5d9ac077dc065c2312a4ed9a5f499f50a9647186eab8da1e" + "fab6" + "74e0a848d7519c6d3d65f572a8e4840ca4cc0f5b471a5d1fa9f20674fbe95b93d8aa54109fd8a3a5b57b9feaa037f18e05a241420ce8" + "deeb" + "1ca08df2e082cbab44057e80044980044945000578b52f400040066bf30a2d00010a2d0003145196440dc85d19f4439577801801fdde" + "6700" + "000101080a7e069e563740bf2b280739aa638e0744a7402ad2fa31ac1bfc444c29030f691403c01f2e0a1100abde7a1f9653c815f5d6" + "1465" + "9dc171eaf3f187b04b324bc71b50d1d3f5a6d82ba1b237ce56ffec743aa30d5edf52d38fbbc50d9d2a930d69fe8e6ef473659b548d67" + "080b" + "cc83e5a3759212f2bb414180216744ce0b71d59f29184c9f2ac63accce02e6205e1fe0be020ab25cabed6cb72104f6f6ab0f19fe7ca6" + "7402" + "1c44a7db3f4a95436d234510d789c89bb4cb2e25fef301b079bed1b1fab31df7a45d85bcbb17ddff956d06d568600ae6332111e0db30" + "8403" + "ec426e8d75c24c4bfb970be95e41c01d4574dee0db362a52b8a65cfc9c42a9acb77f280d416db4a814f643d156f7e92a9adda009940c" + "c597" + "3d83c9fec86a8d8323978cc07d60e5ec268dd3505306491ee9320e16c20564251d5045252a212e1bcdcf05523d9138345aa17828e409" + "5686" + "8dd54d2643b5d8ecacb32fbf4b6ac91b2aebc329bfb40ceb68ebcb9e5b5063a6a8700bcd534eba7c3a9e58fe04d45e9cea6b61338f80" + "4f97" + "4bfd4a3a23c8c74da9b5994b09a29fa0d59c563db393cf5b586649b967392bdd8031fe2b80fd82ad315981c8b7663337b50cc59d02ec" + "3ca5" + "34738d82b34d06f4401a41816543d4312e4f87f0cb6c9f2f74e5ae30d56176174f0cd8b8072405d7e223a1d2c304150b230041dc101a" + "9c8b" + "8e5c2ee848bccafad8c42e6c58182a0a6386ac642cf9a772f996f0388a8d5c4391b5845b2a2e5573bb290b382990ad5e42769c240c47" + "31a9" + "a0371bf0d3cae364b11c504f59c56247ca064972c90e103862aee860d79dc7ad4201a04657760ad4ba0adf5179814188d734c724013c" + "e3a6" + "c9129b11a7d2b967a8c0f96414f694516bb67ce39045b45152cb1e21ea5fb99cd11609eefd742aeb38f6131477784119f7f837b490f4" + "057c" + "048e2242c5b07f88766a3d636212a78d80b5cb134c91d5a5dd3f2235cfeeb4f4f55b75999b70bbe04b4cf1efd94c3bc185fe09e38f65" + "80aa" + "8e644ec2a34c26c7aaef2d8659b17d08aa294100d95fbdb9c59e372b012fff1d36d55fe8b0afa21ea54a59c83d8822f9faa1a5532e3b" + "eccd" + "7cab2dee44916ec4aa0133001da6f7fb39e9ce920615ec5255b9d776094026ae76f1191734ae849a9e36898a3b99dd775d793d4037c8" + "9f49" + "87e4cf39b5b0d81a5a4a5bd2f14d5becd602696ea1b9ffff0814677a25d78e664c5c05c3079c9932bf828f318aca1e00c415d18020c5" + "f8a9" + "a0efdc51b98548d054df662f7caac044ba37f3c6fe55bce99147e4aadc6f3c3826b7b0dff349da90f6a11de6ddb540122477a673d2cd" + "9c60" + "24cce8d8490008b9f3f657ceac83c179f878d25d34dbf26fc427c523249da542327c7f46a97213a5ea65172d43f34dfc517416ef9756" + "fbd2" + "1660a28f256036d86834d5a24904c584991e42db81266c4012622a36ad6acb60796675d85a9369ce648d397084fa1fa54271b5a6e068" + "c208" + "3073cb5054fea4bbb231b48928bdbba51bcefc55ef720bd5b35735cfa48eacefc5fbd96d3fe66d90754b26b006faea606912cd88d8f5" + "3153" + "02a3bd82cf0dd4b5e35e9764681360b55f4d066f0953be3c8bb03792e01bec7d06ab20a074d296bb1ab025d217cffa2227666d1c51ac" + "021b" + "5a72683f7f784d026216754fad6e8c3597bf922d9673bb41163e828a20f1815096039b39664e45a911eedc8361832f622c4302582518" + "b467" + "4aa67e15162f6a0d298b09c6c282ceadf0a1bb969cb17d25943702cc142d4c80be72c5b9c0aa770119f4a8f4f87e319f3d93b2244039" + "2fe4" + "979787c02d65e690776a0dbda4c659edddbd4dff18503ffaa23552831c576493bc799d6165e7d9d1665a6e70e0f4bfce7412185b3fa3" + "a89f" + "30952f32c9ca4322d4e2a4871f01412ab844057e80044a80044a45000578b530400040066bf20a2d00010a2d0003145196440dc8625d" + "f443" + "9577801001fd252900000101080a7e069e563740bf2b3e3cccb1808c5cef03da8a8f678979754acd488c6512f0dcbf20585dac32534d" + "23b1" + "3dbf27538768e9305ef8d32d883c4ce010237d8c401e8ea5923f62668b697c575956ea9448cc9b83a4dbd4b72a2f6b6593a3a7d70060" + "2c5e" + "76dc6b7c4fa992c714ba87a8fe9a5bf53e72369055d34093bae7e6136d13f293c438d5f62d1c19686a4cc4919a851db604a2c9f5407d" + "2937" + "e4cc97d52b5f69cb6edd63358d02b1b15b12849978a9c50da54618fb617c9d706f85957261d69b9ae841c3797b73808abc48b4f46a34" + "c038" + "5436d13253552668bab657eff81ee8482094d87b4cfcfd09c4894d5d77ae6ce6d05f26b48fb271e1735a2e1061b74e63f1868758fd62" + "a855" + "111cfd10766d7eacd128a18c086178e9451822b88577ab1e68dc32ecb60e436c8dda5d97e582c3a5f54d4a77e19a5307f3a6fba11c80" + "34c6" + "ce74c26dd299239db9e85882eb9c62d0a987d79bf66bebf994d91a6d74e145bc15e2e2748a13fa2ef733405f19ec5683f06154830518" + "5765" + "f4aefa6db6fce0a3059f2babdafe50c483a6afd3bd1beac674fc91b6db29a9ce06c19032bdfd571d699d1813c82e5ffd84273e7e7978" + "cebf" + "48639aa4ed12088cbfbe70543efb1e7210749ab8cd512dafda711c5b18da005ff34958afd8e1dc6b73ac8fb3ece79019a5d714d1dccc" + "8ace" + "714816ef321f4c91a059abdc33e051ed46354825a09815076673cdd41c27f49c43a2fad399a8ea83a71bba73491207ade9da3cf228e9" + "f397" + "1d7e463856a01313a1543992f756a669654bfbd8b31195572a9750164076e73c13993cac789c2f827432d0ea6791ac4d68ff399c8673" + "4e76" + "cc316dcb259b7982152ab5282ff0870fc77f61d4d0a9f82202dd17e35616c3bada28d8b97bdb56645bbd0e05d0bc71b860a4041c6f72" + "db71" + "c26e8bb8b601fff8d971d0428d14c5fdceca6bacc2ffadc1fc9bd3aa6c635399c515d948fcfdce219ae1c49fc797b45f69c002d8685e" + "4e7f" + "09f5b823919e0ee826b83af14cd62a2aeb2631a6162c2bc70da672adbecf8ef30bfcfdaf81741aa7c68bc3137b37b249288fe2b3dc27" + "cacf" + "576de4fb530e05cf7be976eadb5c5a25f10aa121aec317a0c2e873c62cdce76d143c0f4c575c5dae91d319732688fe9267aef83bdfac" + "f055" + "d353c0fec24d5f7670eb5832c8833988384cbca4584b7db7d6b9156e89b1b42d2f90fa2b25fbedafc895b08b5d318277a9e0c8024207" + "5513" + "662b765d41312c898a3d0325e922a79b4fa1b403f5e10b0446d8ea72c6411f966da69cf495c8b79d6da8241417c735fed0f50ca41fb6" + "ae1e" + "a4d19ea477955ca3f259df20c525997d238ec8d6b9a1edde0de7e050664ca6f60bf4894d85a80e06c416dd1b16f8bb068581d97494e1" + "d25f" + "ef473b061c1fe3596cb4da02640e1f873abb3d132259e59d32e014b77a6b2a2633b964d4edfd89e5669ed1338abf1e65a3a93abbec0e" + "e441" + "142e99310534af3d7430a01417d078e9e619df5cba25f94c6f2b1c885a34c738dcdbb2d9af5967e4bf1f88f85cf6985c580d6f714eef" + "f2eb" + "99c27d6878bbdf086593ddb1597da886aa34c2b934aeeab8745f1e89182e4889807e5ae0fc4fcc84901d4404ba0ca7667e0553f7c0e8" + "e792" + "b6ba794e55778be94d69503144530c0023a7bf5e2a38ed0101b73515c5b5840c0e57c12b9a08add4d4707635c80c4796e52af51f3041" + "1775" + "ce1a1a2ce543f5e1111f9ed48fa6a43796573bfc1cc5a392cbb51fc5911a39f282c91e677bf7ab82d587e92b7c6ef048ee2f9ca1a0f9" + "5133" + "e540baa131bf5834fb5731353baa9e7317792cb2726fd0e58d96500618d909a4fe5f12e8a240b25710fe23332b6780000126c0f3c5bf" + "065c" + "ca030ba7c29c5695a34136a31f430cba3fd5ad7ad2c63d9c245d44057e80044b80044b45000578b531400040066bf10a2d00010a2d00" + "0314" + "5196440dc867a1f4439577801801fdac3a00000101080a7e069e563740bf2b76ac9734d1c3b4f1f88e7af0afb31110f2fb31da39ab1f" + "5f8b" + "470e6b48b736fc6ae6ac85a72bd023d4044dfb76e696809400e76971903d13ed52f0107ee8a7746edf0d86c7436ff00d1287759d5be6" + "4f3d" + "86dfdef452d810e17a60749f02db6676b8b7d1ba06f884c6094654dbe9422a4887e6c9215622637d05c2037dac929f47d05d479756aa" + "8384" + "2c208625671070ab70b518710038e0ec1a136bbbdd536972b462562bae2bd6bddb43a135da51f76ecb699a3b26ce890bea0ea16a1611" + "9973" + "72dd399a2a00ae3130c29d9ed88baf242ba53554b8e033a72b570c82cb1c54f02afd35924e9d0e1d898cb3fe2964064b479d5b592ed0" + "f60e" + "c93c70a0ee4b4a5e49a32c5a12eb78f13dc3c7be319d5da1149fd278107e4f963cf978704392a7ff70209db75a0a2e770ffc27500ba7" + "9a99" + "dab53b81234bb28fad6901bd24f245c7b1eddb4df03dc6fee69dcb766a2a9232eb8c27d1b00a6ffc8d1d022192a887cfc91e084d3431" + "874c" + "4b6a09b0cc76dd609309f5082ba2c526be65c42842dfbfcc90ea89000c440b2fa1d277d606fbbd712b5b716a3d78b278eed6f0e3cb27" + "4c61" + "3704085085f3aca5af2e82eafcdded1000e75193554421286fc3ad9bdefeb005cfed5ef7811651003fed0c4c06b594b9134aa0aa287e" + "59fd" + "2d5a81cd6ff74e453782bae25cc4c8606954510081f2fdeadb67194c55f40041915249c65b5fa92bb96789ade0821a60102046b949b9" + "6eb7" + "adf876b0df7f4a51146ea79e51c621acda6586116095a8c8eea98c6bab5fc8985a5c84162139ddbc378753d9617f663235a0fd762cf0" + "3ecf" + "1ac3ef3201520cd12ac0f5233693eff7d8c448760ce85420d38f18bad7b37c5d899cf47674f6981f01b12d534c9a7c1c6ef63584a673" + "6045" + "538b4d69713c6851e00b3614269e672815dce462d67f1bce0a3abaa926722c01c011ce5e0a8737877dcbaaaabca2ee1a37680bf260b3" + "08bb" + "bb90c729e646a271462a9c1b02e002238d15f8762ff8683ec7315aa6d6651ee8d00ad8561fd8e32dd97536b33b275c44662d050be81d" + "814d" + "08050075484be95456ff19df9b0b38bfe45f72e23275a954cb066df3a063e7929e22bb824bfeaaeaf69f94ee82284703fe451e1bcfdf" + "789b" + "3b06225db816df06869ca0df026ede41739aacbaef8c3d039f293ecad2cb78b8775ebcec62c24b42eca4f7df537601a275d0f575be06" + "748e" + "52e2cbdea20a76d538a4c7a92fc3958c0beabfd972da3ad949ae6a038e8589f6cbb3ed4112adb9269d9ce6fe230d99b6ccc00d42877f" + "51b0" + "660d84df11804ddcd8a11068bc2d6200d32c72637dd0c332e4ad8fe3ca33f3a5272310acf345e5828244c577df6441a89407fcd62c69" + "eb3c" + "5dddd3108f29979ca9f3d72a4c30a73dcedd241cdfa1b2b0a7900ff70e2c870809ad6ae7085a455a1fa4d22a7c625500880c14eaea79" + "1775" + "7747e23775b4a594c536e25e47911fe215592918005ef74aecf98938ab67e3b3838f4324ef38ce43748062f93a74815bf4e2f2f50479" + "e0b6" + "16afb91d0bcb57b3330c6e8df718178a4272e651eabd8cc620187889cac80466c9d6b2af7bfa9d1b045a9807bcd8413e282349637c21" + "4b84" + "cec1a6aa31ee0a5a287cdd1ce0ae5ca3242efa3bc85a479c53ba132078e5157d008af6d6bc5c3266ddd6a7f2fdee30f63e8f939862e6" + "516f" + "373a06ab8bcc5e3ff7fcb04fd00c1476775c52196f4f9733a2eb8f201d9f1e1c5b85a735a0596352d2643c70b1019a5603a33ba7feab" + "8532" + "2971c96e54fdba1ecc139eac082ca8e390763b26ebe421a230efcfeed2bebf2cf5535793dd947c2e4a674f835b76c6e546581b664f3d" + "614b" + "9ce14ad2fa303d92b1a2f555e4564129aa29021aae4e7b3fccef2aea64ed7391c5a4bf44057e80044c80044c45000578b53240004006" + "6bf0" + "0a2d00010a2d0003145196440dc86ce5f4439577801001fdab7100000101080a7e069e563740bf2baef91b710cac6f6b8e915a1c2acf" + "5a0d" + "7e1d74929f0b08a61b47e3b5f81d4486bc29d333e747bf10a85922422cbb3738eec3bcbc92cb13a3fbdb54ad81487d6e27600600317d" + "09f2" + "3ba1e97acde6956d2bf67a335d05edb243ae541ee9195df66370b01783dbb8d936be7a53761c11679523d9f52704add361de53051b60" + "ae5f" + "2dc21740c5545e35825e9efe9a1a9b2441188fef2870c781f4f11b7d5435ca4012d57e781d2a84eb796fcf73c5299342e35439ae7d98" + "1442" + "a9fdbdccc4f10822f48f02ca88abdc326b774277fbf932dd79319e90a66a058999057ed90b5318898ecb9260bf3ad221ff3b5f441bda" + "b3b2" + "cc67cbcb8b4fd3888e3918781f8cb3c41d54fb6f7b1140a3d73563b77b51c51c7d46d4a80aca45aa6784a357c1edc60e156c4b7a5f11" + "d6ae" + "e49d66fe6c90692226531ed0a8a2fc572744a2a8888e9ce02b540397b8bbc6cc7f0e71171e11e94fe1b84dbe8494bbd47dc19efae019" + "aaf1" + "35eb74ef56c5e641535e2f93537dfcfa8f694718284c353ef186ef26f1941f7b7720aeaca2f354ff899d6edd248ef8abd046e2d42ee2" + "575c" + "bf62c32dd66cce29b168e6ab0347ac3cbc20c159697edaaba86c8afd9b284b37a3f6baa406414fa8bdd3fd8a0a13c0e58ffe8331e06f" + "2ac7" + "eafadd516ee84919d4f5fcfe9d26883cc9081d31332ee31ac84f3111f9a95eeb2481645389e1c57413add83ab7c9931f46590820a939" + "0275" + "bff79b3145c8e2658f183b578faf99ae588828dd7a930dca7ab4c2761dd62f63c06961b40019864dc2bbf16bec673a0b8497caa195ff" + "3c75" + "079e510e0ba56236ab3b54530430db0471ba4381e16d8fea3e288559be69b44d070e8318c78f766920ddf047fa5d4ad04d17534ddea0" + "129f" + "43466ff5591c6fb205d7d66213226b69aab3e6acd3d5e9dbcf479288c93f4a804af76cccf852883e42eafcf6ab268876ac1b00238486" + "992d" + "fc042798fb27acacb0fcf7d016c36a9f0a1021bda71df49f47245a67f2ceb7804dda0838bf48f7f60a2812d8c871b0181416eb7bf61e" + "ce80" + "7365341e36800f8d5a05c24442dd87a8941c5da73260c3fb5d7010be209701230665172fc6a9bf0da67fd8025835079bc6376f01b6df" + "8010" + "2d1cffeb188a1271ddbc52dcc859e3df77face07ff95e61e437328dc6055c055fdd9143b0194a03df8bb67dcf7852b1ea2403fbfb2fe" + "8a99" + "3a572f4eb385c33f90f3d9d1d78d7db906f25dfca57d1b00c4fa49848516ef75d8052f2d9b402250abef1002da37b45d32e5b3f14906" + "a9a0" + "e0c112eb009dd7201ec1a715be573f8dd542badf7dbba74255d984de2b422a2d7153f7eb8bb28a79523a3b5e022f0d21f283e1353599" + "3a23" + "fd5ed87d109f4eadaa2b2705d3ba14aee14cee99f0dca853fb28952e4befdc6c2a8ab45eb046ae036e06e93703c6a2efc4712958d983" + "8c2a" + "959b88804d098d4b0b156c368e5618ab120f4a96928904fa292c03f7fcd7ecd762864a41305dbaa0b0468686984d49d6528323f9dafd" + "0d41" + "06c80af2b090f1a77c20b8fa9b3f3f217214073a4948059448e6356e4386b37705346f7977873d28859166204adbe56ea9e2580e55ae" + "61e5" + "cb0881c1612b5f275e96755d6382f3a36be041b7f56827938ec5e06c92947a912904074c41e8eb820d66546dca3665e431579f335d85" + "0c2f" + "cb73c6517357cbf1cb30a26ca044f3e6646bbae5aebc0809996611f846d9ce248759190eebe68fed2e113ac70da7188f4b8135057039" + "954c" + "2cfab38d52bd90401fcc93201427c00d1674aff3e61efad08c5a4a5d0f108a7fd76d61e5731c3ee68330f6e4f6b6ad9886b801f7521f" + "bbcd" + "f2f2edf73882163011c52db59203b6c10dbff5f0f4292a57dbee22df7477b73931db8fce04da8fe50ee9e36244057e80044d80044d45" + "0005" + "78b533400040066bef0a2d00010a2d0003145196440dc87229f4439577801801fd22f400000101080a7e069e563740bf2b8b57faeddb" + "daf7" + "27c92ab4ef51ad465d9581f19789fb649a2bfbc99dcbd4a07e2f0a1e91a45258ce80ba0a3787af5ce2dced44939e9ce6f56860b3e69d" + "a4ed" + "818e283469ee03878b248acdf4380aeba8d1aa09c4288af8c5b252d7299d17f415cfe8ddb0ccb7b9565633c32573a823eb6bb623c6f6" + "78d2" + "5a5d8555ea4eb916ca67f3ea236a9bbf69be53fa88aee6ade14d809fb489a908cdf7f48cbb190f6ba83937d3cc747d97256da91c849e" + "739f" + "64e2a01f692632fc2860317fa76a08dae7eb30f63da8f25e759f364cb4e49863cb7bbe13323581ac02ae09ced96ec1ed384b1786e1f9" + "218a" + "01fb44a1900c627d9e56ac88f2b9e53e3d741b8057a0535ab8b59b80d8c0a9034d641712ce49fb2de7fbaca36ceb12d00c243b26607a" + "92f4" + "0c26034bbeffa3c1edfda9f4840a6ba83615577f084763adcae3a420d8570b521bfb04323c9a9d06ecc98b117e9e5903b927ad9fd560" + "8e26" + "180ac7120bb63131ada7b260be8f2b099eb6d158debfb3117ea452c17c7089324b1f6828c5f0c7652fe3d557119e98fb22afa36e20fb" + "b570" + "60fefa623db0fd26b5a6c3d34e2a449f5ee74f6a9f49d9174ea22c385fb1f25e2eafc3a9460210f1fd0447289ab89712545b4e6f3228" + "31b6" + "0e9e14e359c3bb7c81c446faf8f15a7ff9a984aad44724357b545ef8514e4e437e0d4bdfc43688701166dfa9cb0928407621fa8ddfa2" + "43a1" + "4f0486c00e449a7ce8e8cb98a41d748727a0bb0360156863b39b8d01c24e75afa6aac016b281e50a62d294592d15b94f070009488751" + "e9bc" + "11f09a70b1ce11ca1206a6600ce252f0dc19bf0a2fe0a20ef7dda172804b3b565c88bf889bb30b591e0006e2b9df2dba537ed9411e51" + "ee11" + "d6e3bedcf2afc4799e72689996ab0b1ecb6b080a97014ccac6df014691380eda49e30567eb7192fb99f6b0b89a2da57dbefdefcd4e13" + "ca68" + "4922faaaf21a2e7b719377699a89d5a7c65da9f21a49ede30dfab7305bd872eb74190c69766e8ec301e42bd909688fb52da5b75340b0" + "7277" + "72e2be0c075a3dd8ad520b6dcfc20d1510a483bd11f912893626e599232ae9f5143faed2b78addf09e490e06d2d1e5be82c11b7104b8" + "9030" + "40e2ecd96e9cd67eb882fb19b22d1cab3c585e27e9b150af90ed5a90fca2531101ea8222f1cf8dc2fafc54f966b27a83940a3366f5d5" + "0543" + "b6ba78f9fbe5c08893a9f7dfcee7610199c195a5386d0faa453a4f56a82bb76678e4f4d1be421dbd2c72129175022b9b72de942b23d7" + "4866" + "5d32a5593df35d137f1558cae59e185120f9860c30616ad1696103676773adcee9c6a618b30fe3d674fe436a5d9eeb41dd6c725c589a" + "6161" + "45c87412f0cc6b07b1f68ccd591bc4bcdd8cc2cd26d1b6e1db9358c1c765a75955470b0347d8553c111a21d1e44032feef3000dd936e" + "ee91" + "27a0997c9547df2e9f1fcac6a71e39b632945ab425471f3712eb010858842e89ec268efcfe8a8094ca5b6c138136f568fa3520300014" + "2c0e" + "a22c210e42d07677b955781330a3ca67380071ab25059b112dfbbdf75b61050782a98f7b0c3db8b485d5d63bfdcc90694b12e6771b05" + "9bf7" + "53c0fa7f6d7c4b4582b73e6b26a43ad399f4f07139a203156ab90825c8129788801d2539b237401345a06c9d8ff8c8c2135e20b6f5b1" + "40d7" + "5a9fa46628daabaa330d23072f2625e0f6d939c1672fbebd818f0f151be2b22eef7f32bcf122e722184a23d58b790adb810bede3a879" + "81ff" + "951ca2c5a8cc95cde85e088b58958d3d4e24806cd46f88661fb3240f18af4005d5eb20bab5f83a8a6e98e59e4ea838f96c8e52fc9ab1" + "62a0" + "52c77e55afe7cba1d3d8cdd87b5d16087baac94601a97bb13a29ae66a73f808969a1d8051d7c26b6a38f8b8d5d57c075f1b6566fd744" + "057e" + "80044e80044e45000578b534400040066bee0a2d00010a2d0003145196440dc8776df4439577801001fd9fc700000101080a7e069e56" + "3740" + "bf2b4a2f1cf1787a5a3675635ac42fdef50f2788f73fda7ce6784c4e94114d497842307f92a80bac28466d0b0ff2ea0fb3fa2b394492" + "6fa0" + "c4f2157bdf1df94a610809778be3450d5bdd64e837d9af4216d54a75e1eb2439dbe1e9b2d4c327a6df3657fe88368b8d199c691cae94" + "4c9a" + "872a2d00c93b96ebaa26106c6149c1e461483340bd6851348f11db933cd483d810d3625270e28df344b1179d3cbb652715dea6c13526" + "7423" + "d543b37656672e60cccbadba3835b339902a9148b8f671117bfcb48f8059a5ed4ce28b29ee28bb0297684c197729808b032efe160d2b" + "9228" + "4f67945840aca0ccbce19063096cf8c78b6cbf8658f9f94652014eb625ab9f0619905ff2a510aa99be9bbb909ae7159b04e933daafe4" + "99ca" + "c144ef221343dc081ef1e140ecc77bae5a3179f5b519ad95ccf94a0edd0fb9c4d5bb66f6b59c6d7067b219e496d5b2d1196ce5b59d00" + "3cc9" + "ada070f4675b66995f9b2b346217517d0b98afac0fc3484c167a15a5b1d21db0c644b25ca3f254a62f55735f51cab10b7df698617703" + "bf99" + "95083886d233dd953d0c3e4fe79a0cd0c9f14cefaa9c6ee27ac86a8f67fab1b74096d269d264e3e281aede0d63eee96ce5d11e5761df" + "3ea2" + "01eb1e27c703c7988d22739c81dd2336202b8002181f04a38c223fc1a1426b47cbe399fb5af605672a94f486bff0f96fef3a18583c13" + "732b" + "9538e61403d17583cf3766b5acbc57d5b652d27c957794c6c9f9ab318ed7eab146b32522f6324305d03652f3cccae64587be8de2538d" + "aefc" + "c9ccd9cabfbe527478b8a84bae4316c7068b5b481ce17a42bd63ee7f15cff1da63fba07b4756b82055efc50a2e7040e3b45e05b05ad8" + "d7cf" + "4b23aa9650140ff996f1506e4697ca7fb0583fef1c1d9a9528b6bd508b6e67e9d1fe9c90a760f350b88ad6b813f30182666e16efa586" + "3923" + "a91446b971aa5d6cad839d4519658d246791e281adc8e563116937319e12902db5d576e68146ecc70860eed942035820fbcc79a0fff5" + "3dcb" + "9a95fffd2e46a2307d76388b3eaf15e561895d02eabc82695c23a661494a3cbc52eb5b029b42014e62b9d92550b330d1b3a20ad80d5f" + "978e" + "d99b2cdf8570eb461f6858669cc153c8fe586cb6b521b6422c481057d2920e50ec73533bb78e093e726a92d43f790585e74a9e3b6789" + "a0b4" + "b4fd36826e8beabe10428da1c051939851c616b69b88fdb10b0a9c161b6bb23c659bc0f989b39a751f687b4afb659ff9039f97784183" + "c81c" + "1c8828aa156151afd1105fb4b725a0129b275285412e5aba66fcf90c16328b8893916b792a84d38393e48fc966940042fc71ae9ba936" + "c4da" + "d86e21ac6a3b5c9f5ba2a284238f4ddcadefe857324bc95246979175cae548771a2a24dba37ed114353a1419e0adf4dd03c2a66c411d" + "6961" + "97e8c11246841d2ebc29e8cee271e70d338b4bb776fca997cd1894751b93f9a10bb16afc8d3e5fcfc841544926600d8030bb21d4aeed" + "3992" + "5b0aca784a35d0fbb3af9f8da4b468150e7811e88f6580a94f94ceb254a075039c7e25241b82379a1691d9913f5321bf89bcd8e4fb1a" + "5e01" + "efca4cd1e662559f849899fbd4386e9224f847594f424aaa5cd0b45e225b3df7ea73e3fa44afbf2c59ac5bfa0ca9a36a08d4460367c0" + "eb5c" + "974e562b47b51adba52b6fbefb3b01b56d865674d5d4e4e8281e27e21a0a851d64565076da964c5f3dcd0fe1bf1c8e755ad28aa1747b" + "6375" + "6e2f7ab0346c8ab5b12c0972da6d71bc4aea4996914bb748abda489567772949dcb047077fbffcdb1f23622f598efb3273de6ac98b29" + "d56e" + "23c2b1f7c9c312b4ffae862ac02f09d03451faa3a4f4325c60e1f9ebc0d370eeb76ced0631c09a782cfa7d207637ea49f915f1f72b4e" + "30f9" + "d87998398c4844057ec0044f80044f45000578b535400040066bed0a2d00010a2d0003145196440dc87cb1f4439577801801fd071b00" + "0001" + "01080a7e069e563740bf2b1c441aa7bdce792d8979eabc8613e58684bdfdda40384becadc48d56eb96040eed45d1d1a5dd1024ec07dd" + "33d3" + "8d97f998bee453a2f31737e1ec25ce6e3522616e1a569e1647895f38810f2b95e16ce6eaa3c183ace6dbe89407da75a3e6852bbf3fa4" + "07cb" + "aba500f3ddf3ddf271ef79f1c412c8af47f55f8f6678a914c33830fed7e6deb214315fbe119bc20bbaa1182ccd98826a3562bdf6e0a8" + "6716" + "156d420b82e71019cea5975e8e5d4c23a4a233195efdaed0ec04ca0aad766143c2cdb5a821eab5645e8d6fa96955c778ba2117c95431" + "3906" + "763aa5e8b13c9e48a3b65b0c917de2572b4e32ec6ddb8bd347dd9dbbd4883c28b5709461e83489804cb8f9c9e4a2525b7c189ad55672" + "c168" + "4eb5bdbfb179e5c42c3d4396f217a12606dcee9abb4db749983298f1a0c548523fb35420c4dae9291490dbe04e2bf336b21d3e54e6f0" + "8ef2" + "c2502952c1be2cf8d9abcf5777342ed016f9d5ac651908d7ba0821a28771af8be7b431d090096eba3970fe0d3ff6952b6a3034ce0033" + "5cbe" + "63b607a57eb6d22a03f65762f796004b228af1e100469dac7189a45f4ceaba12c737a6c2311612905a56d30d1b96efe8b7b58450330f" + "5d2a" + "6ec378d888dc5932108b5b294c451f292f6388d8fd86a237dc1b5886fce9e98309644af1f50d8999ecc7f2327895f5fa5315df4a5f82" + "eeed" + "a80f4e060acb1def8b5531ae88c11c0240ad3280b9f386cbfedfa0300a6dfc6d4d436de4fc050f39a183f8f9d168208054d4cc7a2b52" + "b7fc" + "0d0ccc95c0ea50f6a584dab1ff7a8fe48ae6bd8f976297646c8720b4c24d78c37e8b70ac2f121fd51c872b6cd4ec3c4040876cbc4b6b" + "0d52" + "b3d5d73bdd43aa6a6e64d84e14c66e3e86edd377f8d55127e9daa40e6e85c4caee8ccda06c2eacbefe750985e6f7acdcab9758fac01b" + "4166" + "4745e8e201032034e0b3824711d258be516d91c3179ed14c5947cbb636f19b056e21e3bd21a41070693fc5431ec3f03f1930ff4ddb1b" + "6a56" + "b5876099750b5e33660c88694b8525118b2dfe9dd922e6fe2c0e20c13f8b531f3aac8cf1772cc8b3746f7e43f5469eb5888185d266ce" + "02ba" + "d808e13246922987719a91a25e63dbd9d3f32691cc91c5d5bdbae0e38ba9f12058ff8e97a9d5fd919b070745b93e39d1c68947e90781" + "c151" + "141f179b76b43b6fc3c053df6cbdaaf090c673d68b9b2d6f89f9503332daa2ee24b09f86a612623217ad112d50e9932df0c606f3306c" + "6b23" + "0a476bbbdec1c721fafaf95670f7a0edeb72b89d6f1e6c87a106de10925a4812a4c1cb125bc34cf73e5d13b31424b90b7ae4390c09e3" + "ecfa" + "01815d1e79653b0edb955e9bc01836c47992bc65d6c1b9f2c4c114b22d0237aac167b49e5718ec56ef991c2e7863488c58fea2804ea1" + "bbfd" + "4a81be993751759d4c672edf3326014b7dbd326ef41be0c2da30f526b6c6e222b44a1f038d3df4f8754cc453fd6e05b2a8f5e4fb2d25" + "210e" + "1457cbd9c6169bcf3510797bc868c1048d5d8cd80ad0c078df697df4bf5bf039d8121bcf8f811965ea3e748b86f65450991abf13cec1" + "5843" + "54c721d1cbe606c604d8ad30287821c796a0510a79ce1c0d623b125af5d16889e72e4aa5f77e396238ccaffbf34d79729c2d2231374c" + "2783" + "2a38f74e9efca820212031ec5780d3c1d5be044f5fa329b3c3e7ec2794263d32942f04b086b63d50b7d763397718a43d330ff30ce063" + "797d" + "0522db980cbfd500b475e0b9e0637742408846b4f507e0d6084b5a8028202787c31dfc8af66530575ca0d5fe86fe026406248d76f2ee" + "f4e3" + "acf3ab6b897352fd0f6950319e7ba6f61edcb1cbd60d829e030befcab3bae493cd840cdfe0f3c5dfdbe48061470328096e717680802d" + "0b0c" + "58451b02bb87e54675c4e519c4d11444057e80045080045045000578b536400040066bec0a2d00010a2d0003145196440dc881f5f443" + "9577" + "801001fd70b500000101080a7e069e563740bf2bccc83d4a4c909762301a98871e0b5cef56d49a2e9334062161cec5eb02f20f842556" + "5692" + "69740f29192b1e7cd69534f0412df2ce692b28345bac8817b5da43a4e2bd8b27f84ca20a80240500c1a854a81414ed198fb13214e862" + "0b5a" + "1f394647afb820ef1b0681d71de7e510918a95cd4b18130de7fcb35d1ff7f3fbc0969af42e7060620184aa225c3516bf4958ee880556" + "4336" + "451852bb04d1ba8f891bf07fd342bab3eca57eba3047e4dd6d682a280dfa4446370e197a1ea6d300dbe46fa1cb5623646d901a37e8d8" + "cfa1" + "fef9be428af2aa4bdcef784fa9d979e47bdd4a6003fe5b7b8b454f49f8ae8459ec8cd58c5b253b404b533ab3b422122898369321be78" + "74aa" + "280fc2289afb6cbd273941b3ac80a8ab9f3cdf97ad96319c916f8f8d1538352f6d36ee66e62b8e443b0a28821176457ffe0930019242" + "c16f" + "bd0c6f3e6af40f06f58e2ceed84b80be7667394fb86b28e33b289e38ad01eed62e9c918d08988532ddf5f8b23c804c390ae063d4fd5d" + "7c71" + "d0f23f3d3ccf80353ff21c7d06a0f94c43c9fbab2c7d8e6999bada2d4500d7071d38294d1104220e507289f1b1efdc728af3ce5b1b8f" + "faf6" + "3e87441e4910352fc6f37e79c31fd0d2aa351eb3d8a972adffa594ae76332b862e1f2fffd2c8fb225bd3ebe2b3d326cc4aef23498e99" + "5c0d" + "488fa679053f8c37e810ac16eaab5c684447fd79b16156a75f8708e02330f529b038b150e2e8d085bc69174f38a6412dc11c8a01b236" + "3bac" + "d1734bdc34d4d9d1a3593a69bbba0b2ddaadd36f02175510c0143e33cc1e0ea16e5d907bd379c60467a52bb69ae1e39f59a61e87ab78" + "df8c" + "f5785f3c60bb73049ee4c18d7c9a1cf3388dd6d14c7d82f85500d11df2b1f8ca7cf710d64d49bd9adfc6328fcbed539161757aaf8f05" + "5e73" + "92feb3b6c5054c4ec6dff54ba777ef5af3086b3355f4d3d8d742187555f84ae3c033f5f32e9d631dabdcc3e10a6f18a32bf32fc49bc8" + "5851" + "58d68d428da3516d646a75affdc9dc0ed074e488c85b1e50d56083cada66ca3f2bbf699606720b3ac81dd3c1940f19a56bb68a0bded5" + "d1e0" + "b82926d19bf4b34eda76755072c4854f0f454764ee86450a96ba223db87db62c4cabf53db84d1071b6bc201e855ceffcb579d8979206" + "3370" + "ee33fb66716bdddd1ee511e7900ce3fe2db4a19512fe41c54fea0f4d04843095682a7d2e59bd6befe46f6d5d6d31b25486b9e2da880a" + "0645" + "7afba9d4eaa911f539c93efbe4a4a995fd70c59212eb5f210af7ececc1060c8315221ec8d18a9c1dcd17a80519928beed32e704bac3c" + "53f7" + "084fa17984341743f3f878b9115c9505d274cd091ad2cb0a9f0d61560e73561759f966f59a89d3bb889468002ee2bd1e4b69131e25e5" + "85ab" + "2e0cc0704fdc777d97402b4824f39021596aec0fe9a7e8f1afb72eb1dd767d8c08de2937d3d39e2e7e51ec14f3401f77357e86a4a194" + "37da" + "b5a0f17921cf08f8e5ded1138b1778fa407fa973dee162e4211d4149ff802779e173aace68a7c25b7f92e6a891ca14d9feb53da7d30c" + "6555" + "1dd2d1f7ec1a1bb58242877a09e5902e49f093d008620e589003508e5c89dd5385b4f96ace31a4e53e986ab8b704aa41ebbcb8fb348a" + "6b35" + "5e16446dd10db58bf6ead3a8e3d108bdaddc3d04faf704fd1a5076e6eeef3877d77aaee04c7aa941e92e63df1ba0c9a8347fb3e3ef10" + "fe8c" + "cf610b4b070658ee24f6a10747e1279ecc36adb2f9e87a22787b7844ebb123bb2c83bb63ba943543c73486980bceaf4061f12eea3fcd" + "f649" + "4740fa63e67b2b217c61c265fa242326e884d353e0472439adbb08a14f1fd4bbd5b5afcaae04b9a7ab734e7eaaf838e0f00decca546d" + "7f78" + "89883fe64b147d34f484dceb5069090e460fd54ad6c0599744057e80045180045145000578b537400040066beb0a2d00010a2d000314" + "5196" + "440dc88739f4439577801801fd494100000101080a7e069e563740bf2b98d0933602d916374c32cb9beb41a7e66cdbdc74295b4caf02" + "5444" + "1623b8524dac0f1e3ade88322d58c3b2ebb2c1db73eb972dc44735c115d5469e2080f0199f6f90a3f620564e4a6401d2282b235850a1" + "8bb8" + "333a7081ae50465024199ec841fe5775494926f5ddfa7c23d6b87927e6d57009fdcd76358c243f01323b1b41344db8d46deaa49afa3a" + "669c" + "11caebcb6f4b314ad9784327fc14a8b417329538060ded327d32f7db6288cb59b665526c18b8367dfdf9648a86bfb64632ba499e099b" + "903c" + "cfd16574966657305f80e9236af622ab6a07a0bb501f92901b0c9c2298c80c355414c7d66a96eb25328e0f602d692e07170ed9068fcf" + "e502" + "b4a52d7baf816c5ec41aebcdd5e5a2bd7197791ffb480b6a485e2cedeeb13e138cdd34d5caf526c6671de7f57466b0ea567f1fa02e8a" + "b6e9" + "9de86173344e95dee0bdb26fb3fdf4647700a8350f124a9afe530af427b82650b6a91c62369c806ed6df86bc4601af80c4181357209e" + "4947" + "9c8e759ca001d2a8801f08466ae1660d81e9a561ac62f04bc8b2da2da7147d8075da6f869212c987515b90f9e9b5b467583c8d43e835" + "aa3b" + "66b48e87adc17dc2468789ec4e1de225832aac02381c041a80c520a65bd20740808df454ac7bcf0b97cbd965ab1ccfb796f4751cae9a" + "96dd" + "d2d3abbbce8f4b9f63732e0cc4d49a10c7159c8dc58c6c5fc25ac5f0878a5214d3532d8d8734848f784fa93bf353a62f29b9a1a932b2" + "f666" + "8b5cf87d2429347876373b8e5772e866436b00b88582e9a936ba22db42c0457d1483574ea8f09f2874dc721acf3ea16a626875ac8d07" + "7fda" + "e7971d5fa2f75621d0e747025921a91e57202a4aaab2b5c9571682b515f0506d07046dd35b23b7557d2d03ade7fcb8b0e95489fc5159" + "f0ca" + "010e9bf51d80eebea7506a45dc2d160b45ddf28ce9bac3268841d3dbb646baa63b6d5d7ca027f041295237fdce8c82168b1b02fd193d" + "3087" + "a5aaede64f78b2ba033a0d7e034089554ea191f9dac8c31e907fca2874a8aef671ebc9b584bb1a184daeccbc88810cd754fd072b5b77" + "e1e4" + "8f8f381302d397325a600b5a5564af1db1213f334494b91517080bc459c2308f9e8b14175770d5f74dc36eabf378bedd1ae87b35b130" + "cded" + "1b925c90486bc7c2583f45748abf2cc552e6d1b05516c2bb6c26dbd1f4edf60a18ba8e81e55ed18ea076440a9e23efa5b0b03942649b" + "a230" + "7f8bfb7abb38e143c28330bb9b8236f0a7be67cbda8256608c4e762367491e61a0e6a8b2cebec0124e26b28aefbb1b928e1a383d8ae0" + "b3c5" + "f94a4dd5fc90371df089baaf77c97a1e68a5d3dc8e4d2a4fdc7fe6d69c5f1a52d8227e7c0d16b8b5a3fd52ea342586249ea98e7de0aa" + "b783" + "c9442b52c59e4c861ac10f6251fb22369c26d1d37dd71b3024b45a1a1a7af86b46166c2c0f97bb70026c3fc3098d33d10bbd31e218da" + "b37d" + "0edcdce783650ab408efaa75dea3e9cd24a3a37a995261237d49314fb458bc65890a6a83dbd6b584fbac80917edc1a2372f9827acb31" + "651c" + "073cb5d5aa26d7f48fc9de3f4c825e0f0e8aaf3f54f330c330bdbc06c00dbdfd639cb4d0ff87fe07cca1de5800cfaf8e4d39a745500d" + "f62f" + "27bedc88a1f198a7834d974b841186122f0b467558d4be0b7dd33c0f7574495a151055bb65a2f2b70b130e6eef2b6d44ae91db655f56" + "03c9" + "2a02e0e2bed77d17b0891552d38be9a75a138a702fc55f2e1c670a7516422deab50718a82a9711db0b9fcc008b37725fffa62042573b" + "9bfc" + "5b83a723b8927126e420e768a048d5ac58c2ba9cce131e503018df0e3f4fb47716ba0c62f6a6979b2dacf3a0bab482eef2832141f9f3" + "762a" + "94029e4278d45f3491582b736c0d2bda187f3c434cb7b9f8755a5892875588515a44057e80045280045245000578b538400040066bea" + "0a2d" + "00010a2d0003145196440dc88c7df4439577801001fd764b00000101080a7e069e563740bf2c4561ea729de6af7c0a79676c7763ba17" + "e7ac" + "768c2951bb9870622e77b4d1e904e0d2663c76acadf4e20ec8ba9b8ba83785c9cc2febfdc8ea3d27e9fe1269b777a670dfdce037f19b" + "ccae" + "ce3048b5bb19a900e80928c4b0aeb94cb0f9a38ddb1ce241eec24bed501fceb2e70bf8143d98ad36ae0936a9c5c6eb8edd5f190bb58d" + "5421" + "9a5678852e7ef857f6c19ee1ba30f1a26fa1ab7992969f2be4e1f475b0fa04c824529d121c60113d2f4993ca4c4152c82f98857b1c74" + "343a" + "48731bcd487d4841eab3c0cca90d7ace6ee4eb90dcea844e49158ea5ee26ad7f809591b2b00a6451063ea4fb085792767cb96dec8329" + "4499" + "2d4862c6c6ba255713626ab834bc2f1acbd3dee8fbe0a071e55aadfdc392968206f7b0a3e2f0da4d222c3a0e937b1e0155a8b6e5757e" + "8565" + "cbdf08e73be51e4f9df301064b50fadaca86b22d16c258f83a4328fe50ac1232fd777670267d10c69bf5838c02b608b8598aaf48427f" + "6cc0" + "2f1b446d872fbc0fc8a3855620b34e1ed3103730158434e61306d7167f79a11f864667bbf38dc6c45cd17e5b2fd67afa2cf9df200a7f" + "b5bd" + "d3196ef2b0485d71452a766db4c35dc941ceb20f18f88ea85bc65bdd9526ebdb0cb0d69609e42b9029f09384d7657f82f0eb562e0e15" + "029d" + "8344e5a49eeaca422a4f9f65185be693b5dab21bc685379c6d52c740644cfbcd655f4e12a54d7361cc242ca0b752301040708dc7c80b" + "1ae3" + "938a82ac6d60881260c7db7cecbbd5339bf94bf04fb1ca52704b53803d1cdc8ca1571ed5e417db023e67bfde37e519fca95b85fb83a9" + "2df8" + "a17bffa8ca0c16c8fa17dbbf4523a1bb31dc377ccf2e91b588aa0c566bdd0b428424c375a49093e3e93a0757f86738ca427597cc731c" + "0b6a" + "52c0b49ffd0cd6fb02083759ffc989fc0963f3140de34acb03c7211335ba04f0cc2463b82d7ef55cab7958f4510eb611dc28324cf531" + "a8a9" + "ce71a44b711f60ecea0e00a1a955a400b528fcf8530450b7f9580b1fab14bb847d4d7b29ad62569d906f3ddc3d1c37b75a5a558b6e84" + "e304" + "e7f9d5e7c9a1a9167ee3b758317f7110e95735809d8ed01189cb47ed3c965e0a29ca1c5de7d30a7c5320658bdae1d0680a4479c5e05c" + "1595" + "4fd6089530a25db3f4a549f92aef6d2362b7c6979577b5242ee6d94ef97565cab74045cee009b57751cd5e5188399611552442d686a0" + "456d" + "bf40a210b1de6b4915c370659b8d3c086a56d1c3dae7a0a067c97598076e3c32e0e0a8993f5e9852221efadab70531e7f97b535dcf1d" + "dd5b" + "c6455b4e256ce96c4c5cb6a44bf73167cbd101c5ed585322d65786ff6eda4f411872357f01458e2762be2b5b68d5221ed0ed359dccfd" + "6cd4" + "30a662af3c12f8bd769ebefc619835f4995c2b679a3cf1253be4678a39198907fb86df8c02f369eaf826954a24565467716f62931ba1" + "487e" + "d8b34bbd1edaaccad6eb35e0d0a53253ba0caaa37b15e10ee13a7193d5f9d4c24f131bf32a2fe971a37a4a32120c636fd327aecba249" + "5f84" + "0fc0e062eb2f4c138114bf484332a4193b68058e09057d31ea92dac20f13395d3242d55a66f749792b77e467dd01a90751d2dfb3c2e4" + "b8a7" + "da21eccfed95bf8feb3189433c06d02b134a8a2db80ce983e89702b8fec3448d7071361a7e5c22bd6035a46c50ab20679cb9f0839ebd" + "0ec2" + "c321b2e24dafc4c117264ae1ce27d4d4711e8568b5ed30db24724ec30fae3986937a6b36477acba89e0096ddb1e61d18863315db078a" + "2cef" + "2e0b632c101035e0804616fa2a8058c0a2970ad7023d0a955c75d01bacdee776191468aed782444b664f31d348abceb76ae6ae6e6fef" + "1b09" + "0adef4e73c7e3e104850931b0c4a3917f23a183cb352782665f7b1408412a0719a7a3d4ebe7db5e66b6744057e800453800453450005" + "78b5" + "39400040066be90a2d00010a2d0003145196440dc891c1f4439577801801fdf02c00000101080a7e069e563740bf2ca6c99f170fcbd7" + "8465" + "30e1e5bc62cfe2227c2b02d309a0c23d3883eab7e23aae444300c63d3ddec0075f01bb9ccbde7cf775095335ab1034cebd07f0ff74b7" + "debe" + "3cd1aa343c988025c258acd2b299a119d07045e0e02c0c815171e20e1947faa91779662ffcbe374318637e62b889d34f7b71f2dda4d6" + "ca4c" + "c73e76d9c97fd5bb0f0c6af96f0c307775536af43e7754cb53b2bec2a93b92379f2209592aeedfb2b34702aba85bed9d2cbb22340182" + "a2e8" + "a4a7d7fba881d3607f8509caf7df90fb50a0dd0f1b0d809d993d33ef6e3dff6d06ec0932bada41115230386ea2b0063310d078f035d1" + "ca9e" + "0e1effe47e23938e9417e9e1b28cfd9be2d3c4d082968f035dc9c63063927210337a39d6e4cb2862982a7498c06a2851c753eb7176c3" + "976c" + "fed4a2bff2d14fb838630fe4d1bf59614e940eb8281591f12639a8cf58e04219bf6e3c279d4654a0b5e1517d39ee4d14eab1cc328adb" + "867c" + "769034848ff3386e842890a5b1ce6079c3fdb4d562c0b520dce633a595e55b8b6ae8ad899651ddddcff0dea91c99b1d6ec9f62bafa18" + "a1fa" + "d91e41a7d9bc180b5ba24965b6b7881bfe50fa708d56d8227b381a055a27b8f2507f3c69951d4522d1ee66aad8d2780efe1322ce089a" + "cd49" + "75a0a02b93017d0e7701eb66ffd058738321c4ff66e58eb6492ba0dc54d13afd74221963a2fb29dd269af9c9b96b2201c67cb7363cc9" + "d43a" + "38036bee19c870b7e3f63222dcd7040e7dc5735d03fde4cc351d7ca78c73d58c8f1c35688d5ae6e49a0b90e2e16568462d07e49d461c" + "fd74" + "18e820d62ad336947b183b0ccbbac24ad29ff9133b0f7ca01bfa529ae130aca14e9feed6f6c7a41b5248631bd3f3e5778f3cf5a820a5" + "1542" + "1b07bdd20e8f20c04d855a28179f2f6e2fe771c14d5bdc16796c09a49f3199d4c5a2751a54cb1fa43945c74149eafcb19df3f40514fd" + "d95f" + "4ea9761242f2425d29fea872a6e8d8ab4a99a46132686a35c15c09dae44bfd975a3e264dc308dd0301eb0d000c4181b6586715a11633" + "94c9" + "2b209fb00dffd9d2054d3363e425eddb2b59d7dc297e6697e2e016358dc2c45a3e6244b1447105724193a3e729ed4d1ec3852410d608" + "dfb3" + "5ac801699fd9b76a1a2564275b1c98a34bd1afb2747b845d93db0cbed28a32a13ba7329051513a8ae501a1cc41f006a8e7558e230c64" + "36b1" + "f5486ca38e985d66ab72d20c989a8bfe0d704dc3bf9f72124248c965a5c4ee42e396c341d14f8a0d98736b8a26b996f2a74c0711113c" + "65c9" + "038ed4c00a4ba2a1685861171b9a711e2e852043e14e7aaa786ebd109b71451438bb782f0c2414a1317950a2b4557d4e276793c5df7c" + "f9ec" + "de627334824c4d324356922ea83aa9b60f5649d304619de16bf97e598ab275cf2c61548eb6a3f83e9745215447525bef4feb034e8c15" + "02cd" + "e19853ecf8e0e91448aa3c2ec1c6885f1957b19ce496311766005822d315a658ae3f4de3bf918c9aa25bed8d61000600c86dc6839949" + "e8f8" + "97a56e2ba80151ff8501679b1838fdd0119619e20f3395d75c391478ca6f558ad5546f3095d655d2d719f1fb5f714e56caca16f331f7" + "2e7b" + "d50a63c1b18cdb91f7bbd290fa85e722db810033fb5edcc023dc41369ac31d9c08ea3fce94d86c2fe1720f66f7f1f70295014596da03" + "a738" + "7f59dd7d7c105e3df35a6542485e2bb0b93ed2bd4f97ee0aab8e8c5c77ff4fb6b9f6ff52d7bdf7fbe5bd923966d22892c1791cfcfa4e" + "fe05" + "97f9ec6a8692fa096d312801c59eb4b513a03df612673a54c6fbfaab20f1bebcc328bc7efba9ea0c82ae4da35c30dec172c672abb8ee" + "9519" + "4b4ebf48bf972eef490d8550c5841da62d9685c6754d4919a14c02d1b810a0b77faf097c55f82c16000f6adaaad0f393136e0d44057e" + "8004" + "5480045445000578b53a400040066be80a2d00010a2d0003145196440dc89705f4439577801001fdb51900000101080a7e069e563740" + "bf2c" + "977c4f8933bac1635ac1b347db167e97c35e80a54f1f76dded8218d38a91107da54274e31d4c221f21d38412741abe1d35937c17c537" + "9b3e" + "8cdf023d96f0fa9f8170a45b3acb62cdd7e00840d73c8493bb1952af1b9cf0857c0c62d1d3eb8ac0772dc36f95fe6ab57baaf77b797a" + "2e9f" + "c4ef0e52ec1bfdaa1f685659906444b4907bd79d6808fe173887afd04f03741f0d7773f25d5b942a66fd7d00a29f3ace4e9ed48ba71a" + "2ab7" + "6122aa8dd88c4f73cdb4f867f6888bd116f3558f351d0257a07c593a885dd8d3996fc3c29f96f46ba3e4800073c4e7a57d7f9e73cb22" + "98a0" + "d8bd1cc728191748660e175a11914afc13077cd5a3d3231161dce49d57eb5f9bfd163505d35530dda334d578be3ba05e790490c6194f" + "0103" + "4614975733f39560077dae5e3a1ee1192b7a74a9570e2177f2c513c32cef433715ec74f4c2efa30d27d73a8a02210f99ba0c1cf41eef" + "66bb" + "bbcb85cc30a3b9dfb62e82910412cc09827f57fe7067f7359cd6be112807502d6198a4bbe666cc240e7ca45d081dda065e20f0f7feef" + "fa38" + "451252494cddc4f4e05175b0baf8b62f4917cc262b25a6337044cc34ba548afd7651f6740c8329ae05b4821247e3d21b52767e729844" + "27b1" + "ccfbe20270dad0130b9d8dbfc758441668ca5cac2a9400ee4314ae83dc6a9fa5e39a83ba9fade0b74ab9d96db4fffb876ad27d763a19" + "5311" + "2c13e9b90f409d59fb190a97036037a6c126cad807a314a433beec6dc46508480621d66aa32093143ce3d015e01ea80af8e0967c6410" + "8d67" + "85933d88e5adeb56f7cb06a9777b2636858ac787f28729a1dcab5b49200fc89f82eac81148464184861bf429ca70a308c7072016e573" + "a6fb" + "749ea40487662cde6ba88658f158cfe82889a5f6cf3af5a94a78d2f41eadd224afe358859252fd96bc01997c67534d7b74412cc0f3ba" + "29ca" + "815b0a8c8b5937fecfaaab8add042cfd7097bdff7bfa7673dcfb867c6c1f9bbfe3cb6d03f34aca8b89b28c2a2ba55d620b69b4bf4a43" + "729c" + "a7a28bb67e069df0b9129ca3436aaade7ae366fd5a54389da34f7ae4597c8b718e120b125e0ab0e9f0caf03c28bc48e1d3a16e368640" + "a4f0" + "ce8c5720a698764d46d18c003dafec79d62dc57acea5a2cbffd0bab6e3703a800477ded10efd30c8a13297474fa63157343ce27d4a86" + "a589" + "a31d62056db9ad9b8105550163e6451240dc0f074b8f346a91eef6342cf1131343d971efe0773820a7d98377f7d41f1701249dfddefd" + "4ec5" + "33ceec2432806a4b96a8ccb524c3a9905cdde611b7789a42c78a11c9f90d5ad95858b0a59bbc7c14b33b694e2343876be6fd15a0efba" + "7b91" + "e68dcfc85d11f91df623dd03ebc590a01f6b0cf96b4cdde89c226ae8560fb2f48507fdfe5b3142d8ec217473e1b6bbca534f210a6b24" + "efc9" + "4335fd29bbbe004fe38da90b51eec54b208bf9b5f59ea5d78d58638e89672a65cffd0e972d53c635b178682aa48e1d0ba2684aed1c95" + "94a5" + "1c267c9e4bff57a29f9672db16e7ca30cac6cec476bca4a0ab114e653ed4618d7dfec0580b0d896972c9e0bcfd54ac43f1e53c02eb6c" + "abe7" + "5477b522a5c72da5a033fe744e4c23d6716db95f546e0a3aeaa071a1f126409b7e3625980093c6ab42bcf8ae09ae4cc54618dd12f6c9" + "bc81" + "f6cf0526aa8eac8c69db869a208519e50f2a168f037027b516ea44099877a7911830c738791d75cefb5feee854b7916a009077e18a89" + "6d14" + "90c3269ce885ec6d5f0ba5aa3b65366745ffe35250b0ecb2366a05b2213d8b989c5ea099f1295c38b6f2c57c6a4cc8263a575263f25e" + "941c" + "c37b3dce14b9aed9c2bdffb4bb85e7ea25d8d1cfc7ecd4e036c9b8ca042e840e967d06c13ba2c51ca5d81af2233dd8a454de51fd9af6" + "65a9" + "3239c04244057e80045580045545000578b53b400040066be70a2d00010a2d0003145196440dc89c49f4439577801801fdd662000001" + "0108" + "0a7e069e563740bf2c59b87a35f5179ea0b6405a660fbe39fa5b6860b4fd22c160944c306e51dc2513ff8f8915a0254817f1481a9c2d" + "7326" + "4e6e95c68d4a516afcd2e048bdb4401bf86bfc1d95186a8b14aaa1584799aabc9cedf9053f2641dab620c1df68231b778afa193c570c" + "cd0f" + "fa04646d65f591f8da32ef4b7d3fe593521352a64d2294cb410bcdafd50035541096cc0fe84f201bdb880dc64401012f481752480b9b" + "c0d8" + "0bd493c2139817b38c408142b55cbbc91056e011a35c58b6059bbcf2c491af60cf27e172b3d45241c96b099b9f81e87dc49fe254f1e0" + "c6fb" + "63a595ddb880509ddba227d3c8089905c3e27577d7a10f6434862027983d1cbc98611809a6374be849518c6dbe54b1006d758d5463b0" + "830e" + "eace4f6a5c9e3a281347c8c9483dc72bdbfa4cfeca130779e13e326948e83a16cebc6441c3831b2acfa2060b45f46994aed443b88718" + "bb15" + "5ac06d99ab70401eefae033c88f3d9c26cafdcf3b7549722b343a4d5ed9f39165daf1d4fa21776c10a4cc11ccf0686e9da723d6dc643" + "7b94" + "70bec396e37d9786b3af22380183b6a955605ae97a596cb09a9f687fb68d1ca117e1efbd6c97ebdb040de0145a6483640ee03148db79" + "d475" + "faeacab5df4cb057c52ffb62a4cfc5414681239118491521dd44ca302a37c541c1de68d1140c0c52f27937232f6ab4d69eddf85d5b80" + "7146" + "5407465e68a9de3d943620246325394a7674d5c37e315005bd51ff06ae9ef20063019ea4eb7096c763dc91dd999a169d85d879057349" + "14fa" + "23e6dc366a31af19da68c25e205d0d6cae37c862d9e07af0f63a141304dfa94c4be519ac58440972ae31b59ef15ce46ad2714586d128" + "ee08" + "a5011cab6a30ab74298a5221a321cfbfe9bc0e93de2d84a7de991e970fd2299d83bb529da8b451ad34a8bd5a456d7ee24c9e72d7686e" + "563c" + "7b788858bc4f78e3fd38c1b753e3eb30a9fc77d07eb9fe58967f2b8f77cedb539b769df4f8c271cec4c9062df81fc3b9e5ce64c2765e" + "8259" + "ff73a52ef24f7b161ec8975024f097b36761dec9beba2a7c51ee3471b345ce214a826d323c938d7f9040c08c84e0a2314de473afcc21" + "b068" + "9f40dbd1b570619831cb9dc56cc504d9b2a6f726cea70bdc2ae462ac5c0fe1a3a2e713118ab4ec9fa2b80689386d45ecf3c526632922" + "2f71" + "bb774d4716777e64cc2170de49f422eaf0a437233010ae940cabed94ca1a22298b79d2547df8578db2964cfecb382236a0894dc6b0aa" + "bd20" + "6e27280dbb101ef311262cf5f349116e4165ab50e34ce0f027a19217ba8d3e393b9d6521cc43c1ac096fe58a91550857e72e7d865530" + "6f8e" + "6d414834fcbe557952b15ac424fc27648155e9b2c26e6c56c1061567ab8becfc8b54b00abfe800719ad5b92465a2c2fbf0d3cc9ddf3b" + "a00e" + "18e8b20bf8412ab21c3a38ccbfb16fa1216869ac0bb9df188ad687acdf90afe47ede664e1ed2fb22a44ac71f877df095ac44f8de2efb" + "3b71" + "540f129bb9dfd3d3c8a87f190a9feacee84e1cd57671ab59b62985dd659810349932c2782c5c2d9ee5d21698659ba2ddefa0b95f787a" + "d1ca" + "21f5d2f3b9a8485a168d7e12a936a3c08ee784d52c3a209aa5a015b1ed501770c8e2507c35156784b8cf167d3ec092d739df7e970ad1" + "4288" + "817c5a58f2af904e3e0173decf92bab601c6ef46f7cac54aea4ae21373e929eaf27ddd78e970a9020bf46491e3cc1cd8de04c7bdd741" + "df31" + "a9c7760c3929a6a62d11fdcd81fe8ab5e81a95a6679d003df8ef310df4eb2edf7ad6d94c01cfc67befc432846f746ae801be80178607" + "5b34" + "1d5199fad7c7f58dfc27899fc0dbd483e78c2bb648fedd97f73a9aa29101930a8b6f8e3fe22d2764c60b3d715d931a57a85983809a1a" + "114f" + "64b6fef48d7e537e8047b1363b44057e80045680045645000578b53c400040066be60a2d00010a2d0003145196440dc8a18df4439577" + "8010" + "01fd9bbe00000101080a7e069e563740bf2ce06f1248bd16d7296bb3c6a263b82e3845e48f8b154117f1e8a30024850801c815d78d52" + "2769" + "40519ce6b96a059fe047fdcd5d3b46bc57c66e5b4c919e6cc13dc445546ebb4b547d255c45acf05bef2db27e7a0225fffe46af19a936" + "9362" + "5b81f5a686d9edaa0a23061b5507b9203df4bec9e466be42814712f941decff0f8bd443045543faaccd84c171b4f99ed218cbbd8c4f2" + "2b3f" + "cbf12152fcc7e6b9b2735aab2af590edc313bf6c3a2902050115337133632a0a0bed69b76c1a04af38868dd18f5a1818e042a7f202fc" + "4405" + "c54885b0258e3cd768076b65d5d67c9dceaabd9488e997e0307469c95d9146dfe9ba37a4096f3115be6343c532a850e9586b9274e508" + "27ea" + "d2756e90577c490d4852841e5306cdb2b110cb1a5e34ae8702b8336b5434949f00ab7df67d47bb1425f3c66e76c83d8a0fe06120d9d5" + "ce5d" + "3b0d4a4f3969057b6004389ceff043c01f215b4dc3094c5a350440f82c4ec6dd2c8ae80f183cfa0f6c6f7b66f2c47474bc55d6561efd" + "af5d" + "a79116d4cd17816aed1d2676d3a59e23ef01bbc3f8ccacd9285cb088298f184c235959dc7da033dd7651d04decf58e4ae85dd79784b9" + "f54e" + "bc1d075aaddcd1fa8eec4181b877c972fbbe6e59400e0f8ced3c18f9a3713cc8b98e663ca443f9f520265e26c3c30bf8cf6a06a2f21c" + "7a9a" + "b91398b5781578f7a953ffe551574b87c6d396c48349785e4fa4a7b9e5f9986781c8ec74305dd08b61595fb05de25a9bcf91cf2f80d3" + "1c29" + "088861b5fcc599b937e7c1ffd8bba29a01f730b3ef0337ea94c963dbf1bdf32937a0095dd4e9f94c721ac4d310bbe90755bfd2f3ccf1" + "e685" + "77280034f1ccef5a3d1148712910904a5691df56d66d4a59412cbe6c9620644444c07454488087a887b78df14261d59c638c66c38c44" + "b309" + "aa5f09583f10b4fdcf1bbaf10383d810a077d88a1bfe062193f9da8ce7534c896a77de68428745df36b1237b8bae3c9d48ed06109ada" + "ac89" + "280947a82a8826e6092b975ddd4f0a6f181c007c99ed4e78a8149adf267306d98a8242910d8b388ff3d65f169b487c33e08a3adef097" + "4097" + "69e8530b00585adc9c60c3c69b66b1281f409f22fad035ecaa6e5aca85cb4564cf6c60a80eefa80cade210fa32178b61d60e3c6c0955" + "74eb" + "5950d7c068b18914f65a1ae13b49ac39551eeb4899c427306d26331648b7f131db1e27170d5e5851710d8e0fe992603e0d7fcad95964" + "b1c0" + "46d947df874045ae03216b3d26f5bf5446534f6ac0c0609b960ca1a1cf1be377a86d39d8f6a367673c29cf6285bc44ac6583ead8dc32" + "1122" + "9b077da4cb49ab43f100ebd5f7297923b4bf63479345ce2d93458597f52e767106837fa433ab846ffc36ab333d97c0cd63e9677c22d4" + "e49e" + "5e90af290bc3e08663473d8b0daa54e0120cc8039c56bdf76e4a4e143ee4947c6c246e4f7e2768c720bc824bc1de3112f8f84b58fa38" + "8227" + "1c9ba3ac7de3a80b94c9dbddb9bb0948f862424509382c9a7af37b4b2dc6a14b364d3674141b789e8277e7a95b5b9d9417f8d82eea29" + "7c58" + "42321eab91d36ac42d62aac23417ff08deebc5399f01cccf29f83b2756cb7d3fc2960101143ad7daa190155ffe0b93e943bb3fb4bbd7" + "626b" + "ddfb66d6d2bffe41a7cc350d48f7136ffde25487710bea1e31c2fb7f2614d63e12e0b80a9cc3f6b0e73627517deae9e203a94166d6f6" + "c877" + "e5cb8b6f16de5d2d4f93647ae04b063b5a0b0d18b72fd93a0325afdf3f0888c30d48f06ef9f0a35411f0abc2751ee1cf674517f39364" + "a353" + "3cd276d0379b05ec0da16eb552088e3cb484a96aa9fa43abe2c63ea100cfc074be50a3ec567d08112d952cfe5c71d4d5cb6b1c6f7f6a" + "0aa8" + "27cd90a2c460d894f980016e22f73fe2000dcc7b479b44057e80045780045745000578b53d400040066be50a2d00010a2d0003145196" + "440d" + "c8a6d1f4439577801801fdaf2800000101080a7e069e563740bf2cec3ef95a142a919ff39936cecfc079857d2a3def17af3b7f37e1e0" + "5402" + "87c860d13f76e896cabdc26904bfb47c19ab719a081397abb83ff61efe8c7e10abbee769cea7d4fec0bdd0ce9d59ebbd6c9c6ac24dbd" + "12aa" + "1d28c1755abe40aa9ea0c27c341fcd6b19eb0536bd8b7c9c434697bcc0d42ffb97fd66edfd815375aeafe6570520b99806ccc3b154bf" + "2014" + "4873daf12849942b08a4de7e5e376ebc318092d5e0b1fd1dd668a2e179d95c7cf2418dd1a780320c1fa592e7aae61bbfd6810e31d88d" + "64cc" + "809c8c4fed6fa3ad6cc73b7d9c99e233543803802823f92148f55a2ef2018bf559671a579bca0dccf61ee5a84303af875c7dde89fe1d" + "ec89" + "49fbdec1794cc215253c5e6c3d09dbe9c9f4a3767fe9b0d53d0d9e8fcf40e4970b733a23b26a22b912613a593ac8a9f07be52c4c3ead" + "3a50" + "ef1d2fad02829fbdc162fe34a48910b170048306e590eeca549517f733dfbcf14ce7fe3734a7bb5b85977e7a2b559111ac6ebfa33559" + "51b6" + "8eb93ce88b9e5f4c059e649d949b3942c836525765e0a104d47cdf72fa64f0866bdba6c0719b490a9e1adaca90f6125050d97b1bf96d" + "2ee8" + "4d6b4470a03c9d528cf57fa659cdffb539a260f38e91f2317fd8c50dc7948a9a524be34da3b3bfcb06b7df0be4483386f9fb1b2578fe" + "0562" + "c5b99fffcb2f88d143d5920d984cd8e61ae9c1d328883ebf2aebd5b8552ba459ae2a54814ea79a78c20317d940c074f8b1fdcf4577b4" + "a290" + "3b4118f38f1a1967971f31a992f1918af007c087877a93043b86f05542c79eca59b4deee91c521fb695ee99b902af085d1a07b19a6cd" + "2d3c" + "c873e9ca0ae54eaa83949819884684e2b59040b3bd24d4ad2137310a36fff470cd0092a2049382c8e8420dd8c159a4c5142eccf6160d" + "e7cf" + "225ff5088b6ce0fe89dec6b2891e743a5b411b52dd2780f67f61174c53bdd695579d975ee17c508ff20ba6bf09121822e3fe9ca9e853" + "fe81" + "88400380fa0c4ed6233a68630b80cee749077d08ce8bdd6d51dc59950c5c5bd59ece66abc935965c7ebc5225c7cb6fa23e07beb2d827" + "2cc1" + "0a69f8f8c8d49a89134a6e9dafb5936cad0304f1e5c8e47cc6a1846af22372bbbe973793b1f56c6158de78eaed60cc9a985b18eee85c" + "332e" + "a24d4ba063d78927b9a2250b535e304bd33846b553916c11ec3884a81220903e16b126195417c09a0bf7d2a91b57fd50209a4fe93f1e" + "f5f5" + "8a08b4d1541ef7078d8482965bc4a875eb71e1ef56c8a623225e71700a139ba51366e6e58a3e0fa132ae8547f56ab1f245d8b054d9f4" + "d2a4" + "62f4835b7215e91d4291a76cbf3a97baf8d46776c8ccd137516b251d13aa778bfee34702cd299b71c76f95e3c909b3d38dc4b9953f1b" + "7f08" + "93edd41e0738f7353579871aa30f659995b6e698686261b84aa3bfebb8070efccd5e82d3c2179f03f328dcc9e58af02cdb2e060282b2" + "6014" + "d4fdbcbb4f77684118058e412890618b245b52841aa28a0c7d44cf2871706bf9a56a41db5fff9e57b0e45a8e0494fc9049c36bc605c3" + "6ad4" + "a3806cb9903409ab38fcc3fa6ee61df69d0daf98c62c8c3a380b6a4033a5fbb257e34fa363f6776e44492f6e87ff20abc82cb959bed9" + "1941" + "87767d39ea5bf8c25e93007c48f18fc4ccbc8faca025759abab6be27fb8e8e22d294c60592b5906dd96ef8ffaddbe1eac1477e37be94" + "25cb" + "dd9bd17b17ba70de631ea09f6d9b27245a81b8dd9cd85c064d1832166ab182adb6fb8115d5d9a341fa863e92d2b89ff2e9464723b34b" + "afba" + "9492eef498855ac486e4bab1b9ab69051ccc6fac3a47f16b64a6b43105367e2150603e395665666a4774077d4b7f7947b29092ee01bc" + "48e4" + "d668ec61312d2299924197c3b6e92b025c54e6feeaefa0f60698e9157f546b4403c690045880045845000578b53e400040066be40a2d" + "0001" + "0a2d0003145196440dc8ac15f4439577801001fd38e700000101080a7e069e563740bf2c12aef992cbb4a70140b8edba1ca001962ad3" + "75f2" + "5293727698804fd44d7df7e5c7c780b562d06167cd4bf3f5f9d7a42f321083e88e6389534bf002e81ee1191f6ac48538119c2c067116" + "1acc" + "b440f4533febe253f5a1254b7efd5b5e19638efb3565c81606590523e338bd2ca479408dbab1af0a8c1261472e59290f2035885f061c" + "03ef" + "9404aea3922bcfe439e132079ebf026d5bda87c5bd6685b9c30bb9543ede8a537f47627d250f7f92c4b3c3511e5bc1e460784bb79f30" + "205e" + "8e9cfb28b5d386433851d9cd6bbfa2a769ddcfba784049748c23a97b9dd9b0102a688bbe2ca85d40f24629cf65d35ba2d9ee8c5c3723" + "663a" + "a63460364a767c2fb928778f39b03716db7905c6822c07bc2f26952a20356e2611088ef6eaf21e19c767f2347eb3a7ad8d761542ca72" + "374e" + "3fb10b0b83fc07aa6b75641b48aa2bbbcd48d422fe0d215b4aadd43a6cfedf3f6ecc83a3df3726ab43703d651b077fac0e959c1a008f" + "8a98" + "7a3008e9244c90564af123662104f0f39e1ad51e304dbeeeb0b6d59ce60d2a4eef74c19ea89b75b559d4c6a24b31cf4816b19b3f0edb" + "77c2" + "7c93c667f7f46c8b4153664cb9638dfe71244cd555cff202313a517083312245ad0653a8387e63e844020801fdddfbe7e63901b3d75f" + "82a8" + "2c1bfb601064db01a5ac2681d35a7797ed44c07fa6b8d782fb9726dea13e7a3bcc9c0331acd67e43e6cd6aab62c511a365c4fd2e67c2" + "bd50" + "03067f098d4cc7f16c923859b5ef1e1153176b72eca753baa73bc91adbd85a1aeec61bb63ad72c62992d1a78a87f19bfd7abaed7040e" + "4e8d" + "21e8ac08008d8914e55cf16977b7a9f84b10e7f29d48404b6e87c111ef5e415c99fdc72fbdeabdd9baad9efe1e05d33ccfb1643c43e4" + "8ae9" + "ed7b179693b4f1a270b2a1a6e5e572336160579453d7bef82855b27dc4a26964c9898fbcc2dd26b72019d86b4b26ee27c1a617cff355" + "d026" + "b620b47ba9fefa47b9cbbc7fc97c17ae9713dffaad490e42f40093106a92df63200253682f2e32f1a7fcb7bbe6ef37abe0fc5275b3d9" + "9b9d" + "dd97149c9b466d622b7a641f5a8916dc81cf160f8938185057a770ef7b20de955e30fa904ddc828b97f7e4ad111462dd9bb9b724f8b2" + "9d42" + "c94a12d6ad43edf2697ffa4a62c12ab00378912b106efd2c50e8c46a3a1f8b682c11f8702ce1591e27042413e38c340def44b68a94a4" + "e761" + "beb25106cf2e60c0aa9f8c0f745bdc1ecb8e9bfe71e9b4846bbea0dc5d2d982d17176c3119432b90e3a8f48195d21a06") + .value(); int crnti = 0x01011; int ue_id = 2; diff --git a/tests/unittests/phy/lower/modulation/ofdm_prach_demodulator_test.cpp b/tests/unittests/phy/lower/modulation/ofdm_prach_demodulator_test.cpp index 1c28709c72..af1703ccfb 100644 --- a/tests/unittests/phy/lower/modulation/ofdm_prach_demodulator_test.cpp +++ b/tests/unittests/phy/lower/modulation/ofdm_prach_demodulator_test.cpp @@ -114,7 +114,7 @@ TEST_P(ofdm_prach_demodulator_tester, vector) : get_prach_preamble_short_info(config.format, to_ra_subcarrier_spacing(config.pusch_scs), false); // Calculate number of symbols. - unsigned nof_symbols = preamble_info.symbol_length.to_samples(ra_scs_to_Hz(preamble_info.scs)); + unsigned nof_symbols = preamble_info.nof_symbols; // Build expected buffer data. prach_buffer_spy expected_buffer( diff --git a/tests/unittests/phy/support/resource_grid_test.cpp b/tests/unittests/phy/support/resource_grid_test.cpp index 24983a05e9..16f20b6ca9 100644 --- a/tests/unittests/phy/support/resource_grid_test.cpp +++ b/tests/unittests/phy/support/resource_grid_test.cpp @@ -70,89 +70,6 @@ void test_all_zero(unsigned nof_ports, unsigned nof_symbols, unsigned nof_subc) } } -void test_mask(unsigned nof_ports, unsigned nof_symbols, unsigned nof_subc, unsigned nof_elements) -{ - // Create grid and zero - std::unique_ptr grid = create_resource_grid(nof_ports, nof_symbols, nof_subc); - grid->set_all_zero(); - - std::uniform_int_distribution port_dist(0, nof_ports - 1); - std::uniform_int_distribution symbol_dist(0, nof_symbols - 1); - std::uniform_int_distribution subc_dist(0, nof_subc - 1); - std::uniform_real_distribution value_dist(-1.0, +1.0); - - // Put elements in grid - unsigned symbol_idx = symbol_dist(rgen); - srsvec::aligned_vec symbols_gold(nof_elements); - srsvec::aligned_vec mask(nof_subc); - - // Reset mask - srsvec::zero(mask); - - // Fill mask and generate symbols - unsigned port_gold = port_dist(rgen); - for (unsigned i = 0; i != nof_elements; ++i) { - unsigned subc = 0; - - // Select a subcarrier that has not been set yet - do { - subc = subc_dist(rgen); - } while (mask[subc]); - - // Create random allocation - mask[subc] = true; - symbols_gold[i] = {value_dist(rgen), value_dist(rgen)}; - } - - // Put elements - span symbol_buffer_put = grid->get_writer().put(port_gold, symbol_idx, 0, mask, symbols_gold); - - // Make sure all symbols are used - TESTASSERT(symbol_buffer_put.empty()); - - // Assert grid - unsigned count = 0; - for (unsigned port = 0; port != nof_ports; ++port) { - // Verify the grid for the port is NOT empty. - TESTASSERT_EQ(port != port_gold, grid->get_reader().is_empty(port)); - - for (unsigned symbol = 0; symbol != nof_symbols; ++symbol) { - // Get resource grid data for the given symbol - std::vector rg_data(nof_subc); - grid->get_reader().get(rg_data, port, symbol, 0); - - for (unsigned subc = 0; subc != nof_subc; ++subc) { - cf_t gold = {0.0, 0.0}; - cf_t value = rg_data[subc]; - - if (port == port_gold && symbol == symbol_idx && mask[subc]) { - gold = symbols_gold[count]; - count++; - } - - TESTASSERT_EQ(gold.real(), value.real()); - TESTASSERT_EQ(gold.imag(), value.imag()); - } - } - } - - // Get elements - srsvec::aligned_vec symbols(nof_elements); - span symbol_buffer_get = grid->get_reader().get(symbols, port_gold, symbol_idx, 0, mask); - - // Make sure all symbols are used - TESTASSERT(symbol_buffer_get.empty(), "Symbol buffer - not empty."); - - // Assert symbols - for (unsigned i = 0; i != nof_elements; ++i) { - cf_t gold = symbols_gold[i]; - cf_t value = symbols[i]; - - TESTASSERT_EQ(gold.real(), value.real()); - TESTASSERT_EQ(gold.imag(), value.imag()); - } -} - void test_mask_bitset(unsigned nof_ports, unsigned nof_symbols, unsigned nof_subc, unsigned nof_elements) { // Create grid and zero. @@ -323,7 +240,6 @@ int main() test_all_zero(nof_ports, nof_symbols, nof_subc); // Test symbolic number of elements for (unsigned nof_elements : {1, 2, 4, 8, 16, 32}) { - test_mask(nof_ports, nof_symbols, nof_subc, nof_elements); test_mask_bitset(nof_ports, nof_symbols, nof_subc, nof_elements); test_consecutive(nof_ports, nof_symbols, nof_subc, nof_elements); } diff --git a/tests/unittests/phy/support/resource_grid_test_doubles.h b/tests/unittests/phy/support/resource_grid_test_doubles.h index cd956653c1..175133d787 100644 --- a/tests/unittests/phy/support/resource_grid_test_doubles.h +++ b/tests/unittests/phy/support/resource_grid_test_doubles.h @@ -80,29 +80,6 @@ class resource_grid_writer_spy : public resource_grid_writer // See interface for documentation. unsigned get_nof_symbols() const override { return max_symb; } - // See interface for documentation. - span - put(unsigned port, unsigned l, unsigned k_init, span mask, span symbols) override - { - std::unique_lock lock(entries_mutex); - TESTASSERT(k_init + mask.size() <= max_prb * NRE, - "The mask staring at {} for {} subcarriers exceeds the resource grid bandwidth (max {}).", - k_init, - mask.size(), - max_prb * NRE); - ++count; - unsigned i_symb = 0; - for (unsigned k = 0; k != mask.size(); ++k) { - if (mask[k]) { - put(port, l, k_init + k, symbols[i_symb]); - i_symb++; - } - } - - // Consume buffer. - return symbols.last(symbols.size() - i_symb); - } - span put(unsigned port, unsigned l, unsigned k_init, @@ -302,21 +279,6 @@ class resource_grid_reader_spy : public resource_grid_reader bool is_empty() const override { return entries.empty(); } - span get(span symbols, unsigned port, unsigned l, unsigned k_init, span mask) const override - { - ++count; - unsigned i_symb = 0; - for (unsigned k = 0; k != mask.size(); ++k) { - if (mask[k]) { - symbols[i_symb] = get(static_cast(port), l, k_init + k); - i_symb++; - } - } - - // Consume buffer. - return symbols.last(symbols.size() - i_symb); - } - span get(span symbols, unsigned port, unsigned l, diff --git a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_enc_dec_test.cpp b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_enc_dec_test.cpp index 51071bb665..113b7eb6ea 100644 --- a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_enc_dec_test.cpp +++ b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_enc_dec_test.cpp @@ -360,18 +360,18 @@ TEST_P(LDPCEncDecFixture, LDPCDecTestAlmostZeroLLR) INSTANTIATE_TEST_SUITE_P(LDPCEncDecSuite, LDPCEncDecFixture, ::testing::Combine(::testing::Values("generic" -#ifdef HAVE_AVX2 +#ifdef __AVX2__ , "avx2" -#endif // HAVE_AVX2 -#ifdef HAVE_AVX512 +#endif // __AVX2__ +#ifdef __AVX512F__ , "avx512" -#endif // HAVE_AVX512 -#ifdef HAVE_NEON +#endif // __AVX512F__ +#ifdef __ARM_NEON , "neon" -#endif // HAVE_NEON +#endif // __ARM_NEON ), ::testing::ValuesIn(ldpc_encoder_test_data))); } // namespace diff --git a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_rm_test.cpp b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_rm_test.cpp index 9638f016f3..ff673f3508 100644 --- a/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_rm_test.cpp +++ b/tests/unittests/phy/upper/channel_coding/ldpc/ldpc_rm_test.cpp @@ -219,10 +219,10 @@ INSTANTIATE_TEST_SUITE_P(LDPCRateMatchingSuite, "avx2", "avx512" #endif // __x86_64__ -#ifdef HAVE_NEON +#ifdef __ARM_NEON , "neon" -#endif // HAVE_NEON +#endif // __ARM_NEON ), ::testing::ValuesIn(ldpc_rate_matcher_test_data))); } // namespace diff --git a/tests/unittests/phy/upper/channel_processors/CMakeLists.txt b/tests/unittests/phy/upper/channel_processors/CMakeLists.txt index e12cd3d730..0303b91ef8 100644 --- a/tests/unittests/phy/upper/channel_processors/CMakeLists.txt +++ b/tests/unittests/phy/upper/channel_processors/CMakeLists.txt @@ -55,8 +55,11 @@ target_link_libraries(pucch_processor_format1_unittest gtest_main) add_test(pucch_processor_format1_unittest pucch_processor_format1_unittest) -add_executable(pucch_processor_validator_format2_test pucch_processor_validator_format2_test.cpp) -target_link_libraries(pucch_processor_validator_format2_test +add_executable(pucch_processor_validators_test + pucch_processor_validator_format0_test.cpp + pucch_processor_validator_format1_test.cpp + pucch_processor_validator_format2_test.cpp) +target_link_libraries(pucch_processor_validators_test srsran_channel_equalizer srsran_channel_processors srsran_phy_support @@ -64,18 +67,7 @@ target_link_libraries(pucch_processor_validator_format2_test srsran_upper_phy_support gtest gtest_main) -add_test(pucch_processor_validator_format2_test pucch_processor_validator_format2_test) - -add_executable(pucch_processor_validator_format1_test pucch_processor_validator_format1_test.cpp) -target_link_libraries(pucch_processor_validator_format1_test - srsran_channel_equalizer - srsran_channel_processors - srsran_phy_support - srslog - srsran_upper_phy_support - gtest - gtest_main) -add_test(pucch_processor_validator_format1_test pucch_processor_validator_format1_test) +add_test(pucch_processor_validators_test pucch_processor_validators_test) add_executable(ssb_processor_unittest ssb_processor_unittest.cpp) target_link_libraries(ssb_processor_unittest srsran_channel_processors srslog) @@ -155,6 +147,16 @@ if (USE_PHY_TESTVECTORS) target_link_libraries(pucch_detector_test srsran_channel_processors srsran_channel_equalizer srslog gtest gtest_main) add_test_vector(pucch_detector_test pucch_detector_test_data.tar.gz "") + add_executable(pucch_processor_format0_vectortest pucch_processor_format0_vectortest.cpp) + target_link_libraries(pucch_processor_format0_vectortest + srsran_channel_equalizer + srsran_phy_support + srsran_channel_processors + srslog + gtest + gtest_main) + add_test_vector(pucch_processor_format0_vectortest pucch_processor_format0_test_data.tar.gz "") + add_executable(pucch_processor_format1_vectortest pucch_processor_format1_vectortest.cpp) target_link_libraries(pucch_processor_format1_vectortest srsran_channel_equalizer diff --git a/tests/unittests/phy/upper/channel_processors/prach_detector_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/prach_detector_vectortest.cpp index 6bd87cc719..f6029a6c94 100644 --- a/tests/unittests/phy/upper/channel_processors/prach_detector_vectortest.cpp +++ b/tests/unittests/phy/upper/channel_processors/prach_detector_vectortest.cpp @@ -129,8 +129,7 @@ TEST_P(PrachDetectorFixture, FromVector) } // Calculate the number of symbols. - unsigned nof_symbols = - static_cast(preamble_info.symbol_length.to_seconds() * ra_scs_to_Hz(preamble_info.scs)); + unsigned nof_symbols = preamble_info.nof_symbols; // Get frequency domain data. prach_buffer_tensor sequence(sequence_data); diff --git a/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_test_data.h b/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_test_data.h new file mode 100644 index 0000000000..cdc7d5b33b --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_test_data.h @@ -0,0 +1,139 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#pragma once + +// This file was generated using the following MATLAB class on 22-05-2024 (seed 0): +// + "srsPUCCHProcessorFormat0Unittest.m" + +#include "../../support/resource_grid_test_doubles.h" +#include "srsran/phy/upper/channel_processors/pucch_processor.h" +#include "srsran/support/file_vector.h" + +namespace srsran { + +struct pucch_entry { + pucch_processor::format0_configuration config; + std::vector ack_bits; + std::optional sr; +}; +struct test_case_t { + pucch_entry entry; + file_vector grid; +}; + +static const std::vector pucch_processor_format0_test_data = { + // clang-format off + {{{std::nullopt, {0, 999}, cyclic_prefix::NORMAL, 51, 1, 6, {}, 12, 1, 7, 821, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols0.dat"}}, + {{{std::nullopt, {0, 4307}, cyclic_prefix::NORMAL, 51, 1, 32, {}, 13, 1, 3, 256, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols1.dat"}}, + {{{std::nullopt, {0, 4717}, cyclic_prefix::NORMAL, 51, 1, 35, {}, 10, 1, 3, 5, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols2.dat"}}, + {{{std::nullopt, {0, 7750}, cyclic_prefix::NORMAL, 51, 1, 16, {}, 3, 1, 8, 214, 1, 1, {0,}}, {0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols3.dat"}}, + {{{std::nullopt, {0, 1757}, cyclic_prefix::NORMAL, 51, 1, 44, {}, 7, 1, 0, 993, 1, 1, {0,1,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols4.dat"}}, + {{{std::nullopt, {0, 4302}, cyclic_prefix::NORMAL, 51, 1, 22, {}, 12, 1, 1, 596, 1, 1, {0,1,2,3,}}, {1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols5.dat"}}, + {{{std::nullopt, {0, 4847}, cyclic_prefix::NORMAL, 51, 1, 0, {}, 11, 1, 8, 379, 2, 1, {0,}}, {1, 1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols6.dat"}}, + {{{std::nullopt, {0, 6635}, cyclic_prefix::NORMAL, 51, 1, 16, {}, 8, 1, 7, 627, 2, 1, {0,1,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols7.dat"}}, + {{{std::nullopt, {0, 4521}, cyclic_prefix::NORMAL, 51, 1, 27, {}, 1, 1, 5, 836, 2, 1, {0,1,2,3,}}, {1, 1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols8.dat"}}, + {{{std::nullopt, {0, 9573}, cyclic_prefix::NORMAL, 51, 1, 11, {}, 3, 1, 1, 727, 1, 0, {0,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols9.dat"}}, + {{{std::nullopt, {0, 3370}, cyclic_prefix::NORMAL, 51, 1, 25, {}, 12, 1, 0, 425, 1, 0, {0,1,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols10.dat"}}, + {{{std::nullopt, {0, 154}, cyclic_prefix::NORMAL, 51, 1, 7, {}, 4, 1, 3, 272, 1, 0, {0,1,2,3,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols11.dat"}}, + {{{std::nullopt, {0, 7636}, cyclic_prefix::NORMAL, 51, 1, 45, {}, 0, 1, 0, 152, 2, 0, {0,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols12.dat"}}, + {{{std::nullopt, {0, 2333}, cyclic_prefix::NORMAL, 51, 1, 18, {}, 10, 1, 9, 816, 2, 0, {0,1,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols13.dat"}}, + {{{std::nullopt, {0, 7914}, cyclic_prefix::NORMAL, 51, 1, 8, {}, 8, 1, 6, 646, 2, 0, {0,1,2,3,}}, {1, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols14.dat"}}, + {{{std::nullopt, {0, 9634}, cyclic_prefix::NORMAL, 51, 1, 32, {}, 6, 2, 9, 631, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols15.dat"}}, + {{{std::nullopt, {0, 1489}, cyclic_prefix::NORMAL, 51, 1, 50, {}, 11, 2, 8, 527, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols16.dat"}}, + {{{std::nullopt, {0, 8182}, cyclic_prefix::NORMAL, 51, 1, 40, {}, 3, 2, 2, 1007, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols17.dat"}}, + {{{std::nullopt, {0, 4745}, cyclic_prefix::NORMAL, 51, 1, 1, {}, 0, 2, 9, 525, 1, 1, {0,}}, {1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols18.dat"}}, + {{{std::nullopt, {0, 3781}, cyclic_prefix::NORMAL, 51, 1, 2, {}, 8, 2, 4, 38, 1, 1, {0,1,}}, {1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols19.dat"}}, + {{{std::nullopt, {0, 2014}, cyclic_prefix::NORMAL, 51, 1, 47, {}, 8, 2, 10, 938, 1, 1, {0,1,2,3,}}, {1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols20.dat"}}, + {{{std::nullopt, {0, 2900}, cyclic_prefix::NORMAL, 51, 1, 12, {}, 2, 2, 9, 624, 2, 1, {0,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols21.dat"}}, + {{{std::nullopt, {0, 5526}, cyclic_prefix::NORMAL, 51, 1, 27, {}, 7, 2, 10, 46, 2, 1, {0,1,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols22.dat"}}, + {{{std::nullopt, {0, 5982}, cyclic_prefix::NORMAL, 51, 1, 28, {}, 3, 2, 4, 226, 2, 1, {0,1,2,3,}}, {1, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols23.dat"}}, + {{{std::nullopt, {0, 967}, cyclic_prefix::NORMAL, 51, 1, 20, {}, 10, 2, 4, 746, 1, 0, {0,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols24.dat"}}, + {{{std::nullopt, {0, 3223}, cyclic_prefix::NORMAL, 51, 1, 15, {}, 4, 2, 5, 887, 1, 0, {0,1,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols25.dat"}}, + {{{std::nullopt, {0, 5384}, cyclic_prefix::NORMAL, 51, 1, 3, {}, 12, 2, 0, 63, 1, 0, {0,1,2,3,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols26.dat"}}, + {{{std::nullopt, {0, 3801}, cyclic_prefix::NORMAL, 51, 1, 50, {}, 10, 2, 9, 84, 2, 0, {0,}}, {1, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols27.dat"}}, + {{{std::nullopt, {0, 1080}, cyclic_prefix::NORMAL, 51, 1, 43, {}, 0, 2, 7, 447, 2, 0, {0,1,}}, {0, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols28.dat"}}, + {{{std::nullopt, {0, 1704}, cyclic_prefix::NORMAL, 51, 1, 37, {}, 3, 2, 7, 691, 2, 0, {0,1,2,3,}}, {0, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols29.dat"}}, + {{{std::nullopt, {0, 2838}, cyclic_prefix::NORMAL, 51, 1, 38, {41}, 7, 2, 9, 195, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols30.dat"}}, + {{{std::nullopt, {0, 4643}, cyclic_prefix::NORMAL, 51, 1, 20, {28}, 5, 2, 3, 984, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols31.dat"}}, + {{{std::nullopt, {0, 7840}, cyclic_prefix::NORMAL, 51, 1, 0, {47}, 10, 2, 1, 719, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols32.dat"}}, + {{{std::nullopt, {0, 379}, cyclic_prefix::NORMAL, 51, 1, 3, {50}, 9, 2, 2, 394, 1, 1, {0,}}, {0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols33.dat"}}, + {{{std::nullopt, {0, 9807}, cyclic_prefix::NORMAL, 51, 1, 22, {13}, 3, 2, 5, 811, 1, 1, {0,1,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols34.dat"}}, + {{{std::nullopt, {0, 8793}, cyclic_prefix::NORMAL, 51, 1, 15, {35}, 8, 2, 4, 638, 1, 1, {0,1,2,3,}}, {1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols35.dat"}}, + {{{std::nullopt, {0, 2375}, cyclic_prefix::NORMAL, 51, 1, 49, {25}, 8, 2, 4, 390, 2, 1, {0,}}, {0, 1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols36.dat"}}, + {{{std::nullopt, {0, 3654}, cyclic_prefix::NORMAL, 51, 1, 18, {37}, 9, 2, 11, 200, 2, 1, {0,1,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols37.dat"}}, + {{{std::nullopt, {0, 2807}, cyclic_prefix::NORMAL, 51, 1, 44, {43}, 11, 2, 3, 379, 2, 1, {0,1,2,3,}}, {0, 1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols38.dat"}}, + {{{std::nullopt, {0, 1443}, cyclic_prefix::NORMAL, 51, 1, 45, {21}, 0, 2, 10, 105, 1, 0, {0,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols39.dat"}}, + {{{std::nullopt, {0, 9501}, cyclic_prefix::NORMAL, 51, 1, 11, {9}, 1, 2, 10, 188, 1, 0, {0,1,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols40.dat"}}, + {{{std::nullopt, {0, 2234}, cyclic_prefix::NORMAL, 51, 1, 31, {18}, 9, 2, 1, 483, 1, 0, {0,1,2,3,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols41.dat"}}, + {{{std::nullopt, {0, 6443}, cyclic_prefix::NORMAL, 51, 1, 35, {8}, 10, 2, 8, 768, 2, 0, {0,}}, {0, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols42.dat"}}, + {{{std::nullopt, {0, 6690}, cyclic_prefix::NORMAL, 51, 1, 49, {14}, 10, 2, 10, 747, 2, 0, {0,1,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols43.dat"}}, + {{{std::nullopt, {0, 6257}, cyclic_prefix::NORMAL, 51, 1, 28, {36}, 7, 2, 5, 385, 2, 0, {0,1,2,3,}}, {1, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols44.dat"}}, + {{{std::nullopt, {1, 17146}, cyclic_prefix::NORMAL, 51, 1, 49, {}, 4, 1, 0, 798, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols45.dat"}}, + {{{std::nullopt, {1, 18241}, cyclic_prefix::NORMAL, 51, 1, 2, {}, 12, 1, 10, 709, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols46.dat"}}, + {{{std::nullopt, {1, 861}, cyclic_prefix::NORMAL, 51, 1, 8, {}, 9, 1, 2, 847, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols47.dat"}}, + {{{std::nullopt, {1, 12746}, cyclic_prefix::NORMAL, 51, 1, 49, {}, 11, 1, 8, 622, 1, 1, {0,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols48.dat"}}, + {{{std::nullopt, {1, 15747}, cyclic_prefix::NORMAL, 51, 1, 2, {}, 2, 1, 0, 641, 1, 1, {0,1,}}, {1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols49.dat"}}, + {{{std::nullopt, {1, 3093}, cyclic_prefix::NORMAL, 51, 1, 4, {}, 4, 1, 1, 209, 1, 1, {0,1,2,3,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols50.dat"}}, + {{{std::nullopt, {1, 10210}, cyclic_prefix::NORMAL, 51, 1, 41, {}, 0, 1, 7, 91, 2, 1, {0,}}, {1, 1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols51.dat"}}, + {{{std::nullopt, {1, 19008}, cyclic_prefix::NORMAL, 51, 1, 49, {}, 9, 1, 6, 977, 2, 1, {0,1,}}, {0, 0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols52.dat"}}, + {{{std::nullopt, {1, 10774}, cyclic_prefix::NORMAL, 51, 1, 44, {}, 9, 1, 0, 655, 2, 1, {0,1,2,3,}}, {0, 0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols53.dat"}}, + {{{std::nullopt, {1, 2319}, cyclic_prefix::NORMAL, 51, 1, 42, {}, 8, 1, 4, 2, 1, 0, {0,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols54.dat"}}, + {{{std::nullopt, {1, 5068}, cyclic_prefix::NORMAL, 51, 1, 40, {}, 12, 1, 7, 643, 1, 0, {0,1,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols55.dat"}}, + {{{std::nullopt, {1, 2865}, cyclic_prefix::NORMAL, 51, 1, 3, {}, 1, 1, 4, 653, 1, 0, {0,1,2,3,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols56.dat"}}, + {{{std::nullopt, {1, 10312}, cyclic_prefix::NORMAL, 51, 1, 16, {}, 9, 1, 6, 450, 2, 0, {0,}}, {0, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols57.dat"}}, + {{{std::nullopt, {1, 16333}, cyclic_prefix::NORMAL, 51, 1, 49, {}, 1, 1, 10, 217, 2, 0, {0,1,}}, {1, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols58.dat"}}, + {{{std::nullopt, {1, 7225}, cyclic_prefix::NORMAL, 51, 1, 16, {}, 10, 1, 10, 451, 2, 0, {0,1,2,3,}}, {0, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols59.dat"}}, + {{{std::nullopt, {1, 4932}, cyclic_prefix::NORMAL, 51, 1, 17, {}, 2, 2, 1, 526, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols60.dat"}}, + {{{std::nullopt, {1, 6922}, cyclic_prefix::NORMAL, 51, 1, 31, {}, 5, 2, 5, 676, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols61.dat"}}, + {{{std::nullopt, {1, 6630}, cyclic_prefix::NORMAL, 51, 1, 31, {}, 3, 2, 11, 447, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols62.dat"}}, + {{{std::nullopt, {1, 18420}, cyclic_prefix::NORMAL, 51, 1, 40, {}, 6, 2, 7, 381, 1, 1, {0,}}, {1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols63.dat"}}, + {{{std::nullopt, {1, 9510}, cyclic_prefix::NORMAL, 51, 1, 27, {}, 9, 2, 7, 498, 1, 1, {0,1,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols64.dat"}}, + {{{std::nullopt, {1, 19269}, cyclic_prefix::NORMAL, 51, 1, 28, {}, 3, 2, 0, 819, 1, 1, {0,1,2,3,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols65.dat"}}, + {{{std::nullopt, {1, 11524}, cyclic_prefix::NORMAL, 51, 1, 23, {}, 10, 2, 9, 474, 2, 1, {0,}}, {0, 0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols66.dat"}}, + {{{std::nullopt, {1, 3786}, cyclic_prefix::NORMAL, 51, 1, 32, {}, 6, 2, 3, 570, 2, 1, {0,1,}}, {0, 1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols67.dat"}}, + {{{std::nullopt, {1, 8986}, cyclic_prefix::NORMAL, 51, 1, 26, {}, 1, 2, 3, 862, 2, 1, {0,1,2,3,}}, {0, 0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols68.dat"}}, + {{{std::nullopt, {1, 15387}, cyclic_prefix::NORMAL, 51, 1, 46, {}, 8, 2, 2, 822, 1, 0, {0,}}, {1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols69.dat"}}, + {{{std::nullopt, {1, 17097}, cyclic_prefix::NORMAL, 51, 1, 5, {}, 4, 2, 5, 662, 1, 0, {0,1,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols70.dat"}}, + {{{std::nullopt, {1, 4473}, cyclic_prefix::NORMAL, 51, 1, 46, {}, 7, 2, 9, 35, 1, 0, {0,1,2,3,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols71.dat"}}, + {{{std::nullopt, {1, 14792}, cyclic_prefix::NORMAL, 51, 1, 22, {}, 6, 2, 0, 451, 2, 0, {0,}}, {0, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols72.dat"}}, + {{{std::nullopt, {1, 16115}, cyclic_prefix::NORMAL, 51, 1, 4, {}, 12, 2, 6, 31, 2, 0, {0,1,}}, {0, 1}, {}}, {"test_data/pucch_processor_format0_test_input_symbols73.dat"}}, + {{{std::nullopt, {1, 4329}, cyclic_prefix::NORMAL, 51, 1, 44, {}, 0, 2, 9, 795, 2, 0, {0,1,2,3,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols74.dat"}}, + {{{std::nullopt, {1, 12202}, cyclic_prefix::NORMAL, 51, 1, 28, {11}, 1, 2, 7, 344, 0, 1, {0,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols75.dat"}}, + {{{std::nullopt, {1, 5694}, cyclic_prefix::NORMAL, 51, 1, 7, {18}, 9, 2, 10, 261, 0, 1, {0,1,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols76.dat"}}, + {{{std::nullopt, {1, 19064}, cyclic_prefix::NORMAL, 51, 1, 20, {30}, 1, 2, 5, 456, 0, 1, {0,1,2,3,}}, {}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols77.dat"}}, + {{{std::nullopt, {1, 19137}, cyclic_prefix::NORMAL, 51, 1, 28, {49}, 0, 2, 11, 23, 1, 1, {0,}}, {0}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols78.dat"}}, + {{{std::nullopt, {1, 18417}, cyclic_prefix::NORMAL, 51, 1, 11, {37}, 0, 2, 2, 603, 1, 1, {0,1,}}, {0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols79.dat"}}, + {{{std::nullopt, {1, 8608}, cyclic_prefix::NORMAL, 51, 1, 10, {32}, 1, 2, 11, 943, 1, 1, {0,1,2,3,}}, {1}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols80.dat"}}, + {{{std::nullopt, {1, 136}, cyclic_prefix::NORMAL, 51, 1, 9, {10}, 4, 2, 4, 82, 2, 1, {0,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols81.dat"}}, + {{{std::nullopt, {1, 11273}, cyclic_prefix::NORMAL, 51, 1, 32, {12}, 5, 2, 1, 806, 2, 1, {0,1,}}, {0, 0}, {1}}, {"test_data/pucch_processor_format0_test_input_symbols82.dat"}}, + {{{std::nullopt, {1, 17130}, cyclic_prefix::NORMAL, 51, 1, 31, {23}, 1, 2, 2, 376, 2, 1, {0,1,2,3,}}, {0, 1}, {0}}, {"test_data/pucch_processor_format0_test_input_symbols83.dat"}}, + {{{std::nullopt, {1, 3841}, cyclic_prefix::NORMAL, 51, 1, 36, {33}, 11, 2, 3, 359, 1, 0, {0,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols84.dat"}}, + {{{std::nullopt, {1, 15557}, cyclic_prefix::NORMAL, 51, 1, 42, {23}, 1, 2, 5, 79, 1, 0, {0,1,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols85.dat"}}, + {{{std::nullopt, {1, 19928}, cyclic_prefix::NORMAL, 51, 1, 0, {36}, 8, 2, 3, 98, 1, 0, {0,1,2,3,}}, {0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols86.dat"}}, + {{{std::nullopt, {1, 13669}, cyclic_prefix::NORMAL, 51, 1, 29, {16}, 9, 2, 10, 96, 2, 0, {0,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols87.dat"}}, + {{{std::nullopt, {1, 14472}, cyclic_prefix::NORMAL, 51, 1, 19, {9}, 10, 2, 5, 237, 2, 0, {0,1,}}, {1, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols88.dat"}}, + {{{std::nullopt, {1, 3181}, cyclic_prefix::NORMAL, 51, 1, 33, {32}, 4, 2, 1, 21, 2, 0, {0,1,2,3,}}, {0, 0}, {}}, {"test_data/pucch_processor_format0_test_input_symbols89.dat"}}, + // clang-format on +}; + +} // namespace srsran diff --git a/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_vectortest.cpp b/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_vectortest.cpp new file mode 100644 index 0000000000..2008dcf24c --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pucch_processor_format0_vectortest.cpp @@ -0,0 +1,261 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "pucch_detector_test_doubles.h" +#include "pucch_processor_format0_test_data.h" +#include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" +#include "srsran/srsvec/compare.h" +#include "srsran/support/complex_normal_random.h" +#include + +using namespace srsran; + +namespace srsran { + +std::ostream& operator<<(std::ostream& os, const pucch_processor::format0_configuration& config) +{ + return os << fmt::format("{}", config); +} + +std::ostream& operator<<(std::ostream& os, const test_case_t& tc) +{ + os << tc.entry.config; + return os; +} + +std::ostream& operator<<(std::ostream& os, const uci_status& status) +{ + switch (status) { + case uci_status::unknown: + return os << "unknown"; + case uci_status::valid: + return os << "valid"; + case uci_status::invalid: + default: + return os << "invalid"; + } +} + +std::ostream& operator<<(std::ostream& os, span data) +{ + return os << fmt::format("{}", data); +} + +} // namespace srsran + +namespace { + +using PucchProcessorFormat0Param = test_case_t; + +class PucchProcessorFormat0Fixture : public ::testing::TestWithParam +{ +protected: + static void SetUpTestSuite() + { + if (factory) { + return; + } + + std::shared_ptr lpg_factory = create_low_papr_sequence_generator_sw_factory(); + ASSERT_NE(lpg_factory, nullptr); + + std::shared_ptr lpc_factory = + create_low_papr_sequence_collection_sw_factory(lpg_factory); + ASSERT_NE(lpc_factory, nullptr) << "Cannot create low PAPR sequence collection factory."; + + std::shared_ptr prg_factory = create_pseudo_random_generator_sw_factory(); + ASSERT_NE(prg_factory, nullptr) << "Cannot create PRG factory."; + + std::shared_ptr dft_factory = create_dft_processor_factory_fftw_slow(); + if (!dft_factory) { + dft_factory = create_dft_processor_factory_generic(); + } + ASSERT_NE(dft_factory, nullptr) << "Cannot create DFT factory."; + + std::shared_ptr ta_estimator_factory = + create_time_alignment_estimator_dft_factory(dft_factory); + ASSERT_NE(ta_estimator_factory, nullptr) << "Cannot create TA estimator factory."; + + // Create channel estimator factory. + std::shared_ptr port_chan_estimator_factory = + create_port_channel_estimator_factory_sw(ta_estimator_factory); + ASSERT_NE(port_chan_estimator_factory, nullptr) << "Cannot create port channel estimator factory."; + + std::shared_ptr estimator_factory = + create_dmrs_pucch_estimator_factory_sw(prg_factory, lpc_factory, port_chan_estimator_factory); + ASSERT_NE(estimator_factory, nullptr) << "Cannot create DM-RS PUCCH estimator factory."; + + // Create factories required by the PUCCH demodulator factory. + std::shared_ptr equalizer_factory = create_channel_equalizer_factory_zf(); + ASSERT_NE(equalizer_factory, nullptr) << "Cannot create equalizer factory."; + + std::shared_ptr detector_factory = + create_pucch_detector_factory_sw(lpc_factory, prg_factory, equalizer_factory); + ASSERT_NE(detector_factory, nullptr); + + std::shared_ptr demod_factory = create_channel_modulation_sw_factory(); + ASSERT_NE(demod_factory, nullptr) << "Cannot create channel modulation factory."; + + // Create PUCCH demodulator factory. + std::shared_ptr pucch_demod_factory = + create_pucch_demodulator_factory_sw(equalizer_factory, demod_factory, prg_factory); + ASSERT_NE(pucch_demod_factory, nullptr) << "Cannot create PUCCH demodulator factory."; + + // Create short block detector factory. + std::shared_ptr short_block_det_factory = create_short_block_detector_factory_sw(); + ASSERT_NE(short_block_det_factory, nullptr) << "Cannot create short block detector factory."; + + // Create polar decoder factory. + std::shared_ptr polar_dec_factory = create_polar_factory_sw(); + ASSERT_NE(polar_dec_factory, nullptr) << "Invalid polar decoder factory."; + + // Create CRC calculator factory. + std::shared_ptr crc_calc_factory = create_crc_calculator_factory_sw("auto"); + ASSERT_NE(crc_calc_factory, nullptr) << "Invalid CRC calculator factory."; + + // Create UCI decoder factory. + std::shared_ptr uci_dec_factory = + create_uci_decoder_factory_generic(short_block_det_factory, polar_dec_factory, crc_calc_factory); + ASSERT_NE(uci_dec_factory, nullptr) << "Cannot create UCI decoder factory."; + + channel_estimate::channel_estimate_dimensions channel_estimate_dimensions; + channel_estimate_dimensions.nof_tx_layers = 1; + channel_estimate_dimensions.nof_rx_ports = 4; + channel_estimate_dimensions.nof_symbols = MAX_NSYMB_PER_SLOT; + channel_estimate_dimensions.nof_prb = MAX_RB; + + factory = create_pucch_processor_factory_sw( + estimator_factory, detector_factory, pucch_demod_factory, uci_dec_factory, channel_estimate_dimensions); + ASSERT_NE(factory, nullptr); + } + + void SetUp() override + { + ASSERT_NE(factory, nullptr); + processor = factory->create(); + ASSERT_NE(processor, nullptr) << "Could not create PUCCH processor."; + validator = factory->create_validator(); + ASSERT_NE(validator, nullptr) << "Could not create PUCCH validator."; + } + + static std::shared_ptr factory; + + // PUCCH processor. + std::unique_ptr processor; + // PUCCH processor validator. + std::unique_ptr validator; +}; + +std::shared_ptr PucchProcessorFormat0Fixture::factory; + +TEST_P(PucchProcessorFormat0Fixture, FromVector) +{ + // Prepare resource grid. + resource_grid_reader_spy grid; + grid.write(GetParam().grid.read()); + + const PucchProcessorFormat0Param& param = GetParam(); + + const pucch_entry& entry = param.entry; + // Make sure configuration is valid. + ASSERT_TRUE(validator->is_valid(entry.config)); + + pucch_processor_result result = processor->process(grid, entry.config); + + // Check channel state information. + // Time alignment shouldn't exceed plus minus 3 us. + std::optional time_aligment = result.csi.get_time_alignment(); + ASSERT_FALSE(time_aligment.has_value()); + // EPRE depends on the number of entries. + std::optional epre_dB = result.csi.get_epre_dB(); + ASSERT_TRUE(epre_dB.has_value()); + ASSERT_NEAR(epre_dB.value(), 0.0, 0.2); + // SINR should be larger than -5 dB. + std::optional sinr_dB = result.csi.get_sinr_dB(); + ASSERT_TRUE(sinr_dB.has_value()); + ASSERT_GT(sinr_dB.value(), -5.0) << "Entry configuration: " << entry.config; + + // The message shall be valid. + ASSERT_EQ(result.message.get_status(), uci_status::valid); + ASSERT_EQ(result.message.get_harq_ack_bits().size(), entry.ack_bits.size()); + if (!entry.ack_bits.empty()) { + ASSERT_EQ(span(result.message.get_harq_ack_bits()), span(entry.ack_bits)); + } + if (entry.sr.has_value()) { + ASSERT_EQ(result.message.get_sr_bits().size(), 1); + ASSERT_EQ(result.message.get_sr_bits().front(), entry.sr.value()); + } else { + ASSERT_TRUE(result.message.get_sr_bits().empty()); + } + ASSERT_TRUE(result.message.get_csi_part1_bits().empty()); + ASSERT_TRUE(result.message.get_csi_part2_bits().empty()); +} + +TEST_P(PucchProcessorFormat0Fixture, FalseAlarm) +{ + std::vector res = GetParam().grid.read(); + + complex_normal_distribution noise = {}; + std::mt19937 rgen(12345); + + unsigned nof_trials = 200; + // Acceptable probability of false alarm. The value is higher than the 1% given by the PUCCH requirements in TS38.104 + // Section 8.3. + // Important: This is just a quick test, longer simulations are needed to properly estimate the PFA. + float acceptable_pfa = 0.1; + + // Prepare resource grid. + resource_grid_reader_spy grid; + unsigned counter = 0; + for (unsigned i = 0; i != nof_trials; ++i) { + grid.reset(); + for (auto& entry : res) { + entry.value = noise(rgen); + } + grid.write(res); + + const PucchProcessorFormat0Param& param = GetParam(); + const pucch_entry& entry = param.entry; + + // Make sure configuration is valid. + ASSERT_TRUE(validator->is_valid(entry.config)); + + pucch_processor_result result = processor->process(grid, entry.config); + + counter += static_cast(result.message.get_status() == uci_status::valid); + } + + // Assert that the probability of false alarm doesn't exceed the acceptable value. + float pfa = static_cast(counter) / nof_trials; + ASSERT_TRUE(pfa <= acceptable_pfa) << fmt::format( + "Probability of false alarms too high: {} while max is {} ({} hits out of {} trials).", + pfa, + acceptable_pfa, + counter, + nof_trials); +} + +INSTANTIATE_TEST_SUITE_P(PucchProcessorFormat0, + PucchProcessorFormat0Fixture, + ::testing::ValuesIn(pucch_processor_format0_test_data)); + +} // namespace diff --git a/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format0_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format0_test.cpp new file mode 100644 index 0000000000..0ba7bafe9b --- /dev/null +++ b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format0_test.cpp @@ -0,0 +1,317 @@ +/* + * + * 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 "../../support/resource_grid_test_doubles.h" +#include "srsran/phy/upper/channel_processors/channel_processor_factories.h" +#include "srsran/phy/upper/channel_processors/channel_processor_formatters.h" +#include "srsran/phy/upper/equalization/equalization_factories.h" +#include "srsran/ran/pucch/pucch_constants.h" +#include "fmt/ostream.h" +#include "gtest/gtest.h" + +using namespace srsran; + +namespace { + +// Maximum channel dimensions used to construct the PUCCH processor. It is irrelevant for Format 0. +static channel_estimate::channel_estimate_dimensions max_dimensions = {1, 1, 1, 1}; + +// Valid PUCCH Format 0 configuration. +const pucch_processor::format0_configuration base_format_0_config = { + // Context. + std::nullopt, + // Slot. + {0, 9}, + // CP. + cyclic_prefix::NORMAL, + // BWP size. + 50, + // BWP start. + 10, + // Starting PRB. + 1, + // Second hop PRB. + {}, + // Start symbol index. + 3, + // Number of OFDM symbols. + 1, + // Initial cyclic shift. + 0, + // N_ID + 0, + // Number of HARQ-ACK bits. + 1, + // SR opportunity. + false, + // Rx Ports. + {0}, +}; + +// Test case parameters structure. +struct test_params { + pucch_processor::format0_configuration config; + std::string assert_message; +}; + +struct test_case_t { + std::function get_test_params; +}; + +std::ostream& operator<<(std::ostream& os, const test_case_t& test_case) +{ + fmt::print(os, "{}", test_case.get_test_params().config); + return os; +} + +// Test cases are implemented as lambda functions that generate and return an invalid PUCCH Format 0 configuration, +// along with the expected assert message. +const std::vector pucch_processor_validator_test_data = { + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.bwp_start_rb = 10; + entry.config.bwp_size_rb = MAX_RB - entry.config.bwp_start_rb + 1; + entry.assert_message = fmt::format( + R"(BWP allocation goes up to PRB {}\, exceeding the configured maximum grid RB size\, i\.e\.\, {}\.)", + entry.config.bwp_start_rb + entry.config.bwp_size_rb, + MAX_RB); + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.starting_prb = entry.config.bwp_size_rb; + entry.assert_message = + fmt::format(R"(PRB allocation within the BWP goes up to PRB {}\, exceeding BWP size\, i\.e\.\, {}\.)", + entry.config.starting_prb + 1, + entry.config.bwp_size_rb); + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.second_hop_prb = entry.config.bwp_size_rb; + entry.assert_message = fmt::format( + R"(Second hop PRB allocation within the BWP goes up to PRB {}\, exceeding BWP size\, i\.e\.\, {}\.)", + entry.config.second_hop_prb.value() + 1, + entry.config.bwp_size_rb); + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.slot = slot_point(2, 0); + entry.config.cp = cyclic_prefix::EXTENDED; + entry.config.nof_symbols = pucch_constants::format0_nof_symbols_range.start(); + entry.config.start_symbol_index = get_nsymb_per_slot(entry.config.cp) - entry.config.nof_symbols + 1; + entry.assert_message = fmt::format( + R"(OFDM symbol allocation goes up to symbol {}\, exceeding the number of symbols in the given slot with {} CP\, i\.e\.\, {}\.)", + entry.config.start_symbol_index + entry.config.nof_symbols, + entry.config.cp.to_string(), + get_nsymb_per_slot(entry.config.cp)); + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.nof_symbols = pucch_constants::format0_nof_symbols_range.stop() + 1; + entry.assert_message = R"(Number of symbols \(i\.e\., 3\) is out of the range \[1\.\.2\]\.)"; + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.initial_cyclic_shift = pucch_constants::format0_initial_cyclic_shift_range.stop() + 1; + entry.assert_message = R"(The initial cyclic shift \(i\.e\., 13\) is out of the range \[0\.\.12\)\.)"; + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.n_id = pucch_constants::n_id_range.stop() + 1; + entry.assert_message = + R"(The sequence hopping identifier \(i\.e\., 1025\) is out of the range \[0\.\.1024\)\.)"; + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.nof_harq_ack = pucch_constants::format0_nof_harq_ack_range.stop() + 1; + entry.assert_message = R"(The number of HARQ-ACK bits \(i\.e\., 3\) is out of the range \[0\.\.2\]\.)"; + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.nof_harq_ack = 0; + entry.config.sr_opportunity = false; + entry.assert_message = R"(No payload\.)"; + return entry; + }, + }, + { + [] { + test_params entry = {}; + entry.config = base_format_0_config; + entry.config.ports = {}; + entry.assert_message = R"(The number of receive ports cannot be zero\.)"; + return entry; + }, + }, +}; + +class PucchProcessorFormat0Fixture : public ::testing::TestWithParam +{ +protected: + static std::unique_ptr pucch_proc; + static std::unique_ptr pucch_validator; + + static void SetUpTestSuite() + { + if (!(pucch_proc && pucch_validator)) { + // Create factories required by the PUCCH demodulator factory. + std::shared_ptr equalizer_factory = create_channel_equalizer_factory_zf(); + ASSERT_NE(equalizer_factory, nullptr) << "Cannot create equalizer factory."; + + std::shared_ptr demod_factory = create_channel_modulation_sw_factory(); + ASSERT_NE(demod_factory, nullptr) << "Cannot create channel modulation factory."; + + std::shared_ptr prg_factory = create_pseudo_random_generator_sw_factory(); + ASSERT_NE(prg_factory, nullptr) << "Cannot create pseudo-random generator factory."; + + // Create PUCCH demodulator factory. + std::shared_ptr pucch_demod_factory = + create_pucch_demodulator_factory_sw(equalizer_factory, demod_factory, prg_factory); + ASSERT_NE(pucch_demod_factory, nullptr) << "Cannot create PUCCH demodulator factory."; + + // Create factories required by the PUCCH channel estimator factory. + std::shared_ptr lpg_factory = + create_low_papr_sequence_generator_sw_factory(); + ASSERT_NE(lpg_factory, nullptr) << "Cannot create low PAPR sequence generator factory."; + + std::shared_ptr lpc_factory = + create_low_papr_sequence_collection_sw_factory(lpg_factory); + ASSERT_NE(lpc_factory, nullptr) << "Cannot create low PAPR sequence collection factory."; + + std::shared_ptr dft_factory = create_dft_processor_factory_fftw_slow(); + if (!dft_factory) { + dft_factory = create_dft_processor_factory_generic(); + } + ASSERT_NE(dft_factory, nullptr) << "Cannot create DFT factory."; + + std::shared_ptr ta_estimator_factory = + create_time_alignment_estimator_dft_factory(dft_factory); + ASSERT_NE(ta_estimator_factory, nullptr) << "Cannot create TA estimator factory."; + + // Create channel estimator factory. + std::shared_ptr port_chan_estimator_factory = + create_port_channel_estimator_factory_sw(ta_estimator_factory); + ASSERT_NE(port_chan_estimator_factory, nullptr) << "Cannot create port channel estimator factory."; + + std::shared_ptr estimator_factory = + create_dmrs_pucch_estimator_factory_sw(prg_factory, lpc_factory, port_chan_estimator_factory); + ASSERT_NE(estimator_factory, nullptr) << "Cannot create DM-RS PUCCH estimator factory."; + + // Create PUCCH detector factory. + std::shared_ptr detector_factory = + create_pucch_detector_factory_sw(lpc_factory, prg_factory, equalizer_factory); + ASSERT_NE(detector_factory, nullptr) << "Cannot create PUCCH detector factory."; + + // Create short block detector factory. + std::shared_ptr short_block_det_factory = create_short_block_detector_factory_sw(); + ASSERT_NE(short_block_det_factory, nullptr) << "Cannot create short block detector factory."; + + // Create polar decoder factory. + std::shared_ptr polar_dec_factory = create_polar_factory_sw(); + ASSERT_NE(polar_dec_factory, nullptr) << "Invalid polar decoder factory."; + + // Create CRC calculator factory. + std::shared_ptr crc_calc_factory = create_crc_calculator_factory_sw("auto"); + ASSERT_NE(crc_calc_factory, nullptr) << "Invalid CRC calculator factory."; + + // Create UCI decoder factory. + std::shared_ptr uci_dec_factory = + create_uci_decoder_factory_generic(short_block_det_factory, polar_dec_factory, crc_calc_factory); + ASSERT_NE(uci_dec_factory, nullptr) << "Cannot create UCI decoder factory."; + + // Create PUCCH processor factory. + std::shared_ptr processor_factory = create_pucch_processor_factory_sw( + estimator_factory, detector_factory, pucch_demod_factory, uci_dec_factory, max_dimensions); + ASSERT_NE(processor_factory, nullptr) << "Cannot create PUCCH processor factory."; + + // Create PUCCH processor. + pucch_proc = processor_factory->create(); + ASSERT_NE(pucch_proc, nullptr) << "Cannot create PUCCH processor."; + + // Create PUCCH processor validator. + pucch_validator = processor_factory->create_validator(); + ASSERT_NE(pucch_validator, nullptr) << "Cannot create PUCCH validator."; + } + } +}; + +std::unique_ptr PucchProcessorFormat0Fixture::pucch_proc; +std::unique_ptr PucchProcessorFormat0Fixture::pucch_validator; + +TEST_P(PucchProcessorFormat0Fixture, PucchProcessorValidatortest) +{ + ASSERT_NE(pucch_proc, nullptr) << "PUCCH processor not created."; + ASSERT_NE(pucch_validator, nullptr) << "PUCCH validator not created."; + + const test_case_t& param = GetParam(); + + // Make sure the configuration is invalid. + ASSERT_FALSE(pucch_validator->is_valid(param.get_test_params().config)); + + // Prepare resource grid. + resource_grid_reader_spy grid; + + // Process PUCCH PDU. +#ifdef ASSERTS_ENABLED + ASSERT_DEATH({ pucch_proc->process(grid, param.get_test_params().config); }, param.get_test_params().assert_message); +#endif // ASSERTS_ENABLED +} + +// Creates test suite that combines all possible parameters. +INSTANTIATE_TEST_SUITE_P(PucchProcessorValidatortest, + PucchProcessorFormat0Fixture, + ::testing::ValuesIn(pucch_processor_validator_test_data)); + +} // namespace diff --git a/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format1_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format1_test.cpp index 01509c2717..990dc15c58 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format1_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format1_test.cpp @@ -181,7 +181,7 @@ const std::vector pucch_processor_validator_test_data = { }, }; -class PucchProcessorFixture : public ::testing::TestWithParam +class PucchProcessorFormat1Fixture : public ::testing::TestWithParam { protected: static std::unique_ptr pucch_proc; @@ -271,10 +271,10 @@ class PucchProcessorFixture : public ::testing::TestWithParam } }; -std::unique_ptr PucchProcessorFixture::pucch_proc; -std::unique_ptr PucchProcessorFixture::pucch_validator; +std::unique_ptr PucchProcessorFormat1Fixture::pucch_proc; +std::unique_ptr PucchProcessorFormat1Fixture::pucch_validator; -TEST_P(PucchProcessorFixture, PucchProcessorValidatortest) +TEST_P(PucchProcessorFormat1Fixture, PucchProcessorValidatortest) { ASSERT_NE(pucch_proc, nullptr) << "PUCCH processor not created."; ASSERT_NE(pucch_validator, nullptr) << "PUCCH validator not created."; @@ -295,7 +295,7 @@ TEST_P(PucchProcessorFixture, PucchProcessorValidatortest) // Creates test suite that combines all possible parameters. INSTANTIATE_TEST_SUITE_P(PucchProcessorValidatortest, - PucchProcessorFixture, + PucchProcessorFormat1Fixture, ::testing::ValuesIn(pucch_processor_validator_test_data)); } // namespace diff --git a/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format2_test.cpp b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format2_test.cpp index 86638c2f3b..2dc30bb4b8 100644 --- a/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format2_test.cpp +++ b/tests/unittests/phy/upper/channel_processors/pucch_processor_validator_format2_test.cpp @@ -237,7 +237,7 @@ const std::vector pucch_processor_validator_test_data = { }, }}; -class PucchProcessorFixture : public ::testing::TestWithParam +class PucchProcessorFormat2Fixture : public ::testing::TestWithParam { protected: static std::unique_ptr pucch_proc; @@ -327,10 +327,10 @@ class PucchProcessorFixture : public ::testing::TestWithParam } }; -std::unique_ptr PucchProcessorFixture::pucch_proc; -std::unique_ptr PucchProcessorFixture::pucch_validator; +std::unique_ptr PucchProcessorFormat2Fixture::pucch_proc; +std::unique_ptr PucchProcessorFormat2Fixture::pucch_validator; -TEST_P(PucchProcessorFixture, PucchProcessorValidatortest) +TEST_P(PucchProcessorFormat2Fixture, PucchProcessorValidatortest) { ASSERT_NE(pucch_proc, nullptr) << "PUCCH processor not created."; ASSERT_NE(pucch_validator, nullptr) << "PUCCH validator not created."; @@ -351,7 +351,7 @@ TEST_P(PucchProcessorFixture, PucchProcessorValidatortest) // Creates test suite that combines all possible parameters. INSTANTIATE_TEST_SUITE_P(PucchProcessorValidatortest, - PucchProcessorFixture, + PucchProcessorFormat2Fixture, ::testing::ValuesIn(pucch_processor_validator_test_data)); } // namespace 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 e0fa3cac26..18e496307e 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 @@ -183,6 +183,10 @@ class PuschProcessorFixture : public ::testing::TestWithParam crc_calculator_factory, const std::string& decoder_type) { + if (decoder_type == "empty") { + return create_pusch_decoder_empty_factory(MAX_RB, pusch_constants::MAX_NOF_LAYERS); + } + if (decoder_type == "generic") { return create_generic_pusch_decoder_factory(crc_calculator_factory); } @@ -341,10 +345,11 @@ class PuschProcessorFixture : public ::testing::TestWithParam(param); - const test_case_context& context = test_case.context; - const pusch_processor::pdu_t& config = context.config; + const PuschProcessorParams& param = GetParam(); + const std::string& decoder_type = std::get<0>(param); + const test_case_t& test_case = std::get<1>(param); + const test_case_context& context = test_case.context; + const pusch_processor::pdu_t& config = context.config; // Prepare resource grid. resource_grid_reader_spy grid; @@ -369,15 +374,22 @@ TEST_P(PuschProcessorFixture, PuschProcessorVectortest) pusch_processor_result_notifier_spy results_notifier; pusch_proc->process(data, std::move(rm_buffer), results_notifier, grid, config); + // The CRC must be KO if the decoder is empty. + bool expected_tb_crc_ok = true; + if (decoder_type == "empty") { + expected_tb_crc_ok = false; + srsvec::zero(expected_data); + } + // Verify UL-SCH decode results. const auto& sch_entries = results_notifier.get_sch_entries(); ASSERT_FALSE(sch_entries.empty()); const auto& sch_entry = sch_entries.front(); - ASSERT_TRUE(sch_entry.data.tb_crc_ok); + ASSERT_EQ(expected_tb_crc_ok, sch_entry.data.tb_crc_ok); ASSERT_EQ(expected_data, data); // Make sure SINR is normal. - std::optional sinr_dB = results_notifier.get_sch_entries().front().csi.get_sinr_dB(); + std::optional sinr_dB = sch_entries.front().csi.get_sinr_dB(); ASSERT_TRUE(sinr_dB.has_value()); ASSERT_TRUE(std::isnormal(sinr_dB.value())); @@ -503,9 +515,9 @@ TEST_P(PuschProcessorFixture, PuschProcessorVectortestZero) INSTANTIATE_TEST_SUITE_P(PuschProcessorVectortest, PuschProcessorFixture, #ifdef HWACC_PUSCH_ENABLED - testing::Combine(testing::Values("generic", "acc100"), + testing::Combine(testing::Values("generic", "empty", "acc100"), #else // HWACC_PUSCH_ENABLED - testing::Combine(testing::Values("generic"), + testing::Combine(testing::Values("generic", "empty"), #endif // HWACC_PUSCH_ENABLED ::testing::ValuesIn(pusch_processor_test_data))); } // namespace diff --git a/tests/unittests/phy/upper/dl_processor_test_data.h b/tests/unittests/phy/upper/dl_processor_test_data.h deleted file mode 100644 index dacff5876e..0000000000 --- a/tests/unittests/phy/upper/dl_processor_test_data.h +++ /dev/null @@ -1,76 +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 - -// This file was generated using the following MATLAB class on 10-02-2023: -// + "srsDLProcessorUnittest.m" - -#include "../support/resource_grid_test_doubles.h" -#include "srsran/phy/upper/channel_processors/pdcch_processor.h" -#include "srsran/phy/upper/channel_processors/pdsch_processor.h" -#include "srsran/support/file_vector.h" - -namespace srsran { - -/// Describes a Resource Grid entry (shorts the type). -using rg_entry = resource_grid_writer_spy::expected_entry_t; - -struct test_model_description { - std::string test_model; - std::string description; - std::string bandwidth; - std::string subcarrier_spacing; - std::string duplex_mode; - std::string standard_version; -}; - -struct pdsch_transmission { - pdsch_processor::pdu_t pdu; - file_vector transport_block; - file_vector data_symbols; - file_vector dmrs_symbols; -}; - -struct pdcch_transmission { - pdcch_processor::pdu_t pdu; - file_vector data_symbols; - file_vector dmrs_symbols; -}; - -struct test_case_t { - test_model_description test_model; - std::vector pdcch; - std::vector pdsch; -}; - -static const std::vector dl_processor_test_data = { - // clang-format off -{{"R.PDSCH.1-1.1", "Reference channel described in TS38.101-4 Table 5.2.3.1.1-3 test 1-1 for testing FDD PDSCH QPSK modulation with target rate of 0.3, mapping Type A and allocated in the full band.", "10MHz", "15kHz", "FDD"}, { {{std::nullopt, {0, 1}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdcch_dmrs_symbols19.dat"}}}, { {{nullopt, {0, 1}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.1_transport_block19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.1_pdsch_dmrs_symbols19.dat"}}}}, -{{"R.PDSCH.1-1.2", "Reference channel described in TS38.101-4 Table 5.2.3.1.1-3 test 1-2 for testing PDSCH QPSK modulation with target rate of 0.3, mapping Type A and allocated in the center 6 RB.", "10MHz", "15kHz", "FDD"}, { {{nullopt, {0, 1}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdcch_dmrs_symbols19.dat"}}}, { {{nullopt, {0, 1}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(23, 6), 2, 12, ldpc_base_graph_type::BG2, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-1.2_transport_block19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-1.2_pdsch_dmrs_symbols19.dat"}}}}, -{{"R.PDSCH.1-4.1", "Reference channel described in TS38.101-4 Table 5.2.3.1.1-3 test 1-3 for testing PDSCH 256QAM modulation with target rate of 0.82, mapping Type A and allocated in the full band.", "10MHz", "15kHz", "FDD"}, { {{nullopt, {0, 1}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdcch_dmrs_symbols19.dat"}}}, { {{nullopt, {0, 1}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block1.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block2.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block3.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block4.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block5.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block6.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block7.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block8.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block9.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block10.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block11.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block12.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block13.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block14.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block15.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block16.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block17.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block18.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-4.1_transport_block19.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-4.1_pdsch_dmrs_symbols19.dat"}}}}, -{{"R.PDSCH.1-2.1", "Reference channel described in TS38.101-4 Table 5.2.3.1.1-3 test 1-4 for testing PDSCH 16QAM modulation with target rate of 0.48, mapping Type A and allocated in the full band.", "10MHz", "15kHz", "FDD"}, { {{nullopt, {0, 1}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdcch_dmrs_symbols19.dat"}}}, { {{nullopt, {0, 1}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block1.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block2.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block3.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block4.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block5.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block6.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block7.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block8.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block9.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block10.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block11.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block12.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block13.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block14.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block15.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block16.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block17.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block18.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-2.1_transport_block19.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-2.1_pdsch_dmrs_symbols19.dat"}}}}, -{{"R.PDSCH.1-8.1", "Reference channel described in TS38.101-4 Table 5.2.3.1.1-3 test 1-5 for testing PDSCH 16QAM modulation with target rate of 0.48, mapping Type A and allocated in the full band. Optimized for HST scenario.", "10MHz", "15kHz", "FDD"}, { {{nullopt, {0, 1}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 0, 1, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {0, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 0, 1, 1, 0, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 8, 8, 0, 0, {1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, cyclic_prefix::NORMAL, {52, 0, 0, 2, {1, 1, 1, 1, 1, 1, 1, 1}, pdcch_processor::cce_to_reg_mapping_type::NON_INTERLEAVED, 6, 2, 0}, {1, 2, 2, 1, 0, 8, 0, 0, {1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0}, {0}}}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdcch_dmrs_symbols19.dat"}}}, { {{nullopt, {0, 1}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block1.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols1.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols1.dat"}}, {{nullopt, {0, 2}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block2.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols2.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols2.dat"}}, {{nullopt, {0, 3}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block3.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols3.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols3.dat"}}, {{nullopt, {0, 4}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block4.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols4.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols4.dat"}}, {{nullopt, {0, 5}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block5.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols5.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols5.dat"}}, {{nullopt, {0, 6}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block6.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols6.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols6.dat"}}, {{nullopt, {0, 7}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block7.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols7.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols7.dat"}}, {{nullopt, {0, 8}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block8.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols8.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols8.dat"}}, {{nullopt, {0, 9}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block9.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols9.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols9.dat"}}, {{nullopt, {0, 10}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block10.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols10.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols10.dat"}}, {{nullopt, {0, 11}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block11.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols11.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols11.dat"}}, {{nullopt, {0, 12}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {{0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0}}, {0, 52, 1, {1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0}, {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0}}}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block12.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols12.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols12.dat"}}, {{nullopt, {0, 13}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block13.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols13.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols13.dat"}}, {{nullopt, {0, 14}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block14.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols14.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols14.dat"}}, {{nullopt, {0, 15}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block15.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols15.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols15.dat"}}, {{nullopt, {0, 16}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block16.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols16.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols16.dat"}}, {{nullopt, {0, 17}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block17.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols17.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols17.dat"}}, {{nullopt, {0, 18}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block18.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols18.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols18.dat"}}, {{nullopt, {0, 19}, 1, 52, 0, cyclic_prefix::NORMAL, {{modulation_scheme::QPSK, 0}}, 1, {0}, pdsch_processor::pdu_t::CRB0, {0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0}, dmrs_type::TYPE1, 0, 0, 1, rb_allocation::make_type1(0, 52), 2, 12, ldpc_base_graph_type::BG1, 3168, {}, 0, 0}, {"test_data/dl_processor_R.PDSCH.1-8.1_transport_block19.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_data_symbols19.dat"}, {"test_data/dl_processor_R.PDSCH.1-8.1_pdsch_dmrs_symbols19.dat"}}}}, - // clang-format on -}; - -} // namespace srsran diff --git a/tests/unittests/ran/csi_report/csi_report_on_pucch_helpers_test.cpp b/tests/unittests/ran/csi_report/csi_report_on_pucch_helpers_test.cpp index 76c1d1b2d5..cb78ce2861 100644 --- a/tests/unittests/ran/csi_report/csi_report_on_pucch_helpers_test.cpp +++ b/tests/unittests/ran/csi_report/csi_report_on_pucch_helpers_test.cpp @@ -40,18 +40,18 @@ auto to_tuple(const csi_report_data& data) bool operator==(const csi_report_pmi& left, const csi_report_pmi& right) { - if (variant_holds_alternative(left.type) && - variant_holds_alternative(right.type)) { - csi_report_pmi::two_antenna_port left2 = variant_get(left.type); - csi_report_pmi::two_antenna_port right2 = variant_get(right.type); + if (std::holds_alternative(left.type) && + std::holds_alternative(right.type)) { + csi_report_pmi::two_antenna_port left2 = std::get(left.type); + csi_report_pmi::two_antenna_port right2 = std::get(right.type); return left2.pmi == right2.pmi; } - if (variant_holds_alternative(left.type) && - variant_holds_alternative(right.type)) { + if (std::holds_alternative(left.type) && + std::holds_alternative(right.type)) { csi_report_pmi::typeI_single_panel_4ports_mode1 left2 = - variant_get(left.type); + std::get(left.type); csi_report_pmi::typeI_single_panel_4ports_mode1 right2 = - variant_get(right.type); + std::get(right.type); return (left2.i_1_1 == right2.i_1_1) && (left2.i_1_3 == right2.i_1_3) && (left2.i_2 == right2.i_2); } diff --git a/tests/unittests/ran/csi_report/csi_report_on_pusch_helpers_test.cpp b/tests/unittests/ran/csi_report/csi_report_on_pusch_helpers_test.cpp index 2e56278c8f..b853c2b3d4 100644 --- a/tests/unittests/ran/csi_report/csi_report_on_pusch_helpers_test.cpp +++ b/tests/unittests/ran/csi_report/csi_report_on_pusch_helpers_test.cpp @@ -39,18 +39,18 @@ auto to_tuple(const csi_report_data& data) bool operator==(const csi_report_pmi& left, const csi_report_pmi& right) { - if (variant_holds_alternative(left.type) && - variant_holds_alternative(right.type)) { - csi_report_pmi::two_antenna_port left2 = variant_get(left.type); - csi_report_pmi::two_antenna_port right2 = variant_get(right.type); + if (std::holds_alternative(left.type) && + std::holds_alternative(right.type)) { + csi_report_pmi::two_antenna_port left2 = std::get(left.type); + csi_report_pmi::two_antenna_port right2 = std::get(right.type); return left2.pmi == right2.pmi; } - if (variant_holds_alternative(left.type) && - variant_holds_alternative(right.type)) { + if (std::holds_alternative(left.type) && + std::holds_alternative(right.type)) { csi_report_pmi::typeI_single_panel_4ports_mode1 left2 = - variant_get(left.type); + std::get(left.type); csi_report_pmi::typeI_single_panel_4ports_mode1 right2 = - variant_get(right.type); + std::get(right.type); return (left2.i_1_1 == right2.i_1_1) && (left2.i_1_3 == right2.i_1_3) && (left2.i_2 == right2.i_2); } diff --git a/tests/unittests/rlc/rlc_pdu_recycler_test.cpp b/tests/unittests/rlc/rlc_pdu_recycler_test.cpp index bc7b3740cc..525562bee0 100644 --- a/tests/unittests/rlc/rlc_pdu_recycler_test.cpp +++ b/tests/unittests/rlc/rlc_pdu_recycler_test.cpp @@ -54,8 +54,8 @@ class rlc_pdu_recycler_test : public ::testing::Test srslog::flush(); } - srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); - rlc_bearer_logger rlc_logger = rlc_bearer_logger("RLC", {0, du_ue_index_t{}, rb_id_t{}, "DL"}); + srslog::basic_logger& logger = srslog::fetch_basic_logger("TEST", false); + rlc_bearer_logger rlc_logger = rlc_bearer_logger("RLC", {gnb_du_id_t::min, du_ue_index_t{}, rb_id_t{}, "DL"}); std::unique_ptr pdu_recycler; manual_task_worker_always_enqueue_tasks other_worker{8}; diff --git a/tests/unittests/rlc/rlc_rx_am_test.cpp b/tests/unittests/rlc/rlc_rx_am_test.cpp index 4dd415df78..d41e6b6d2a 100644 --- a/tests/unittests/rlc/rlc_rx_am_test.cpp +++ b/tests/unittests/rlc/rlc_rx_am_test.cpp @@ -99,7 +99,7 @@ class rlc_rx_am_test : public ::testing::Test, public ::testing::WithParamInterf tester = std::make_unique(config.sn_field_length); // Create RLC AM RX entity - rlc = std::make_unique(0, + rlc = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, diff --git a/tests/unittests/rlc/rlc_rx_tm_test.cpp b/tests/unittests/rlc/rlc_rx_tm_test.cpp index 120013248a..cc955bba2e 100644 --- a/tests/unittests/rlc/rlc_rx_tm_test.cpp +++ b/tests/unittests/rlc/rlc_rx_tm_test.cpp @@ -63,8 +63,13 @@ class rlc_rx_am_test : public ::testing::Test tester = std::make_unique(); // Create RLC AM TX entity - rlc = std::make_unique( - 0, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, make_default_srb0_rlc_config().tm.rx, *tester, true, pcap); + rlc = std::make_unique(gnb_du_id_t::min, + du_ue_index_t::MIN_DU_UE_INDEX, + srb_id_t::srb0, + make_default_srb0_rlc_config().tm.rx, + *tester, + true, + pcap); } void TearDown() override diff --git a/tests/unittests/rlc/rlc_sdu_queue_lockfree_test.cpp b/tests/unittests/rlc/rlc_sdu_queue_lockfree_test.cpp index 6de1a4073a..ebf810f007 100644 --- a/tests/unittests/rlc/rlc_sdu_queue_lockfree_test.cpp +++ b/tests/unittests/rlc/rlc_sdu_queue_lockfree_test.cpp @@ -30,8 +30,8 @@ namespace srsran { void queue_unqueue_test() { - rlc_bearer_logger logger("RLC", {0, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); - test_delimit_logger delimiter{"RLC SDU queue unqueue test"}; + rlc_bearer_logger logger("RLC", {gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); + test_delimit_logger delimiter{"RLC SDU queue unqueue test"}; rlc_sdu_queue_lockfree tx_queue(4096, logger); // Write 1 SDU @@ -62,9 +62,9 @@ void queue_unqueue_test() void full_capacity_test() { - rlc_bearer_logger logger("RLC", {0, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); - test_delimit_logger delimiter{"RLC SDU capacity test"}; - unsigned capacity = 5; + rlc_bearer_logger logger("RLC", {gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); + test_delimit_logger delimiter{"RLC SDU capacity test"}; + unsigned capacity = 5; rlc_sdu_queue_lockfree tx_queue(capacity, logger); // Write Capacity + 1 SDUs @@ -104,10 +104,10 @@ void full_capacity_test() void discard_test() { - rlc_bearer_logger logger("RLC", {0, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); - test_delimit_logger delimiter{"RLC SDU discard test"}; - unsigned capacity = 10; - unsigned n_sdus = capacity; + rlc_bearer_logger logger("RLC", {gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); + test_delimit_logger delimiter{"RLC SDU discard test"}; + unsigned capacity = 10; + unsigned n_sdus = capacity; rlc_sdu_queue_lockfree tx_queue(capacity, logger); // Fill SDU queue with SDUs @@ -147,10 +147,10 @@ void discard_test() void discard_all_test() { - rlc_bearer_logger logger("RLC", {0, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); - test_delimit_logger delimiter{"RLC SDU discard all test"}; - unsigned capacity = 10; - unsigned n_sdus = capacity / 2; + rlc_bearer_logger logger("RLC", {gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, rb_id_t(drb_id_t::drb1), "DL"}); + test_delimit_logger delimiter{"RLC SDU discard all test"}; + unsigned capacity = 10; + unsigned n_sdus = capacity / 2; rlc_sdu_queue_lockfree tx_queue(capacity, logger); // Fill SDU queue with SDUs diff --git a/tests/unittests/rlc/rlc_tx_am_test.cpp b/tests/unittests/rlc/rlc_tx_am_test.cpp index ca36e7044d..d502f9477a 100644 --- a/tests/unittests/rlc/rlc_tx_am_test.cpp +++ b/tests/unittests/rlc/rlc_tx_am_test.cpp @@ -108,7 +108,7 @@ class rlc_tx_am_test : public ::testing::Test, public ::testing::WithParamInterf tester = std::make_unique(config.sn_field_length); // Create RLC AM TX entity - rlc = std::make_unique(0, + rlc = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, diff --git a/tests/unittests/rlc/rlc_tx_tm_test.cpp b/tests/unittests/rlc/rlc_tx_tm_test.cpp index 711e390ae5..9344695a16 100644 --- a/tests/unittests/rlc/rlc_tx_tm_test.cpp +++ b/tests/unittests/rlc/rlc_tx_tm_test.cpp @@ -76,7 +76,7 @@ class rlc_tx_tm_test : public ::testing::Test tester = std::make_unique(); // Create RLC TM TX entity - rlc = std::make_unique(0, + rlc = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, make_default_srb0_rlc_config().tm.tx, diff --git a/tests/unittests/rlc/rlc_um_test.cpp b/tests/unittests/rlc/rlc_um_test.cpp index 24ef539f43..ae6a4961ea 100644 --- a/tests/unittests/rlc/rlc_um_test.cpp +++ b/tests/unittests/rlc/rlc_um_test.cpp @@ -97,7 +97,7 @@ class rlc_um_test : public ::testing::Test, public ::testing::WithParamInterface config.tx.queue_size = 4096; // Create RLC entities - rlc1 = std::make_unique(0, + rlc1 = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, @@ -111,7 +111,7 @@ class rlc_um_test : public ::testing::Test, public ::testing::WithParamInterface pcell_worker, ue_worker, pcap1); - rlc2 = std::make_unique(0, + rlc2 = std::make_unique(gnb_du_id_t::min, du_ue_index_t::MIN_DU_UE_INDEX, srb_id_t::srb0, config, diff --git a/tests/unittests/rrc/rrc_ue_meas_report_test.cpp b/tests/unittests/rrc/rrc_ue_meas_report_test.cpp index 45027abdcb..a32e75e15e 100644 --- a/tests/unittests/rrc/rrc_ue_meas_report_test.cpp +++ b/tests/unittests/rrc/rrc_ue_meas_report_test.cpp @@ -44,7 +44,6 @@ class rrc_ue_meas_report : public rrc_ue_test_helper, public ::testing::Test } }; -/// Test the RRC Reestablishment TEST_F(rrc_ue_meas_report, when_dummy_meas_report_received_then_conversion_successful) { byte_buffer meas_report_pdu = generate_measurement_report_pdu(); @@ -57,8 +56,32 @@ TEST_F(rrc_ue_meas_report, when_dummy_meas_report_received_then_conversion_succe ASSERT_EQ(ul_dcch_msg.msg.c1().type().value, asn1::rrc_nr::ul_dcch_msg_type_c::c1_c_::types_opts::meas_report); rrc_meas_results meas_results = - asn1_to_measurement_results(ul_dcch_msg.msg.c1().meas_report().crit_exts.meas_report().meas_results); + asn1_to_measurement_results(ul_dcch_msg.msg.c1().meas_report().crit_exts.meas_report().meas_results, logger); // check if the meas results conversion was successful check_meas_results(meas_results); } + +TEST_F(rrc_ue_meas_report, when_invalid_meas_report_received_then_meas_results_are_empty) +{ + std::vector fuzzed_data{0x01, 0xe2, 0x02, 0xdd, 0x00, 0x5d, 0x5e, 0xaf, 0xe0, 0x51, 0x07, 0x20, 0x04, + 0x03, 0xe4, 0x82, 0x07, 0x00, 0x00, 0x02, 0x00, 0x02, 0xbd, 0x60, 0x6c, 0x78, + 0x0a, 0xd5, 0x2f, 0x70, 0x70, 0xfb, 0xa3, 0xc0, 0xc1, 0xb9, 0xbf, 0x01, 0x43, + 0xe0, 0x01, 0x66, 0x07, 0x08, 0xcf, 0x92, 0x81, 0xc0, 0x40, 0x00, 0x60, 0x00, + 0x01, 0x00, 0x81, 0x05, 0x25, 0xb9, 0xb4, 0xad, 0x44, 0x06, 0xa0, 0xd3, 0x0f, + 0x4a, 0x6f, 0x10, 0x20, 0xc0, 0xfc, 0x00, 0xfb, 0x85, 0xcd, 0x8e, 0x00, 0x00}; + srsran::byte_buffer meas_report_pdu = srsran::byte_buffer::create(fuzzed_data).value(); + + // Parse UL-CCCH + asn1::rrc_nr::ul_dcch_msg_s ul_dcch_msg; + asn1::cbit_ref bref({meas_report_pdu.begin(), meas_report_pdu.end()}); + ASSERT_EQ(ul_dcch_msg.unpack(bref), asn1::SRSASN_SUCCESS); + ASSERT_EQ(ul_dcch_msg.msg.type().value, asn1::rrc_nr::ul_dcch_msg_type_c::types_opts::c1); + ASSERT_EQ(ul_dcch_msg.msg.c1().type().value, asn1::rrc_nr::ul_dcch_msg_type_c::c1_c_::types_opts::meas_report); + + rrc_meas_results meas_results = + asn1_to_measurement_results(ul_dcch_msg.msg.c1().meas_report().crit_exts.meas_report().meas_results, logger); + + ASSERT_TRUE(meas_results.meas_result_neigh_cells.has_value() and + meas_results.meas_result_neigh_cells->meas_result_list_nr.empty()); +} diff --git a/tests/unittests/rrc/rrc_ue_test_messages.cpp b/tests/unittests/rrc/rrc_ue_test_messages.cpp index 988c73d9c8..33133e6631 100644 --- a/tests/unittests/rrc/rrc_ue_test_messages.cpp +++ b/tests/unittests/rrc/rrc_ue_test_messages.cpp @@ -33,7 +33,7 @@ using namespace srs_cu_cp; security::sec_key srsran::srs_cu_cp::make_sec_key(std::string hex_str) { - byte_buffer key_buf = make_byte_buffer(hex_str); + byte_buffer key_buf = make_byte_buffer(hex_str).value(); security::sec_key key = {}; std::copy(key_buf.begin(), key_buf.end(), key.begin()); return key; @@ -41,7 +41,7 @@ security::sec_key srsran::srs_cu_cp::make_sec_key(std::string hex_str) security::sec_128_key srsran::srs_cu_cp::make_sec_128_key(std::string hex_str) { - byte_buffer key_buf = make_byte_buffer(hex_str); + byte_buffer key_buf = make_byte_buffer(hex_str).value(); security::sec_128_key key = {}; std::copy(key_buf.begin(), key_buf.end(), key.begin()); return key; @@ -184,10 +184,10 @@ rrc_reconfiguration_procedure_request srsran::srs_cu_cp::generate_rrc_reconfigur rrc_recfg_v1530_ies non_crit_ext; // add dummy NAS PDU - non_crit_ext.ded_nas_msg_list.push_back(make_byte_buffer("aabbcc")); + non_crit_ext.ded_nas_msg_list.push_back(make_byte_buffer("aabbcc").value()); // add dummy master cell group config - non_crit_ext.master_cell_group = make_byte_buffer("deadbeef"); + non_crit_ext.master_cell_group = make_byte_buffer("deadbeef").value(); args.non_crit_ext = non_crit_ext; diff --git a/tests/unittests/scheduler/common_scheduling/CMakeLists.txt b/tests/unittests/scheduler/common_scheduling/CMakeLists.txt index c3acef694d..5c16e960b7 100644 --- a/tests/unittests/scheduler/common_scheduling/CMakeLists.txt +++ b/tests/unittests/scheduler/common_scheduling/CMakeLists.txt @@ -32,4 +32,4 @@ target_link_libraries(common_scheduler_test scheduler_test_suite gtest gtest_main) -gtest_discover_tests(common_scheduler_test) +add_test(common_scheduler_test common_scheduler_test) diff --git a/tests/unittests/scheduler/common_scheduling/ra_scheduler_test.cpp b/tests/unittests/scheduler/common_scheduling/ra_scheduler_test.cpp index 5e2f475d4f..dfa4aaa4ef 100644 --- a/tests/unittests/scheduler/common_scheduling/ra_scheduler_test.cpp +++ b/tests/unittests/scheduler/common_scheduling/ra_scheduler_test.cpp @@ -51,13 +51,13 @@ std::ostream& operator<<(std::ostream& out, const test_params& params) } /// Common tester class used by FDD and TDD unit tests for the RA scheduler. -class base_ra_scheduler_test : public ::testing::TestWithParam +class base_ra_scheduler_test { protected: static constexpr unsigned tx_rx_delay = 2U; - base_ra_scheduler_test(duplex_mode dplx_mode) : - params(GetParam()), cell_cfg(sched_cfg, get_sched_req(dplx_mode, params)) + base_ra_scheduler_test(duplex_mode dplx_mode, const test_params& params_) : + params(params_), cell_cfg(sched_cfg, get_sched_req(dplx_mode, params)), ev_logger(to_du_cell_index(0), cell_cfg.pci) { mac_logger.set_level(srslog::basic_levels::debug); test_logger.set_level(srslog::basic_levels::info); @@ -76,9 +76,12 @@ class base_ra_scheduler_test : public ::testing::TestWithParam max_k_value = pusch.k2 + max_msg3_delta; } } + + // Run slot once so that the resource grid gets initialized with the initial slot. + run_slot(); } - ~base_ra_scheduler_test() override + ~base_ra_scheduler_test() { // Log pending allocations before finishing test. for (unsigned i = 0; i != max_k_value; ++i) { @@ -327,13 +330,13 @@ class base_ra_scheduler_test : public ::testing::TestWithParam /// \brief For a slot to be valid for RAR in TDD mode, the RAR PDCCH, RAR PDSCH and Msg3 PUSCH must fall in DL, DL /// and UL slots, respectively. - bool is_slot_valid_for_rar_pdcch() const + bool is_slot_valid_for_rar_pdcch(unsigned delay = 0) const { if (not cell_cfg.is_tdd()) { // FDD case. return true; } - slot_point pdcch_slot = res_grid[0].slot; + slot_point pdcch_slot = res_grid[delay].slot; if (not cell_cfg.is_dl_enabled(pdcch_slot)) { // slot for PDCCH is not DL slot. @@ -410,7 +413,7 @@ class base_ra_scheduler_test : public ::testing::TestWithParam return true; } - bool is_in_rar_window(slot_point rach_slot_rx) const + slot_interval get_rar_window(slot_point rach_slot_rx) const { slot_point rar_win_start; for (unsigned i = 1; i != rach_slot_rx.nof_slots_per_frame(); ++i) { @@ -419,9 +422,13 @@ class base_ra_scheduler_test : public ::testing::TestWithParam break; } } - slot_interval rar_win = {rar_win_start, - rar_win_start + - cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common->rach_cfg_generic.ra_resp_window}; + return {rar_win_start, + rar_win_start + cell_cfg.ul_cfg_common.init_ul_bwp.rach_cfg_common->rach_cfg_generic.ra_resp_window}; + } + + bool is_in_rar_window(slot_point rach_slot_rx) const + { + slot_interval rar_win = get_rar_window(rach_slot_rx); return rar_win.contains(result_slot_tx()); } @@ -453,25 +460,25 @@ class base_ra_scheduler_test : public ::testing::TestWithParam unsigned max_k_value = 0; }; -class fdd_test : public base_ra_scheduler_test +class ra_scheduler_fdd_test : public base_ra_scheduler_test, public ::testing::TestWithParam { protected: - fdd_test() : base_ra_scheduler_test(duplex_mode::FDD) {} + ra_scheduler_fdd_test() : base_ra_scheduler_test(duplex_mode::FDD, GetParam()) {} }; -class tdd_test : public base_ra_scheduler_test +class ra_scheduler_tdd_test : public base_ra_scheduler_test, public ::testing::TestWithParam { protected: - tdd_test() : base_ra_scheduler_test(duplex_mode::TDD) {} + ra_scheduler_tdd_test() : base_ra_scheduler_test(duplex_mode::TDD, GetParam()) {} }; /// This test verifies that the cell resource grid remains empty when no RACH indications arrive to the RA scheduler. -TEST_P(fdd_test, when_no_rach_indication_received_then_no_rar_allocated) +TEST_P(ra_scheduler_fdd_test, when_no_rach_indication_received_then_no_rar_allocated) { run_slot(); ASSERT_TRUE(no_rar_grants_scheduled()); } -TEST_P(tdd_test, when_no_rach_indication_received_then_no_rar_allocated) +TEST_P(ra_scheduler_tdd_test, when_no_rach_indication_received_then_no_rar_allocated) { run_slot(); ASSERT_TRUE(no_rar_grants_scheduled()); @@ -480,7 +487,7 @@ TEST_P(tdd_test, when_no_rach_indication_received_then_no_rar_allocated) /// This test verifies the correct scheduling of a RAR and Msg3s in an FDD frame, when multiple RACH preambles /// are received for the same PRACH occasion. /// The scheduler is expected to allocate one RAR and multiple MSG3 grants. -TEST_P(fdd_test, schedules_one_rar_per_slot_when_multi_preambles_with_same_prach_occasion) +TEST_P(ra_scheduler_fdd_test, schedules_one_rar_per_slot_when_multi_preambles_with_same_prach_occasion) { // Forward single RACH occasion with multiple preambles. rach_indication_message one_rach = @@ -526,7 +533,7 @@ TEST_P(fdd_test, schedules_one_rar_per_slot_when_multi_preambles_with_same_prach /// This test verifies the correct scheduling of a RAR and Msg3 in an FDD frame, when multiple RACH Preambles are /// received, each in a different PRACH occasion. /// The scheduler is expected to allocate several RARs (with different RA-RNTIs), each composed by one Msg3. -TEST_P(fdd_test, schedules_multiple_rars_per_slot_when_multiple_prach_occasions) +TEST_P(ra_scheduler_fdd_test, schedules_multiple_rars_per_slot_when_multiple_prach_occasions) { // Forward multiple RACH occasions with one preamble. unsigned nof_occasions = test_rgen::uniform_int(1, MAX_PRACH_OCCASIONS_PER_SLOT); @@ -566,7 +573,7 @@ TEST_P(fdd_test, schedules_multiple_rars_per_slot_when_multiple_prach_occasions) /// scheduling opportunities where the RAR PDCCH and PDSCH fall in a DL slot and the Msg3 falls in an UL slot. /// The scheduler will need to search in the PUSCH-TimeDomainResourceList provided in the cell config for a k2 /// value that allows it to fit the Msg3 in an UL slot. -TEST_P(tdd_test, schedules_rar_in_valid_slots_when_tdd) +TEST_P(ra_scheduler_tdd_test, schedules_rar_in_valid_slots_when_tdd) { // Forward single RACH occasion with multiple preambles. // Note: The number of preambles is small enough to fit all grants in one slot. @@ -598,7 +605,7 @@ TEST_P(tdd_test, schedules_rar_in_valid_slots_when_tdd) } } -TEST_P(tdd_test, schedules_msg3_retx_in_valid_slots_when_tdd) +TEST_P(ra_scheduler_tdd_test, schedules_msg3_retx_in_valid_slots_when_tdd) { // Forward single RACH occasion with multiple preambles. // Note: The number of preambles is small enough to fit all grants in one slot. @@ -630,8 +637,59 @@ TEST_P(tdd_test, schedules_msg3_retx_in_valid_slots_when_tdd) ASSERT_GT(msg3_retx_count, 0); } +TEST_P(ra_scheduler_tdd_test, when_no_rbs_are_available_then_rar_is_scheduled_in_following_slot) +{ + // Forward RACH indication to scheduler. + run_slot_until_next_rach_opportunity(); + rach_indication_message rach_ind = create_rach_indication(1); + handle_rach_indication(rach_ind); + slot_interval rar_win = get_rar_window(rach_ind.slot_rx); + + // Forbid PDCCH alloc in the next slot. + this->pdcch_sch.fail_pdcch_alloc_cond = [next_sl = res_grid[1].slot](slot_point pdcch_slot) { + return pdcch_slot == next_sl; + }; + + // Process slot and schedule RAR in a future slot. + run_slot(); + + // Given that the resource grid was already filled for this slot, no RAR should be scheduled. + ASSERT_TRUE(res_grid[0].result.dl.dl_pdcchs.empty()); + + int td_res = -1; + unsigned n = 1; + for (; rar_win.contains(res_grid[0].slot + n); ++n) { + if (not is_slot_valid_for_rar_pdcch(n)) { + ASSERT_TRUE(scheduled_dl_pdcchs().empty()) + << fmt::format("RAR PDCCH allocated in invalid slot {}", result_slot_tx()); + continue; + } + + // RAR PDCCH scheduled. + ASSERT_EQ(res_grid[n].result.dl.dl_pdcchs.size(), 1); + td_res = res_grid[n].result.dl.dl_pdcchs[0].dci.ra_f1_0.time_resource; + break; + } + + ASSERT_GE(td_res, 0) << "RAR PDCCH not found"; + for (unsigned i = 0; i != n; ++i) { + // Update current slot to the slot when PDCCH was scheduled. + run_slot(); + } + // RAR PDSCH allocated. + span rars = scheduled_rars(td_res); + ASSERT_EQ(rars.size(), 1); + unsigned nof_grants = 0; + ASSERT_TRUE(rars_consistent_with_rach_indication(rars, rach_ind, nof_grants)); + ASSERT_EQ(nof_grants, 1); + ASSERT_EQ(nof_grants, rars[0].grants.size()) << "All scheduled RAR grants must be for the provided occasion"; + // Msg3 scheduled. + ASSERT_EQ(scheduled_msg3_newtxs(rars[0].grants[0].time_resource_assignment).size(), nof_grants) + << "Number of Msg3 PUSCHs must match number of RARs"; +} + INSTANTIATE_TEST_SUITE_P(ra_scheduler, - fdd_test, + ra_scheduler_fdd_test, ::testing::Values(test_params{.scs = subcarrier_spacing::kHz15, .k0 = 0, .k2s = {2}}, test_params{.scs = subcarrier_spacing::kHz15, .k0 = 2, .k2s = {2}}, test_params{.scs = subcarrier_spacing::kHz15, .k0 = 4, .k2s = {2}}, @@ -640,7 +698,7 @@ INSTANTIATE_TEST_SUITE_P(ra_scheduler, test_params{.scs = subcarrier_spacing::kHz30, .k0 = 4, .k2s = {2}})); INSTANTIATE_TEST_SUITE_P(ra_scheduler, - tdd_test, + ra_scheduler_tdd_test, ::testing::Values(test_params{.scs = subcarrier_spacing::kHz15, .k0 = 0, .k2s = {2, 4}}, test_params{.scs = subcarrier_spacing::kHz15, .k0 = 2, .k2s = {2, 4}}, test_params{.scs = subcarrier_spacing::kHz30, .k0 = 0, .k2s = {2, 4}}, diff --git a/tests/unittests/scheduler/multiple_ue_sched_test.cpp b/tests/unittests/scheduler/multiple_ue_sched_test.cpp index c52dbd0db0..aebdf6dbe7 100644 --- a/tests/unittests/scheduler/multiple_ue_sched_test.cpp +++ b/tests/unittests/scheduler/multiple_ue_sched_test.cpp @@ -980,7 +980,7 @@ TEST_P(multiple_ue_sched_tester, dl_dci_format_1_1_test) pdcch_grant->ctx.context.ss_id); } else { const auto dci_fmt = - variant_get(ss_cfg->get_monitored_dci_formats()); + std::get(ss_cfg->get_monitored_dci_formats()); if (dci_fmt == srsran::search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0) { ASSERT_TRUE(pdcch_grant->dci.type == srsran::dci_dl_rnti_config_type::c_rnti_f1_0) << fmt::format( "Condition failed for UE with c-rnti={} and SS id={}", test_ue.crnti, pdcch_grant->ctx.context.ss_id); @@ -1107,7 +1107,7 @@ TEST_P(multiple_ue_sched_tester, ul_dci_format_0_1_test) pdcch_grant->ctx.context.ss_id); } else { const auto dci_fmt = - variant_get(ss_cfg->get_monitored_dci_formats()); + std::get(ss_cfg->get_monitored_dci_formats()); if (dci_fmt == srsran::search_space_configuration::ue_specific_dci_format::f0_0_and_f1_0) { ASSERT_TRUE(pdcch_grant->dci.type == srsran::dci_ul_rnti_config_type::c_rnti_f0_0) << fmt::format( "Condition failed for UE with c-rnti={} and SS id={}", test_ue.crnti, pdcch_grant->ctx.context.ss_id); diff --git a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp index 9761fd0661..7103abd236 100644 --- a/tests/unittests/scheduler/policy/scheduler_policy_test.cpp +++ b/tests/unittests/scheduler/policy/scheduler_policy_test.cpp @@ -21,8 +21,12 @@ */ #include "../test_utils/config_generators.h" +#include "lib/scheduler/pdcch_scheduling/pdcch_resource_allocator_impl.h" #include "lib/scheduler/policy/scheduler_time_rr.h" +#include "lib/scheduler/pucch_scheduling/pucch_allocator_impl.h" +#include "lib/scheduler/uci_scheduling/uci_allocator_impl.h" #include "lib/scheduler/ue_scheduling/ue.h" +#include "lib/scheduler/ue_scheduling/ue_cell_grid_allocator.h" #include "tests/unittests/scheduler/test_utils/dummy_test_components.h" #include "srsran/du/du_cell_config_helpers.h" #include "srsran/support/error_handling.h" @@ -33,45 +37,6 @@ using namespace srsran; enum class policy_type { time_rr }; -class dummy_pdsch_allocator : public ue_pdsch_allocator -{ -public: - cell_resource_allocator& res_grid; - std::vector last_grants; - - dummy_pdsch_allocator(cell_resource_allocator& res_grid_) : res_grid(res_grid_) {} - - alloc_outcome allocate_dl_grant(const ue_pdsch_grant& grant) override - { - last_grants.push_back(grant); - const auto& cell_cfg_cmn = grant.user->get_pcell().cfg().cell_cfg_common; - res_grid[0].dl_res_grid.fill(grant_info{ - cell_cfg_cmn.dl_cfg_common.init_dl_bwp.generic_params.scs, - cell_cfg_cmn.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list[grant.time_res_index].symbols, - grant.crbs}); - return alloc_outcome::success; - } -}; - -class dummy_pusch_allocator : public ue_pusch_allocator -{ -public: - cell_resource_allocator& res_grid; - std::vector last_grants; - - dummy_pusch_allocator(cell_resource_allocator& res_grid_) : res_grid(res_grid_) {} - - alloc_outcome allocate_ul_grant(const ue_pusch_grant& grant) override - { - last_grants.push_back(grant); - const auto& cell_cfg_cmn = grant.user->get_pcell().cfg().cell_cfg_common; - unsigned k2 = cell_cfg_cmn.ul_cfg_common.init_ul_bwp.pusch_cfg_common->pusch_td_alloc_list[grant.time_res_index].k2; - res_grid[k2].ul_res_grid.fill( - grant_info{cell_cfg_cmn.ul_cfg_common.init_ul_bwp.generic_params.scs, {0, 14}, grant.crbs}); - return alloc_outcome::success; - } -}; - class base_scheduler_policy_test { protected: @@ -87,6 +52,7 @@ class base_scheduler_policy_test logger.set_level(srslog::basic_levels::debug); srslog::init(); + grid_alloc.add_cell(to_du_cell_index(0), pdcch_alloc, uci_alloc, res_grid); ue_res_grid.add_cell(res_grid); switch (policy) { @@ -102,18 +68,16 @@ class base_scheduler_policy_test void run_slot() { - pdsch_alloc.last_grants.clear(); - pusch_alloc.last_grants.clear(); + grid_alloc.slot_indication(next_slot); res_grid.slot_indication(next_slot); + pdcch_alloc.slot_indication(next_slot); + pucch_alloc.slot_indication(next_slot); + uci_alloc.slot_indication(next_slot); - unsigned dl_idx = next_slot.to_uint() % 2; - for (unsigned i = 0; i != 2; ++i) { - if (dl_idx == i) { - sched->dl_sched(pdsch_alloc, ue_res_grid, ues); - } else { - sched->ul_sched(pusch_alloc, ue_res_grid, ues); - } + if (cell_cfg.is_dl_enabled(next_slot)) { + sched->dl_sched(grid_alloc, ue_res_grid, ues); + sched->ul_sched(grid_alloc, ue_res_grid, ues); } next_slot++; @@ -184,11 +148,13 @@ class base_scheduler_policy_test scheduler_ue_metrics_dummy_notifier metrics_notif; scheduler_harq_timeout_dummy_handler harq_timeout_handler; - cell_resource_allocator res_grid{cell_cfg}; - ue_resource_grid_view ue_res_grid; - dummy_pdsch_allocator pdsch_alloc{res_grid}; - dummy_pusch_allocator pusch_alloc{res_grid}; - ue_repository ues; + cell_resource_allocator res_grid{cell_cfg}; + pdcch_resource_allocator_impl pdcch_alloc{cell_cfg}; + pucch_allocator_impl pucch_alloc{cell_cfg, sched_cfg.ue.max_pucchs_per_slot, sched_cfg.ue.max_ul_grants_per_slot}; + uci_allocator_impl uci_alloc{pucch_alloc}; + ue_resource_grid_view ue_res_grid; + ue_repository ues; + ue_cell_grid_allocator grid_alloc{sched_cfg.ue, ues, logger}; std::unique_ptr sched; slot_point next_slot{0, test_rgen::uniform_int(0, 10239)}; }; @@ -216,40 +182,18 @@ TEST_P(scheduler_policy_test, when_coreset0_used_then_dl_grant_is_within_bounds_ run_slot(); - ASSERT_FALSE(this->pdsch_alloc.last_grants.empty()); - ASSERT_EQ(this->pdsch_alloc.last_grants[0].user->ue_index, u.ue_index); - crb_interval crbs = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs(); - ASSERT_EQ(this->pdsch_alloc.last_grants[0].crbs, crbs); + ASSERT_FALSE(this->res_grid[0].result.dl.ue_grants.empty()); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants.back().context.ue_index, u.ue_index); + crb_interval crbs = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs(); + prb_interval alloced_prbs = prb_interval{this->res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().start(), + this->res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().stop()}; + ASSERT_EQ(prb_to_crb(crbs, alloced_prbs), crbs); } -TEST_P(scheduler_policy_test, scheduler_favors_ss_with_higher_nof_candidates_for_aggr_lvl) +TEST_P(scheduler_policy_test, scheduler_uses_only_specific_searchspaces_defined_in_dedicated_configuration) { sched_ue_creation_request_message ue_req = make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(4)}, uint_to_lcg_id(1)); - // Default CORESET(s) and SearchSpace(s) configurations are as follows: - // CS#0 - PRBs [0, 48) - // CS#1 - PRBs [0, 36) - // SS#0 - Nof. Candidates at Aggregation level [ [0 -> 0], [2 -> 0], [4 -> 1], [8 -> 0], [16 -> 0] ] - CS#0 - // SS#1 - Nof. Candidates at Aggregation level [ [0 -> 0], [2 -> 2], [4 -> 1], [8 -> 0], [16 -> 0] ] - CS#0 - // SS#2 - Nof. Candidates at Aggregation level [ [0 -> 0], [2 -> 2], [4 -> 1], [8 -> 0], [16 -> 0] ] - CS#1 - - // NOTE: Policy scheduler currently uses fixed aggr. lvl of 4 so test is based on nof. PDCCH candidates for aggr. - // lvl. 4. - - // Modify SS#2 to have more nof. candidates for aggregation level 4. - auto& ss_list = (*ue_req.cfg.cells)[0].serv_cell_cfg.init_dl_bwp.pdcch_cfg.value().search_spaces; - for (auto& ss : ss_list) { - if (ss.get_id() == to_search_space_id(2)) { - ss.set_non_ss0_nof_candidates( - {0, - 0, - config_helpers::compute_max_nof_candidates( - aggregation_level::n4, - ue_req.cfg.cells.value()[0].serv_cell_cfg.init_dl_bwp.pdcch_cfg.value().coresets[0]), - 0}); - break; - } - } const ue& u = add_ue(ue_req); push_dl_bs(u.ue_index, uint_to_lcid(4), 1053); @@ -257,13 +201,11 @@ TEST_P(scheduler_policy_test, scheduler_favors_ss_with_higher_nof_candidates_for run_slot(); - ASSERT_FALSE(this->pdsch_alloc.last_grants.empty()); - ASSERT_EQ(this->pdsch_alloc.last_grants[0].user->ue_index, u.ue_index); - ASSERT_EQ(this->pdsch_alloc.last_grants[0].ss_id, to_search_space_id(2)); - - ASSERT_FALSE(this->pusch_alloc.last_grants.empty()); - ASSERT_EQ(this->pusch_alloc.last_grants[0].user->ue_index, u.ue_index); - ASSERT_EQ(this->pusch_alloc.last_grants[0].ss_id, to_search_space_id(2)); + ASSERT_EQ(this->res_grid[0].result.dl.dl_pdcchs.size(), 1); + ASSERT_EQ(this->res_grid[0].result.dl.ul_pdcchs.size(), 1); + // SearchSpace#2 is the only SearchSpace defined in UE dedicated configuration. + ASSERT_EQ(this->res_grid[0].result.dl.dl_pdcchs.back().ctx.context.ss_id, to_search_space_id(2)); + ASSERT_EQ(this->res_grid[0].result.dl.ul_pdcchs.back().ctx.context.ss_id, to_search_space_id(2)); } TEST_P(scheduler_policy_test, scheduler_allocates_more_than_one_ue_in_case_their_bsr_is_low) @@ -277,9 +219,20 @@ TEST_P(scheduler_policy_test, scheduler_allocates_more_than_one_ue_in_case_their run_slot(); - ASSERT_EQ(pusch_alloc.last_grants.size(), 2); - ASSERT_NE(pusch_alloc.last_grants[0].user->ue_index, pusch_alloc.last_grants[1].user->ue_index); - ASSERT_FALSE(pusch_alloc.last_grants[0].crbs.overlaps(pusch_alloc.last_grants[1].crbs)); + ASSERT_EQ(this->res_grid[0].result.dl.ul_pdcchs.size(), 2); + ASSERT_NE(this->res_grid[0].result.dl.ul_pdcchs[0].ctx.rnti, this->res_grid[0].result.dl.ul_pdcchs[1].ctx.rnti); + // NOTE: Both UEs have same PUSCH time domain resources configured. + span pusch_td_res_list = + u1.get_pcell().cfg().cell_cfg_common.ul_cfg_common.init_ul_bwp.pusch_cfg_common->pusch_td_alloc_list; + // In default UE dedicated configuration, SearchSpace#2 is configured to use DCI format 1_1/0_1. + if (this->res_grid[0].result.dl.ul_pdcchs[0].dci.c_rnti_f0_1.time_resource == + this->res_grid[0].result.dl.ul_pdcchs[1].dci.c_rnti_f0_1.time_resource) { + const unsigned k2 = pusch_td_res_list[this->res_grid[0].result.dl.ul_pdcchs[0].dci.c_rnti_f0_1.time_resource].k2; + const auto& pusch_res_grid = this->res_grid[k2]; + ASSERT_EQ(pusch_res_grid.result.ul.puschs.size(), 2); + ASSERT_FALSE(pusch_res_grid.result.ul.puschs[0].pusch_cfg.rbs.type1().overlaps( + pusch_res_grid.result.ul.puschs[1].pusch_cfg.rbs.type1())); + } } TEST_P(scheduler_policy_test, scheduler_allocates_more_than_one_ue_in_case_their_dl_buffer_state_is_low) @@ -293,9 +246,11 @@ TEST_P(scheduler_policy_test, scheduler_allocates_more_than_one_ue_in_case_their run_slot(); - ASSERT_EQ(pdsch_alloc.last_grants.size(), 2); - ASSERT_NE(pdsch_alloc.last_grants[0].user->ue_index, pdsch_alloc.last_grants[1].user->ue_index); - ASSERT_FALSE(pdsch_alloc.last_grants[0].crbs.overlaps(pdsch_alloc.last_grants[1].crbs)); + ASSERT_EQ(this->res_grid[0].result.dl.dl_pdcchs.size(), 2); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants.size(), 2); + ASSERT_NE(this->res_grid[0].result.dl.dl_pdcchs[0].ctx.rnti, this->res_grid[0].result.dl.dl_pdcchs[1].ctx.rnti); + ASSERT_FALSE(this->res_grid[0].result.dl.ue_grants[0].pdsch_cfg.rbs.type1().overlaps( + this->res_grid[0].result.dl.ue_grants[1].pdsch_cfg.rbs.type1())); } TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_sr_opportunity_first_than_ues_with_only_ul_data) @@ -309,8 +264,8 @@ TEST_P(scheduler_policy_test, scheduler_allocates_ues_with_sr_opportunity_first_ run_slot(); - ASSERT_GE(pusch_alloc.last_grants.size(), 1); - ASSERT_EQ(pusch_alloc.last_grants.front().user->ue_index, to_du_ue_index(1)) + ASSERT_GE(this->res_grid[0].result.dl.ul_pdcchs.size(), 1); + ASSERT_EQ(this->res_grid[0].result.dl.ul_pdcchs.front().ctx.rnti, to_rnti(0x4602)) << fmt::format("UE with SR opportunity should have been scheduled first."); } @@ -342,22 +297,16 @@ class scheduler_policy_partial_slot_tdd_test : public base_scheduler_policy_test builder_params.search_space0_index, builder_params.max_coreset0_duration); srsran_assert(ssb_freq_loc.has_value(), "Invalid cell config parameters"); - builder_params.offset_to_point_a = ssb_freq_loc->offset_to_point_A; - builder_params.k_ssb = ssb_freq_loc->k_ssb; - builder_params.coreset0_index = ssb_freq_loc->coreset0_idx; - auto cfg = test_helpers::make_default_sched_cell_configuration_request(builder_params); - const tdd_ul_dl_config_common tdd_cfg{.ref_scs = subcarrier_spacing::kHz30, - .pattern1 = {.dl_ul_tx_period_nof_slots = 10, - .nof_dl_slots = 6, - .nof_dl_symbols = 5, - .nof_ul_slots = 3, - .nof_ul_symbols = 0}}; - cfg.tdd_ul_dl_cfg_common = tdd_cfg; - // Generate PDSCH Time domain allocation based on the partial slot TDD configuration. - cfg.dl_cfg_common.init_dl_bwp.pdsch_common.pdsch_td_alloc_list = config_helpers::make_pdsch_time_domain_resource( - cfg.searchspace0, cfg.dl_cfg_common.init_dl_bwp.pdcch_common, std::nullopt, cfg.tdd_ul_dl_cfg_common); - - return cfg; + builder_params.offset_to_point_a = ssb_freq_loc->offset_to_point_A; + builder_params.k_ssb = ssb_freq_loc->k_ssb; + builder_params.coreset0_index = ssb_freq_loc->coreset0_idx; + builder_params.tdd_ul_dl_cfg_common = tdd_ul_dl_config_common{.ref_scs = subcarrier_spacing::kHz30, + .pattern1 = {.dl_ul_tx_period_nof_slots = 10, + .nof_dl_slots = 5, + .nof_dl_symbols = 5, + .nof_ul_slots = 4, + .nof_ul_symbols = 0}}; + return test_helpers::make_default_sched_cell_configuration_request(builder_params); }()) { next_slot = {to_numerology_value(subcarrier_spacing::kHz30), 0}; @@ -379,7 +328,7 @@ TEST_P(scheduler_policy_partial_slot_tdd_test, scheduler_allocates_in_partial_sl // Run for partial slot. run_slot(); - ASSERT_EQ(pdsch_alloc.last_grants.size(), 1); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants.size(), 1); } class scheduler_round_robin_test : public base_scheduler_policy_test, public ::testing::Test @@ -402,12 +351,12 @@ TEST_F(scheduler_round_robin_test, round_robin_does_not_account_ues_with_empty_b unsigned offset = 0; for (unsigned i = 0; i != 10; ++i) { run_slot(); - ASSERT_GE(pdsch_alloc.last_grants.size(), 1); + ASSERT_GE(this->res_grid[0].result.dl.ue_grants.size(), 1); if (i == 0) { - offset = pdsch_alloc.last_grants[0].user->ue_index == u1.ue_index ? 0 : 1; - ASSERT_NE(pdsch_alloc.last_grants[0].user->ue_index, u2.ue_index); + offset = this->res_grid[0].result.dl.ue_grants[0].context.ue_index == u1.ue_index ? 0 : 1; + ASSERT_NE(this->res_grid[0].result.dl.ue_grants[0].context.ue_index, u2.ue_index); } else { - ASSERT_EQ(pdsch_alloc.last_grants[0].user->ue_index, rr_ues[(i + offset) % rr_ues.size()]); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants[0].context.ue_index, rr_ues[(i + offset) % rr_ues.size()]); } } } @@ -418,14 +367,14 @@ TEST_F(scheduler_round_robin_test, round_robin_must_not_attempt_to_allocate_twic ue& u1 = add_ue(make_ue_create_req(to_du_ue_index(0), to_rnti(0x4601), {uint_to_lcid(5)}, lcg_id)); auto get_pdsch_grant_count_for_ue = [&](const rnti_t crnti) { - return std::count_if(pdsch_alloc.last_grants.begin(), - pdsch_alloc.last_grants.end(), - [&](const ue_pdsch_grant& grant) { return crnti == grant.user->crnti; }); + return std::count_if(this->res_grid[0].result.dl.ue_grants.begin(), + this->res_grid[0].result.dl.ue_grants.end(), + [&](const dl_msg_alloc& grant) { return crnti == grant.pdsch_cfg.rnti; }); }; auto get_pusch_grant_count_for_ue = [&](const rnti_t crnti) { - return std::count_if(pusch_alloc.last_grants.begin(), - pusch_alloc.last_grants.end(), - [&](const ue_pusch_grant& grant) { return crnti == grant.user->crnti; }); + return std::count_if(this->res_grid[0].result.ul.puschs.begin(), + this->res_grid[0].result.ul.puschs.end(), + [&](const ul_sched_info& grant) { return crnti == grant.pusch_cfg.rnti; }); }; // Action: Push buffer status notification for DL + Ul and a SR indication. @@ -473,9 +422,12 @@ TEST_P(scheduler_policy_alloc_bounds_test, scheduler_allocates_pdsch_within_conf // Expected CRBs is based on the expert configuration passed to scheduler during class initialization. crb_interval expected_crb_allocation{10, 15}; + prb_interval expected_prb_interval = crb_to_prb( + u1.get_pcell().cfg().bwp(u1.get_pcell().active_bwp_id()).dl_common->generic_params.crbs, expected_crb_allocation); + vrb_interval expected_vrb_interval{expected_prb_interval.start(), expected_prb_interval.stop()}; - ASSERT_EQ(pdsch_alloc.last_grants.size(), 1); - ASSERT_TRUE(pdsch_alloc.last_grants.back().crbs == expected_crb_allocation); + ASSERT_EQ(this->res_grid[0].result.dl.ue_grants.size(), 1); + ASSERT_TRUE(this->res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1() == expected_vrb_interval); } TEST_P(scheduler_policy_alloc_bounds_test, scheduler_allocates_pusch_within_configured_boundaries) @@ -486,14 +438,18 @@ TEST_P(scheduler_policy_alloc_bounds_test, scheduler_allocates_pusch_within_conf // Buffer has to be large enough so that the allocation does not stop. notify_ul_bsr(u1.ue_index, lcg_id, 2000000); - // Run for partial slot. - run_slot(); + // Run until one slot before partial slot. + while (this->res_grid[0].result.ul.puschs.empty()) { + run_slot(); + } // Expected CRBs is based on the expert configuration passed to scheduler during class initialization. crb_interval expected_crb_allocation{10, 15}; + prb_interval expected_prb_interval = crb_to_prb( + u1.get_pcell().cfg().bwp(u1.get_pcell().active_bwp_id()).ul_common->generic_params.crbs, expected_crb_allocation); + vrb_interval expected_vrb_interval{expected_prb_interval.start(), expected_prb_interval.stop()}; - ASSERT_EQ(pusch_alloc.last_grants.size(), 1); - ASSERT_TRUE(pusch_alloc.last_grants.back().crbs == expected_crb_allocation); + ASSERT_TRUE(this->res_grid[0].result.ul.puschs.back().pusch_cfg.rbs.type1() == expected_vrb_interval); } INSTANTIATE_TEST_SUITE_P(scheduler_policy, scheduler_policy_test, testing::Values(policy_type::time_rr)); diff --git a/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp b/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp index 25257d51b5..063206a023 100644 --- a/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp +++ b/tests/unittests/scheduler/scheduler_ue_fallback_mode_test.cpp @@ -91,14 +91,13 @@ class base_scheduler_conres_test : public scheduler_test_bench class scheduler_conres_without_pdu_test : public base_scheduler_conres_test, public ::testing::Test {}; -TEST_F(scheduler_conres_without_pdu_test, - when_conres_ce_is_enqueued_and_no_msg4_is_enqueued_then_pdsch_is_not_scheduled) +TEST_F(scheduler_conres_without_pdu_test, when_conres_ce_is_enqueued_and_no_msg4_is_enqueued_then_pdsch_is_scheduled) { // Enqueue ConRes CE. this->sched->handle_dl_mac_ce_indication(dl_mac_ce_indication{ue_index, lcid_dl_sch_t::UE_CON_RES_ID}); - // Ensure the ConRes CEU is not scheduled without a Msg4 SDU. - ASSERT_FALSE(this->run_slot_until( + // Ensure the ConRes CE is scheduled without a Msg4 SDU. + ASSERT_TRUE(this->run_slot_until( [this]() { return find_ue_pdsch(rnti, *this->last_sched_res_list[to_du_cell_index(0)]) != nullptr; })); } @@ -142,6 +141,9 @@ TEST_P(scheduler_con_res_msg4_test, // Enqueue several RACH indications, so that RARs that need to be scheduled may fight for RB space with the Msg4. enqueue_random_number_of_rach_indications(); + // Run until all RARs are scheduled. + this->run_slot_until([this]() { return this->last_sched_res_list[to_du_cell_index(0)]->dl.rar_grants.empty(); }); + // Enqueue ConRes CE. this->sched->handle_dl_mac_ce_indication(dl_mac_ce_indication{ue_index, lcid_dl_sch_t::UE_CON_RES_ID}); 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 94115b263d..443cac51ef 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 @@ -86,7 +86,7 @@ TEST_P(sched_pucch_res_builder_tester, when_ues_are_added_their_cfg_have_differe const bool has_csi_cfg = ue->serv_cell_cfg.csi_meas_cfg.has_value() and not ue->serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.empty(); ASSERT_TRUE(has_csi_cfg); - const auto& csi_res_cfg = srsran::variant_get( + const auto& csi_res_cfg = std::get( ue->serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.front().report_cfg_type); auto ue_csi_res_offset_pair = std::make_pair(csi_res_cfg.pucch_csi_res_list.front().pucch_res_id.cell_res_id, csi_res_cfg.report_slot_offset); diff --git a/tests/unittests/scheduler/test_utils/config_generators.h b/tests/unittests/scheduler/test_utils/config_generators.h index b284abd6ad..10ed562852 100644 --- a/tests/unittests/scheduler/test_utils/config_generators.h +++ b/tests/unittests/scheduler/test_utils/config_generators.h @@ -190,34 +190,34 @@ inline uplink_config make_test_ue_uplink_config(const config_helpers::cell_confi pucch_cfg.pucch_res_list.push_back(res_basic_f2); // >>> PUCCH resource 4. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res4 = pucch_cfg.pucch_res_list.back(); - res4.res_id = pucch_res_id_t{4, 4}; - variant_get(res4.format_params).starting_sym_idx = 2; + pucch_resource& res4 = pucch_cfg.pucch_res_list.back(); + res4.res_id = pucch_res_id_t{4, 4}; + std::get(res4.format_params).starting_sym_idx = 2; // >>> PUCCH resource 5. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res5 = pucch_cfg.pucch_res_list.back(); - res5.res_id = pucch_res_id_t{5, 5}; - variant_get(res5.format_params).starting_sym_idx = 4; + pucch_resource& res5 = pucch_cfg.pucch_res_list.back(); + res5.res_id = pucch_res_id_t{5, 5}; + std::get(res5.format_params).starting_sym_idx = 4; // >>> PUCCH resource 6. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res6 = pucch_cfg.pucch_res_list.back(); - res6.res_id = pucch_res_id_t{6, 6}; - variant_get(res6.format_params).starting_sym_idx = 6; + pucch_resource& res6 = pucch_cfg.pucch_res_list.back(); + res6.res_id = pucch_res_id_t{6, 6}; + std::get(res6.format_params).starting_sym_idx = 6; // >>> PUCCH resource 7. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res7 = pucch_cfg.pucch_res_list.back(); - res7.res_id = pucch_res_id_t{7, 7}; - variant_get(res7.format_params).starting_sym_idx = 8; + pucch_resource& res7 = pucch_cfg.pucch_res_list.back(); + res7.res_id = pucch_res_id_t{7, 7}; + std::get(res7.format_params).starting_sym_idx = 8; // >>> PUCCH resource 8. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res8 = pucch_cfg.pucch_res_list.back(); - res8.res_id = pucch_res_id_t{8, 8}; - variant_get(res8.format_params).starting_sym_idx = 10; + pucch_resource& res8 = pucch_cfg.pucch_res_list.back(); + res8.res_id = pucch_res_id_t{8, 8}; + std::get(res8.format_params).starting_sym_idx = 10; // >>> PUCCH resource 9. pucch_cfg.pucch_res_list.push_back(res_basic_f2); - pucch_resource& res9 = pucch_cfg.pucch_res_list.back(); - res9.res_id = pucch_res_id_t{9, 9}; - variant_get(res9.format_params).starting_sym_idx = 12; + pucch_resource& res9 = pucch_cfg.pucch_res_list.back(); + res9.res_id = pucch_res_id_t{9, 9}; + std::get(res9.format_params).starting_sym_idx = 12; // >>> PUCCH resource 10. pucch_cfg.pucch_res_list.push_back(res_basic); diff --git a/tests/unittests/scheduler/test_utils/dummy_test_components.h b/tests/unittests/scheduler/test_utils/dummy_test_components.h index 3085410b46..9451cc0e8f 100644 --- a/tests/unittests/scheduler/test_utils/dummy_test_components.h +++ b/tests/unittests/scheduler/test_utils/dummy_test_components.h @@ -34,9 +34,10 @@ namespace srsran { class dummy_pdcch_resource_allocator : public pdcch_resource_allocator { public: - rnti_t last_dl_pdcch_rnti; - pdcch_dl_information next_ue_pdcch_alloc; - pdcch_ul_information next_ue_ul_pdcch_alloc; + rnti_t last_dl_pdcch_rnti; + pdcch_dl_information next_ue_pdcch_alloc; + pdcch_ul_information next_ue_ul_pdcch_alloc; + std::function fail_pdcch_alloc_cond; pdcch_dl_information* alloc_dl_pdcch_common(cell_slot_resource_allocator& slot_alloc, rnti_t rnti, @@ -44,6 +45,9 @@ class dummy_pdcch_resource_allocator : public pdcch_resource_allocator aggregation_level aggr_lvl) override { TESTASSERT_EQ(ss_id, slot_alloc.cfg.dl_cfg_common.init_dl_bwp.pdcch_common.ra_search_space_id); + if (fail_pdcch_alloc_cond and fail_pdcch_alloc_cond(slot_alloc.slot)) { + return nullptr; + } slot_alloc.result.dl.dl_pdcchs.emplace_back(); slot_alloc.result.dl.dl_pdcchs.back().ctx.rnti = rnti; slot_alloc.result.dl.dl_pdcchs.back().ctx.bwp_cfg = &slot_alloc.cfg.dl_cfg_common.init_dl_bwp.generic_params; 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 fbb8c2bd3a..f2b67c8c5f 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 @@ -544,7 +544,7 @@ class test_pucch_res_manager_multiple_cfg : public test_pucch_resource_manager } const unsigned f2_idx_offset = tot_ue_f1_res * nof_pucch_cfgs + (ue_idx % nof_pucch_cfgs) * tot_ue_f2_res; - auto& csi_cfg = variant_get( + auto& csi_cfg = std::get( serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list.front().report_cfg_type) .pucch_csi_res_list.front(); diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp index 9d48d41469..e75a0b877a 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_scheduling_test.cpp @@ -41,22 +41,22 @@ class uci_sr_scheduler_tester : public ::testing::TestWithParam .period = sr_period, .offset = sr_offset}} { - csi_offset = variant_get(t_bench.get_main_ue() - .get_pcell() - .cfg() - .cfg_dedicated() - .csi_meas_cfg.value() - .csi_report_cfg_list[0] - .report_cfg_type) + csi_offset = std::get(t_bench.get_main_ue() + .get_pcell() + .cfg() + .cfg_dedicated() + .csi_meas_cfg.value() + .csi_report_cfg_list[0] + .report_cfg_type) .report_slot_offset; - csi_period = variant_get(t_bench.get_main_ue() - .get_pcell() - .cfg() - .cfg_dedicated() - .csi_meas_cfg.value() - .csi_report_cfg_list[0] - .report_cfg_type) + csi_period = std::get(t_bench.get_main_ue() + .get_pcell() + .cfg() + .cfg_dedicated() + .csi_meas_cfg.value() + .csi_report_cfg_list[0] + .report_cfg_type) .report_slot_period; // In the slots with SR only, the expected format is Format 1. diff --git a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp index 3e77202f46..96f6dd3940 100644 --- a/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp +++ b/tests/unittests/scheduler/uci_and_pucch/uci_test_utils.cpp @@ -170,8 +170,8 @@ test_bench::test_bench(const test_bench_params& params, const unsigned pucch_f2_nof_prbs = 3U; for (auto& pucch_res : ul_cfg.init_ul_bwp.pucch_cfg.value().pucch_res_list) { if (pucch_res.format == pucch_format::FORMAT_2 and - variant_holds_alternative(pucch_res.format_params)) { - variant_get(pucch_res.format_params).nof_prbs = pucch_f2_nof_prbs; + std::holds_alternative(pucch_res.format_params)) { + std::get(pucch_res.format_params).nof_prbs = pucch_f2_nof_prbs; } } } @@ -179,15 +179,15 @@ test_bench::test_bench(const test_bench_params& params, if (params.cfg_for_mimo_4x4) { ue_req.cfg.cells->back().serv_cell_cfg.csi_meas_cfg = csi_helper::make_csi_meas_config(csi_helper::csi_builder_params{.nof_ports = 4}); - auto& beta_offsets = variant_get(ue_req.cfg.cells->back() - .serv_cell_cfg.ul_config.value() - .init_ul_bwp.pusch_cfg.value() - .uci_cfg.value() - .beta_offsets_cfg.value()); + auto& beta_offsets = std::get(ue_req.cfg.cells->back() + .serv_cell_cfg.ul_config.value() + .init_ul_bwp.pusch_cfg.value() + .uci_cfg.value() + .beta_offsets_cfg.value()); beta_offsets.beta_offset_csi_p2_idx_1.value() = 6; } - auto& csi_report = variant_get( + auto& csi_report = std::get( ue_req.cfg.cells->back().serv_cell_cfg.csi_meas_cfg.value().csi_report_cfg_list[0].report_cfg_type); csi_report.report_slot_period = params.csi_period; csi_report.report_slot_offset = params.csi_offset; diff --git a/tests/unittests/scheduler/ue_scheduling/CMakeLists.txt b/tests/unittests/scheduler/ue_scheduling/CMakeLists.txt index 945128a90e..8bb1cc0fd2 100644 --- a/tests/unittests/scheduler/ue_scheduling/CMakeLists.txt +++ b/tests/unittests/scheduler/ue_scheduling/CMakeLists.txt @@ -27,7 +27,7 @@ add_executable(ue_scheduler_test ue_configuration_test.cpp ue_grid_allocator_test.cpp ul_logical_channel_test.cpp - ue_pdsch_param_candidate_searcher_test.cpp + ue_pxsch_alloc_param_candidate_searcher_test.cpp ue_link_adaptation_controller_test.cpp ta_manager_test.cpp ue_harq_link_adaptation_test.cpp) diff --git a/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp b/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp index 6425cd3cab..8b5eeaa8a2 100644 --- a/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/fallback_scheduler_test.cpp @@ -243,14 +243,31 @@ class base_fallback_tester { return std::any_of(bench->res_grid[0].result.dl.dl_pdcchs.begin(), bench->res_grid[0].result.dl.dl_pdcchs.end(), - [&u](const auto& pdcch) { return pdcch.ctx.rnti == u.crnti; }); + [&u](const pdcch_dl_information& pdcch) { return pdcch.ctx.rnti == u.crnti; }); + } + + const pdcch_dl_information* get_ue_allocated_pdcch(const ue& u) + { + const auto* it = std::find_if(bench->res_grid[0].result.dl.dl_pdcchs.begin(), + bench->res_grid[0].result.dl.dl_pdcchs.end(), + [&u](const pdcch_dl_information& pdcch) { return pdcch.ctx.rnti == u.crnti; }); + return it == bench->res_grid[0].result.dl.dl_pdcchs.end() ? nullptr : it; } bool ue_is_allocated_pdsch(const ue& u) { return std::any_of(bench->res_grid[0].result.dl.ue_grants.begin(), bench->res_grid[0].result.dl.ue_grants.end(), - [&u](const auto& grant) { return grant.pdsch_cfg.rnti == u.crnti; }); + [&u](const dl_msg_alloc& grant) { return grant.pdsch_cfg.rnti == u.crnti; }); + } + + const dl_msg_alloc* get_ue_allocated_pdsch(const ue& u) + { + const auto* it = std::find_if(bench->res_grid[0].result.dl.ue_grants.begin(), + bench->res_grid[0].result.dl.ue_grants.end(), + [&u](const dl_msg_alloc& grant) { return grant.pdsch_cfg.rnti == u.crnti; }); + + return it == bench->res_grid[0].result.dl.ue_grants.end() ? nullptr : it; } bool ue_is_allocated_pucch(const ue& u) @@ -298,6 +315,7 @@ class base_fallback_tester bench->ue_db[ue_idx].handle_dl_buffer_state_indication(msg); if (tx_conres) { bench->ue_db[ue_idx].handle_dl_mac_ce_indication(dl_mac_ce_indication{ue_idx, lcid_dl_sch_t::UE_CON_RES_ID}); + bench->fallback_sched.handle_conres_indication(ue_idx); } // Notify scheduler of DL buffer state. @@ -329,9 +347,14 @@ class base_fallback_tester bench->fallback_sched.handle_sr_indication(ue_idx); } - unsigned get_pending_bytes(du_ue_index_t ue_idx) + unsigned get_srb0_pending_bytes(du_ue_index_t ue_idx) { - return bench->ue_db[ue_idx].pending_dl_srb0_or_srb1_newtx_bytes(true); + return bench->ue_db[ue_idx].pending_dl_newtx_bytes(LCID_SRB0); + } + + unsigned get_srb0_and_ce_pending_bytes(du_ue_index_t ue_idx) + { + return bench->ue_db[ue_idx].pending_dl_newtx_bytes(LCID_SRB0) + bench->ue_db[ue_idx].pending_ce_bytes(); } ue& get_ue(du_ue_index_t ue_idx) { return bench->ue_db[ue_idx]; } @@ -367,7 +390,7 @@ TEST_P(fallback_scheduler_tester, successfully_allocated_resources) const unsigned mac_srb0_sdu_size = 101; push_buffer_state_to_dl_ue(ue_idx, current_slot, mac_srb0_sdu_size, true); - const unsigned exp_size = get_pending_bytes(ue_idx); + const unsigned exp_size = get_srb0_and_ce_pending_bytes(ue_idx); // Test the following: // 1. Check for DCI_1_0 allocation for SRB0 on PDCCH. @@ -378,12 +401,21 @@ TEST_P(fallback_scheduler_tester, successfully_allocated_resources) bool is_ue_allocated_pdsch{false}; for (unsigned sl_idx = 0; sl_idx < bench->max_test_run_slots_per_ue * (1U << current_slot.numerology()); sl_idx++) { run_slot(); - if (ue_is_allocated_pdcch(test_ue)) { + const pdcch_dl_information* pdcch_it = get_ue_allocated_pdcch(test_ue); + if (pdcch_it != nullptr) { is_ue_allocated_pdcch = true; } - if (ue_is_allocated_pdsch(test_ue)) { - is_ue_allocated_pdsch = true; - ASSERT_TRUE(tbs_scheduled_bytes_matches_given_size(test_ue, exp_size)); + if (is_ue_allocated_pdcch) { + const dl_msg_alloc* pdsch_it = get_ue_allocated_pdsch(test_ue); + if (pdsch_it != nullptr) { + for (const auto& lc_info : pdsch_it->tb_list.back().lc_chs_to_sched) { + if (lc_info.lcid.is_sdu()) { + is_ue_allocated_pdsch = true; + ASSERT_TRUE(tbs_scheduled_bytes_matches_given_size(test_ue, exp_size)); + break; + } + } + } } } ASSERT_TRUE(is_ue_allocated_pdcch); @@ -393,18 +425,18 @@ TEST_P(fallback_scheduler_tester, successfully_allocated_resources) TEST_P(fallback_scheduler_tester, failed_allocating_resources) { - setup_sched(create_expert_config(0), create_custom_cell_config_request(params.k0)); + setup_sched(create_expert_config(3), create_custom_cell_config_request(params.k0)); // Add UE 1. add_ue(to_rnti(0x4601), to_du_ue_index(0)); // Notify about SRB0 message in DL of size 101 bytes. - unsigned ue1_mac_srb0_sdu_size = 101; + unsigned ue1_mac_srb0_sdu_size = 99; push_buffer_state_to_dl_ue(to_du_ue_index(0), current_slot, ue1_mac_srb0_sdu_size, true); // Add UE 2. add_ue(to_rnti(0x4602), to_du_ue_index(1)); - // Notify about SRB0 message in DL of size 350 bytes. i.e. big enough to not get allocated with the max. mcs chosen. - unsigned ue2_mac_srb0_sdu_size = 350; + // Notify about SRB0 message in DL of size 450 bytes. i.e. big enough to not get allocated with the max. mcs chosen. + unsigned ue2_mac_srb0_sdu_size = 450; push_buffer_state_to_dl_ue(to_du_ue_index(1), current_slot, ue2_mac_srb0_sdu_size, true); run_slot(); @@ -413,9 +445,42 @@ TEST_P(fallback_scheduler_tester, failed_allocating_resources) const auto& test_ue = get_ue(to_du_ue_index(1)); for (unsigned sl_idx = 0; sl_idx < bench->max_test_run_slots_per_ue * (1U << current_slot.numerology()); sl_idx++) { run_slot(); - ASSERT_FALSE(ue_is_allocated_pdcch(test_ue)); - ASSERT_FALSE(ue_is_allocated_pdsch(test_ue)); + const pdcch_dl_information* pdcch_it = get_ue_allocated_pdcch(test_ue); + if (pdcch_it != nullptr) { + const dl_msg_alloc* pdsch_it = get_ue_allocated_pdsch(test_ue); + ASSERT_FALSE(pdsch_it != nullptr and pdsch_it->tb_list.back().lc_chs_to_sched.back().lcid.is_sdu()); + } + } +} + +TEST_P(fallback_scheduler_tester, conres_and_msg4_scheduled_scheduled_over_different_slots_if_they_dont_fit_together) +{ + setup_sched(create_expert_config(1), create_custom_cell_config_request(params.k0)); + + // Add UE 1. + add_ue(to_rnti(0x4601), to_du_ue_index(0)); + // Notify about SRB0 message in DL of size 101 bytes. + unsigned ue1_mac_srb0_sdu_size = 99; + push_buffer_state_to_dl_ue(to_du_ue_index(0), current_slot, ue1_mac_srb0_sdu_size, true); + + // ConRes and Msg4 are scheduled separately. + const auto& test_ue = get_ue(to_du_ue_index(0)); + std::optional conres_pdcch; + std::optional msg4_pdcch; + for (unsigned sl_idx = 0; sl_idx < bench->max_test_run_slots_per_ue * (1U << current_slot.numerology()); sl_idx++) { + run_slot(); + const pdcch_dl_information* pdcch_it = get_ue_allocated_pdcch(test_ue); + if (pdcch_it != nullptr) { + if (pdcch_it->dci.type == dci_dl_rnti_config_type::tc_rnti_f1_0) { + conres_pdcch = current_slot; + } else if (pdcch_it->dci.type == dci_dl_rnti_config_type::c_rnti_f1_0) { + msg4_pdcch = current_slot; + } + } } + ASSERT_TRUE(conres_pdcch.has_value()); + ASSERT_TRUE(msg4_pdcch.has_value()); + ASSERT_TRUE(conres_pdcch != msg4_pdcch); } TEST_P(fallback_scheduler_tester, test_large_srb0_buffer_size) @@ -428,19 +493,28 @@ TEST_P(fallback_scheduler_tester, test_large_srb0_buffer_size) const unsigned mac_srb0_sdu_size = 458; push_buffer_state_to_dl_ue(ue_idx, current_slot, mac_srb0_sdu_size, true); - const unsigned exp_size = get_pending_bytes(ue_idx); + const unsigned exp_size = get_srb0_pending_bytes(ue_idx); const auto& test_ue = get_ue(ue_idx); bool is_ue_allocated_pdcch{false}; bool is_ue_allocated_pdsch{false}; for (unsigned sl_idx = 0; sl_idx < bench->max_test_run_slots_per_ue * (1U << current_slot.numerology()); sl_idx++) { run_slot(); - if (ue_is_allocated_pdcch(test_ue)) { + const pdcch_dl_information* pdcch_it = get_ue_allocated_pdcch(test_ue); + if (pdcch_it != nullptr) { is_ue_allocated_pdcch = true; } - if (ue_is_allocated_pdsch(test_ue)) { - is_ue_allocated_pdsch = true; - ASSERT_TRUE(tbs_scheduled_bytes_matches_given_size(test_ue, exp_size)); + if (is_ue_allocated_pdcch) { + const dl_msg_alloc* pdsch_it = get_ue_allocated_pdsch(test_ue); + if (pdsch_it != nullptr) { + for (const auto& lc_info : pdsch_it->tb_list.back().lc_chs_to_sched) { + if (lc_info.lcid.is_sdu()) { + is_ue_allocated_pdsch = true; + ASSERT_TRUE(tbs_scheduled_bytes_matches_given_size(test_ue, exp_size)); + break; + } + } + } } } ASSERT_TRUE(is_ue_allocated_pdcch); @@ -462,8 +536,14 @@ TEST_P(fallback_scheduler_tester, test_srb0_buffer_size_exceeding_max_msg4_mcs_i const auto& test_ue = get_ue(to_du_ue_index(0)); for (unsigned sl_idx = 0; sl_idx < bench->max_test_run_slots_per_ue * (1U << current_slot.numerology()); sl_idx++) { run_slot(); - ASSERT_FALSE(ue_is_allocated_pdcch(test_ue)); - ASSERT_FALSE(ue_is_allocated_pdsch(test_ue)); + const pdcch_dl_information* pdcch_it = get_ue_allocated_pdcch(test_ue); + // ConRes CE is sent beforehand hence DCI 1_0 scrambled with C-RNTI is used to send Msg4. + ASSERT_FALSE(pdcch_it != nullptr and pdcch_it->dci.type == srsran::dci_dl_rnti_config_type::c_rnti_f1_0); + if (pdcch_it != nullptr) { + const dl_msg_alloc* pdsch_it = get_ue_allocated_pdsch(test_ue); + // ConRes CE is sent beforehand hence Msg4 should consist only SDU with no ConRes MAC CE. + ASSERT_FALSE(pdsch_it != nullptr and pdsch_it->tb_list.back().lc_chs_to_sched.back().lcid.is_sdu()); + } } } @@ -538,8 +618,8 @@ TEST_F(fallback_scheduler_tdd_tester, test_allocation_in_partial_slots_tdd) const unsigned k0 = 0; const sch_mcs_index max_msg4_mcs_index = 8; const tdd_ul_dl_config_common tdd_cfg{.ref_scs = subcarrier_spacing::kHz30, - .pattern1 = {.dl_ul_tx_period_nof_slots = 5, - .nof_dl_slots = 2, + .pattern1 = {.dl_ul_tx_period_nof_slots = 10, + .nof_dl_slots = 7, .nof_dl_symbols = 8, .nof_ul_slots = 2, .nof_ul_symbols = 0}}; @@ -552,7 +632,10 @@ TEST_F(fallback_scheduler_tdd_tester, test_allocation_in_partial_slots_tdd) cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common, std::nullopt, cell_cfg.tdd_ul_dl_cfg_common); - setup_sched(create_expert_config(max_msg4_mcs_index), cell_cfg); + // Set minimum k1 according to TDD pattern. + scheduler_expert_config expert_cfg = create_expert_config(max_msg4_mcs_index); + expert_cfg.ue.min_k1 = 2; + setup_sched(expert_cfg, cell_cfg); const unsigned MAX_TEST_RUN_SLOTS = 40; const unsigned MAC_SRB0_SDU_SIZE = 129; @@ -562,7 +645,7 @@ TEST_F(fallback_scheduler_tdd_tester, test_allocation_in_partial_slots_tdd) for (unsigned idx = 0; idx < MAX_TEST_RUN_SLOTS * (1U << current_slot.numerology()); idx++) { run_slot(); - // Notify about SRB0 message in DL one slot before partial slot in order for it to be scheduled in the next + // Notify about SRB0 message in DL 1 slot before partial slot in order for it to be scheduled in the next // (partial) slot. if (bench->cell_cfg.is_dl_enabled(current_slot + 1) and (not bench->cell_cfg.is_fully_dl_enabled(current_slot + 1))) { @@ -635,13 +718,13 @@ class fallback_scheduler_head_scheduling : public base_fallback_tester, } while (occupy_grid_slot_cnt != nof_slot_grid_occupancy); auto k1_falls_on_ul = [&cfg = bench->cell_cfg](slot_point pdsch_slot) { - static const std::array dci_1_0_k1_values = {4, 5, 6, 7, 8}; + static const std::array dci_1_0_k1_values = {4, 5, 6, 7}; return std::any_of(dci_1_0_k1_values.begin(), dci_1_0_k1_values.end(), [&cfg, pdsch_slot](uint8_t k1) { return cfg.is_ul_enabled(pdsch_slot + k1); }); }; - // Make sure the final slot for the SRB0/SRB1 PDSCH is such that the corresponding PUCCH is in falls on a UL slot. + // Make sure the final slot for the SRB0/SRB1 PDSCH is such that the corresponding PUCCH falls on a UL slot. while ((not k1_falls_on_ul(sched_slot)) or (not bench->cell_cfg.is_dl_enabled(sched_slot)) or csi_helper::is_csi_rs_slot(bench->cell_cfg, sched_slot)) { sched_slot++; @@ -649,6 +732,17 @@ class fallback_scheduler_head_scheduling : public base_fallback_tester, return sched_slot; } + + void fill_resource_grid(slot_point sl, unsigned nof_slots, crb_interval crbs) + { + for (unsigned sl_inc = 0; sl_inc < nof_slots; ++sl_inc) { + if (bench->cell_cfg.is_dl_enabled(sl + sl_inc)) { + unsigned nof_dl_symbols = bench->cell_cfg.get_nof_dl_symbol_per_slot(sl + sl_inc); + grant_info grant{bench->cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs, {0, nof_dl_symbols}, crbs}; + bench->res_grid[sl + sl_inc].dl_res_grid.fill(grant); + } + } + } }; TEST_P(fallback_scheduler_head_scheduling, test_ahead_scheduling_for_srb_allocation_1_ue) @@ -671,39 +765,34 @@ TEST_P(fallback_scheduler_head_scheduling, test_ahead_scheduling_for_srb_allocat for (unsigned idx = 1; idx < MAX_TEST_RUN_SLOTS * (1U << current_slot.numerology()); idx++) { run_slot(); - if (current_slot != check_alloc_slot) { ASSERT_FALSE(ue_is_allocated_pdcch(test_ue)); ASSERT_FALSE(ue_is_allocated_pdsch(test_ue)); } else { - ASSERT_TRUE(ue_is_allocated_pdcch(test_ue)); + ASSERT_TRUE(ue_is_allocated_pdcch(test_ue)) + << fmt::format("Current slot={}, slot_update_srb_traffic={}, nof_slots_grid_is_busy={}, " + "candidate_srb_slot={}, check_alloc_slot={}", + current_slot, + slot_update_srb_traffic, + nof_slots_grid_is_busy, + candidate_srb_slot, + check_alloc_slot); ASSERT_TRUE(ue_is_allocated_pdsch(test_ue)); - check_alloc_slot = get_next_candidate_alloc_slot(candidate_srb_slot, nof_slots_grid_is_busy); + slot_update_srb_traffic = current_slot + generate_srb0_traffic_slot(); + nof_slots_grid_is_busy = generate_nof_slot_grid_occupancy(); + candidate_srb_slot = get_next_dl_slot(slot_update_srb_traffic); + check_alloc_slot = get_next_candidate_alloc_slot(candidate_srb_slot, nof_slots_grid_is_busy); } // Allocate buffer and occupy the grid to test the scheduler in advance scheduling. if (current_slot == slot_update_srb_traffic) { push_buffer_state_to_dl_ue(to_du_ue_index(du_idx), current_slot, MAC_SRB0_SDU_SIZE, GetParam().is_srb0); - auto fill_bw_grant = grant_info{bench->cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs, - ofdm_symbol_range{0, 14}, - bench->cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.crbs}; - - slot_point occupy_grid_slot = slot_update_srb_traffic; - unsigned occupy_grid_slot_cnt = 0; - while (occupy_grid_slot_cnt < nof_slots_grid_is_busy) { - // Only set the grid busy in the DL slots. - if (bench->cell_cfg.is_dl_enabled(occupy_grid_slot)) { - bench->res_grid[occupy_grid_slot].dl_res_grid.fill(fill_bw_grant); - occupy_grid_slot_cnt++; - } - occupy_grid_slot++; - } - - slot_update_srb_traffic = current_slot + generate_srb0_traffic_slot(); - nof_slots_grid_is_busy = generate_nof_slot_grid_occupancy(); - candidate_srb_slot = get_next_dl_slot(slot_update_srb_traffic); + // Mark resource grid as occupied. + fill_resource_grid(current_slot, + check_alloc_slot.to_uint() - current_slot.to_uint(), + bench->cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.crbs); } // Ack the HARQ processes that are waiting for ACK, otherwise the scheduler runs out of empty HARQs. @@ -745,7 +834,7 @@ class fallback_scheduler_retx : public base_fallback_tester, public ::testing::T explicit ue_retx_tester(const cell_configuration& cell_cfg_, ue& test_ue_, fallback_scheduler_retx* parent_) : cell_cfg(cell_cfg_), test_ue(test_ue_), parent(parent_) { - slot_update_srb_traffic = generate_srb1_next_update_delay(); + slot_update_srb_traffic = parent->current_slot + generate_srb1_next_update_delay(); nof_packet_to_tx = parent->SRB_PACKETS_TOT_TX; } @@ -755,17 +844,18 @@ class fallback_scheduler_retx : public base_fallback_tester, public ::testing::T // Wait until the slot to update the SRB0 traffic. case ue_state::idle: { if (sl == slot_update_srb_traffic and nof_packet_to_tx > 0) { - // Notify about SRB0 message in DL. + // Notify about SRB0/SRB1 message in DL. parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, parent->MAC_SRB0_SDU_SIZE, GetParam().is_srb0); state = ue_state::waiting_for_tx; } break; } - // Wait until the UE transmits the PDSCH with SRB0 or SRB1. + // Wait until the UE transmits the PDSCH with SRB0/SRB1. case ue_state::waiting_for_tx: { for (harq_id_t h_id = to_harq_id(0); h_id != MAX_HARQ_ID; h_id = to_harq_id(std::underlying_type_t(h_id) + 1)) { - if (test_ue.get_pcell().harqs.dl_harq(h_id).is_waiting_ack() == true) { + const auto& h_dl = test_ue.get_pcell().harqs.dl_harq(h_id); + if (h_dl.is_waiting_ack()) { ongoing_h_id = h_id; break; } @@ -784,7 +874,7 @@ class fallback_scheduler_retx : public base_fallback_tester, public ::testing::T if (h_dl.slot_ack() == sl) { static constexpr double ack_probability = 0.5f; ack_outcome = test_rgen::bernoulli(ack_probability); - if (ack_outcome == true) { + if (ack_outcome) { ++successful_tx_cnt; state = ue_state::reset_harq; } else { @@ -815,7 +905,7 @@ class fallback_scheduler_retx : public base_fallback_tester, public ::testing::T // ack_harq_process() function. case ue_state::reset_harq: { // Notify about SRB0 message in DL. - slot_update_srb_traffic = sl.to_uint() + generate_srb1_next_update_delay(); + slot_update_srb_traffic = sl + generate_srb1_next_update_delay(); ongoing_h_id = INVALID_HARQ_ID; state = ue_state::idle; @@ -836,11 +926,10 @@ class fallback_scheduler_retx : public base_fallback_tester, public ::testing::T } } - slot_point generate_srb1_next_update_delay() const + unsigned generate_srb1_next_update_delay() const { // Generate a random number of slots to wait until the next SRB1 buffer update. - return slot_point{to_numerology_value(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs), - test_rgen::uniform_int(20U, 40U)}; + return test_rgen::uniform_int(20U, 40U); } const cell_configuration& cell_cfg; @@ -905,11 +994,10 @@ INSTANTIATE_TEST_SUITE_P(fallback_scheduler, fallback_sched_test_params{.is_srb0 = false, .duplx_mode = duplex_mode::FDD}, fallback_sched_test_params{.is_srb0 = false, .duplx_mode = duplex_mode::TDD})); -class fallback_scheduler_srb1_segmentation : public base_fallback_tester, - public ::testing::TestWithParam +class fallback_scheduler_srb1_segmentation : public base_fallback_tester, public ::testing::TestWithParam { protected: - fallback_scheduler_srb1_segmentation() : base_fallback_tester(GetParam().duplx_mode) + fallback_scheduler_srb1_segmentation() : base_fallback_tester(GetParam()) { const unsigned k0 = 0; const sch_mcs_index max_msg4_mcs_index = 8; @@ -927,7 +1015,7 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, fallback_scheduler_srb1_segmentation* parent_) : cell_cfg(cell_cfg_), test_ue(test_ue_), parent(parent_) { - slot_update_srb_traffic = generate_srb1_next_update_delay(); + slot_update_srb_traffic = parent->current_slot + generate_srb1_next_update_delay(); nof_packet_to_tx = parent->SRB_PACKETS_TOT_TX; test_logger.set_level(srslog::basic_levels::debug); @@ -943,7 +1031,7 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, if (sl == slot_update_srb_traffic and nof_packet_to_tx > 0) { // Notify about SRB1 message in DL. pending_srb1_bytes = generate_srb1_buffer_size(); - parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, GetParam().is_srb0); + parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, false); latest_rlc_update_slot.emplace(sl); --nof_packet_to_tx; test_logger.debug("rnti={}, slot={}: pushing SRB1 traffic of {} bytes", test_ue.crnti, sl, pending_srb1_bytes); @@ -951,26 +1039,30 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, // When all pending bytes have been transmitted, generate the slot for the next traffic generation. if (sl > slot_update_srb_traffic and pending_srb1_bytes == 0 and nof_packet_to_tx > 0) { - slot_update_srb_traffic = sl.to_uint() + generate_srb1_next_update_delay(); + slot_update_srb_traffic = sl + generate_srb1_next_update_delay(); } // Generate an RLC buffer update every time a HARQ process gets transmitted for the first time. for (uint8_t h_id_idx = 0; h_id_idx != std::underlying_type_t(MAX_HARQ_ID); ++h_id_idx) { harq_id_t h_id = to_harq_id(h_id_idx); - auto& h_dl = test_ue.get_pcell().harqs.dl_harq(h_id); + dl_harq_process& h_dl = test_ue.get_pcell().harqs.dl_harq(h_id); if (h_dl.is_waiting_ack() and h_dl.tb(0).nof_retxs == 0 and h_dl.slot_tx() == sl) { const unsigned tb_idx = 0U; const unsigned tx_bytes = h_dl.last_alloc_params().tb[tb_idx]->tbs_bytes > MAX_MAC_SDU_SUBHEADER_SIZE ? h_dl.last_alloc_params().tb[tb_idx]->tbs_bytes - MAX_MAC_SDU_SUBHEADER_SIZE : 0U; - pending_srb1_bytes > tx_bytes ? pending_srb1_bytes -= tx_bytes : pending_srb1_bytes = 0U; + if (pending_srb1_bytes > tx_bytes) { + pending_srb1_bytes -= tx_bytes; + } else { + pending_srb1_bytes = 0U; + } test_logger.debug("rnti={}, slot={}: RLC buffer state update for h_id={} with {} bytes", test_ue.crnti, sl, to_harq_id(h_dl.id), pending_srb1_bytes); - parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, GetParam().is_srb0); + parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, false); latest_rlc_update_slot.emplace(sl); } } @@ -985,7 +1077,7 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, test_ue.crnti, sl, pending_srb1_bytes); - parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, GetParam().is_srb0); + parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, false); latest_rlc_update_slot.emplace(sl); } } @@ -1007,7 +1099,7 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, sl, to_harq_id(h_dl.id), pending_srb1_bytes); - parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, GetParam().is_srb0); + parent->push_buffer_state_to_dl_ue(test_ue.ue_index, sl, pending_srb1_bytes, false); } // Check if any HARQ process with pending transmissions is re-set by the scheduler. @@ -1047,12 +1139,11 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, } } - unsigned generate_srb1_buffer_size() { return test_rgen::uniform_int(128U, parent->MAX_MAC_SRB0_SDU_SIZE); } - slot_point generate_srb1_next_update_delay() const + unsigned generate_srb1_buffer_size() { return test_rgen::uniform_int(128U, parent->MAX_MAC_SRB0_SDU_SIZE); } + unsigned generate_srb1_next_update_delay() const { // Generate a random number of slots to wait until the next SRB1 buffer update. - return slot_point{to_numerology_value(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs), - test_rgen::uniform_int(20U, 40U)}; + return test_rgen::uniform_int(20U, 40U); } const cell_configuration& cell_cfg; @@ -1072,7 +1163,7 @@ class fallback_scheduler_srb1_segmentation : public base_fallback_tester, const unsigned SRB_PACKETS_TOT_TX = 10; const unsigned MAX_UES = 10; - const unsigned MAX_TEST_RUN_SLOTS = 100; + const unsigned MAX_TEST_RUN_SLOTS = 200; const unsigned MAX_MAC_SRB0_SDU_SIZE = 1600; std::vector ues_testers; @@ -1108,8 +1199,7 @@ TEST_P(fallback_scheduler_srb1_segmentation, test_scheduling_srb1_segmentation) INSTANTIATE_TEST_SUITE_P(fallback_scheduler, fallback_scheduler_srb1_segmentation, - testing::Values(fallback_sched_test_params{.is_srb0 = false, .duplx_mode = duplex_mode::FDD}, - fallback_sched_test_params{.is_srb0 = false, .duplx_mode = duplex_mode::TDD})); + testing::Values(duplex_mode::FDD, duplex_mode::TDD)); /////// UL Scheduling tests /////// diff --git a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp index 8f2dc8e67a..130c1be759 100644 --- a/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/logical_channel_test.cpp @@ -253,17 +253,6 @@ TEST(dl_logical_channel_test, check_scheduling_of_ue_con_res_id_mac_ce) ASSERT_EQ(subpdu.sched_bytes, lcid_dl_sch_t{ce_lcid}.sizeof_ce()); } -TEST(dl_logical_channel_test, pending_bytes_does_not_include_ue_con_res_id_mac_ce) -{ - dl_logical_channel_manager lch_mng; - - lcid_dl_sch_t ce_lcid = lcid_dl_sch_t::UE_CON_RES_ID; - const unsigned dummy_ce_payload = 0; - lch_mng.handle_mac_ce_indication({.ce_lcid = ce_lcid, .ce_payload = dummy_ce_payload}); - - ASSERT_EQ(lch_mng.pending_bytes(), 0); -} - TEST(dl_logical_channel_test, pending_ue_con_res_id_ce_bytes_does_not_include_other_mac_ce) { dl_logical_channel_manager lch_mng; @@ -294,3 +283,27 @@ TEST(ul_logical_channel_test, when_logical_channel_groups_are_inactive_then_no_u ul_lch_mng2.set_status(lcg_id, false); ASSERT_EQ(ul_lch_mng2.pending_bytes(), 0); } + +TEST(dl_logical_channel_test, assign_leftover_bytes_to_sdu_if_leftover_bytes_is_less_than_five_bytes) +{ + const unsigned tb_size = 309; + + dl_logical_channel_manager lch_mng; + lcid_dl_sch_t ce_lcid = lcid_dl_sch_t::UE_CON_RES_ID; + const unsigned dummy_ce_payload = 0; + lch_mng.handle_mac_ce_indication({.ce_lcid = ce_lcid, .ce_payload = dummy_ce_payload}); + lch_mng.set_status(LCID_SRB0, true); + lch_mng.set_status(LCID_SRB1, true); + lch_mng.handle_dl_buffer_status_indication(LCID_SRB0, 295); + lch_mng.handle_dl_buffer_status_indication(LCID_SRB1, 10000); + + dl_msg_lc_info subpdu; + + unsigned allocated_bytes = 0; + // ConRes occupies 7 bytes => 6 bytes ConRes CE + 1 bytes header. + allocated_bytes += lch_mng.allocate_ue_con_res_id_mac_ce(subpdu, tb_size); + // SRB0 SDU requires at least 298 bytes => 295 payload size + 3 bytes MAC header. Leftover bytes = 4 bytes. + allocated_bytes += lch_mng.allocate_mac_sdu(subpdu, tb_size - allocated_bytes, LCID_SRB0); + // Verify leftover bytes are assigned to the last SDU. + ASSERT_EQ(allocated_bytes, tb_size); +} diff --git a/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp b/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp index baac166f83..3e8f44c289 100644 --- a/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ta_manager_test.cpp @@ -109,9 +109,9 @@ TEST_P(ta_manager_tester, ta_cmd_is_successfully_triggered) } ASSERT_TRUE(ta_cmd_mac_ce_alloc.has_value()) << "Missing TA command CE allocation"; ASSERT_TRUE(ta_cmd_mac_ce_alloc->lcid == lcid_dl_sch_t::TA_CMD) << "TA command is not be triggered"; - ASSERT_TRUE(variant_holds_alternative(ta_cmd_mac_ce_alloc->ce_payload)) + ASSERT_TRUE(std::holds_alternative(ta_cmd_mac_ce_alloc->ce_payload)) << "TA command CE payload is absent"; - auto ta_cmd_ce = variant_get(ta_cmd_mac_ce_alloc->ce_payload); + auto ta_cmd_ce = std::get(ta_cmd_mac_ce_alloc->ce_payload); ASSERT_EQ(ta_cmd_ce.ta_cmd, new_ta_cmd) << "New TA command does not match the expected TA command value"; } @@ -137,9 +137,9 @@ TEST_P(ta_manager_tester, verify_computed_new_ta_cmd_based_on_multiple_n_ta_diff } ASSERT_TRUE(ta_cmd_mac_ce_alloc.has_value()) << "Missing TA command CE allocation"; ASSERT_TRUE(ta_cmd_mac_ce_alloc->lcid == lcid_dl_sch_t::TA_CMD) << "TA command is not be triggered"; - ASSERT_TRUE(variant_holds_alternative(ta_cmd_mac_ce_alloc->ce_payload)) + ASSERT_TRUE(std::holds_alternative(ta_cmd_mac_ce_alloc->ce_payload)) << "TA command CE payload is absent"; - auto ta_cmd_ce = variant_get(ta_cmd_mac_ce_alloc->ce_payload); + auto ta_cmd_ce = std::get(ta_cmd_mac_ce_alloc->ce_payload); ASSERT_EQ(ta_cmd_ce.ta_cmd, expected_new_ta_cmd) << "New TA command does not match the expected TA command value"; } diff --git a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp index ed89d07ed0..15720e4b0e 100644 --- a/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp +++ b/tests/unittests/scheduler/ue_scheduling/ue_grid_allocator_test.cpp @@ -104,23 +104,11 @@ class ue_grid_allocator_tester : public ::testing::Test ue_cell_grid_allocator alloc{expert_cfg, ues, srslog::fetch_basic_logger("SCHED")}; }; -TEST_F(ue_grid_allocator_tester, when_coreset0_grant_inside_coreset0_rb_lims_then_allocation_is_successful) -{ - ue& u = add_ue(to_du_ue_index(0), {}); - - ue_pdsch_grant grant{.user = &u, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(0), - .time_res_index = 0, - .crbs = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs()}; - - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); -} - TEST_F(ue_grid_allocator_tester, - when_grant_inside_coreset_start_and_coreset0_end_rb_lims_for_css_then_allocation_is_successful) + when_ue_dedicated_ss_is_css_then_allocation_is_within_coreset_start_crb_and_coreset0_end_crb) { + static const unsigned nof_bytes_to_schedule = 40U; + sched_ue_creation_request_message ue_creation_req = test_helpers::create_default_sched_ue_creation_request(this->cfg_builder_params); // Change SS type to common. @@ -134,45 +122,22 @@ TEST_F(ue_grid_allocator_tester, const crb_interval crbs = get_coreset_crbs((*ue_creation_req.cfg.cells)[0].serv_cell_cfg.init_dl_bwp.pdcch_cfg.value().coresets.back()); + const crb_interval crb_lims = { + crbs.start(), crbs.start() + cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().length()}; - ue_pdsch_grant grant{ - .user = &u, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(2), - .time_res_index = 0, - .crbs = {crbs.start(), - crbs.start() + cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().length()}}; - - ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); -} - -TEST_F(ue_grid_allocator_tester, when_using_fallback_dci_format_only_64_qam_mcs_table_is_used) -{ - sched_ue_creation_request_message ue_creation_req = - test_helpers::create_default_sched_ue_creation_request(this->cfg_builder_params); - // Change PDSCH MCS table to be used when using non-fallback DCI format. - (*ue_creation_req.cfg.cells)[0].serv_cell_cfg.init_dl_bwp.pdsch_cfg->mcs_table = srsran::pdsch_mcs_table::qam256; - ue_creation_req.ue_index = to_du_ue_index(0); - ue_creation_req.crnti = to_rnti(0x4601); - - const ue& u = add_ue(ue_creation_req); - - // SearchSpace#1 uses fallback DCI format hence the 64QAM MCS table must be used. - const ue_pdsch_grant grant{.user = &u, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(0), - .time_res_index = 0, - .crbs = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs()}; + ue_pdsch_grant grant{.user = &u, + .cell_index = to_du_cell_index(0), + .h_id = to_harq_id(0), + .recommended_nof_bytes = nof_bytes_to_schedule}; ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); - ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.codewords.back().mcs_table, - srsran::pdsch_mcs_table::qam64); + ASSERT_TRUE(crb_lims.contains(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1())); } TEST_F(ue_grid_allocator_tester, when_using_non_fallback_dci_format_use_mcs_table_set_in_pdsch_cfg) { + static const unsigned nof_bytes_to_schedule = 40U; + sched_ue_creation_request_message ue_creation_req = test_helpers::create_default_sched_ue_creation_request(this->cfg_builder_params); // Change PDSCH MCS table to be used when using non-fallback DCI format. @@ -183,12 +148,10 @@ TEST_F(ue_grid_allocator_tester, when_using_non_fallback_dci_format_use_mcs_tabl const ue& u = add_ue(ue_creation_req); // SearchSpace#2 uses non-fallback DCI format hence the MCS table set in dedicated PDSCH configuration must be used. - const ue_pdsch_grant grant{.user = &u, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(2), - .time_res_index = 0, - .crbs = cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs()}; + const ue_pdsch_grant grant{.user = &u, + .cell_index = to_du_cell_index(0), + .h_id = to_harq_id(0), + .recommended_nof_bytes = nof_bytes_to_schedule}; ASSERT_EQ(alloc.allocate_dl_grant(grant), alloc_outcome::success); ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.codewords.back().mcs_table, @@ -206,33 +169,24 @@ TEST_F(ue_grid_allocator_tester, remaining_dl_rbs_are_allocated_if_max_pucch_per ue_creation_req.crnti = to_rnti(0x4602); const ue& u2 = add_ue(ue_creation_req); - const crb_interval grant1_crbs = {cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().start(), - cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().start() + - 2}; - const ue_pdsch_grant grant1{.user = &u1, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(2), - .time_res_index = 0, - .crbs = grant1_crbs}; + static const unsigned sched_bytes = 20U; + const ue_pdsch_grant grant1{ + .user = &u1, .cell_index = to_du_cell_index(0), .h_id = to_harq_id(0), .recommended_nof_bytes = sched_bytes}; // Successfully allocates RBs corresponding to the grant. ASSERT_EQ(alloc.allocate_dl_grant(grant1), alloc_outcome::success); - ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().length(), grant1_crbs.length()); - - const crb_interval grant2_crbs = { - cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().start() + 2, - cell_cfg.dl_cfg_common.init_dl_bwp.pdcch_common.coreset0->coreset0_crbs().start() + 4}; - const ue_pdsch_grant grant2{.user = &u2, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .ss_id = to_search_space_id(2), - .time_res_index = 0, - .crbs = grant2_crbs}; + ASSERT_GE(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.codewords.back().tb_size_bytes, sched_bytes); + + // Since UE dedicated SearchSpace is a UE specific SearchSpace (Not CSS). Entire BWP CRBs can be used for allocation. + const unsigned total_crbs = cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.crbs.length(); + const unsigned crbs_allocated = res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().length(); + + const ue_pdsch_grant grant2{ + .user = &u2, .cell_index = to_du_cell_index(0), .h_id = to_harq_id(0), .recommended_nof_bytes = sched_bytes}; // Allocates all remaining RBs to UE2. ASSERT_EQ(alloc.allocate_dl_grant(grant2), alloc_outcome::success); - ASSERT_GT(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().length(), grant2_crbs.length()); + ASSERT_EQ(res_grid[0].result.dl.ue_grants.back().pdsch_cfg.rbs.type1().length(), (total_crbs - crbs_allocated)); } TEST_F(ue_grid_allocator_tester, remaining_ul_rbs_are_allocated_if_max_ul_grant_per_slot_is_reached) @@ -246,35 +200,35 @@ TEST_F(ue_grid_allocator_tester, remaining_ul_rbs_are_allocated_if_max_ul_grant_ ue_creation_req.crnti = to_rnti(0x4602); const ue& u2 = add_ue(ue_creation_req); - const crb_interval grant1_crbs = {cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start(), - cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start() + 2}; - const ue_pusch_grant grant1{.user = &u1, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .crbs = grant1_crbs, - .ss_id = to_search_space_id(2), - .mcs = sch_mcs_index{24}}; + const unsigned recommended_nof_bytes_to_schedule = 200U; + const unsigned max_nof_rbs_to_schedule = 10U; + + const crb_interval cell_crbs = {cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start(), + cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.stop()}; + const ue_pusch_grant grant1{.user = &u1, + .cell_index = to_du_cell_index(0), + .h_id = to_harq_id(0), + .recommended_nof_bytes = recommended_nof_bytes_to_schedule, + .max_nof_rbs = max_nof_rbs_to_schedule}; // Successfully allocates RBs corresponding to the grant. ASSERT_EQ(alloc.allocate_ul_grant(grant1), alloc_outcome::success); unsigned k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; - ASSERT_EQ(res_grid[k2].result.ul.puschs.back().pusch_cfg.rbs.type1().length(), grant1_crbs.length()); - const crb_interval grant2_crbs = {cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start() + 2, - cell_cfg.ul_cfg_common.init_ul_bwp.generic_params.crbs.start() + 4}; - const ue_pusch_grant grant2{.user = &u2, - .cell_index = to_du_cell_index(0), - .h_id = to_harq_id(0), - .crbs = grant1_crbs, - .ss_id = to_search_space_id(2), - .mcs = sch_mcs_index{24}}; + const unsigned remaining_crbs = + cell_crbs.length() - res_grid[k2].result.ul.puschs.back().pusch_cfg.rbs.type1().length(); + const ue_pusch_grant grant2{.user = &u2, + .cell_index = to_du_cell_index(0), + .h_id = to_harq_id(0), + .recommended_nof_bytes = recommended_nof_bytes_to_schedule, + .max_nof_rbs = max_nof_rbs_to_schedule}; // Allocates all remaining RBs to UE2. ASSERT_EQ(alloc.allocate_ul_grant(grant2), alloc_outcome::success); k2 = cell_cfg.ul_cfg_common.init_ul_bwp.pusch_cfg_common ->pusch_td_alloc_list[res_grid[0].result.dl.ul_pdcchs.back().dci.c_rnti_f0_1.time_resource] .k2; - ASSERT_GT(res_grid[k2].result.ul.puschs.back().pusch_cfg.rbs.type1().length(), grant2_crbs.length()); + ASSERT_EQ(res_grid[k2].result.ul.puschs.back().pusch_cfg.rbs.type1().length(), remaining_crbs); } diff --git a/tests/unittests/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher_test.cpp deleted file mode 100644 index 559d0f8753..0000000000 --- a/tests/unittests/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher_test.cpp +++ /dev/null @@ -1,172 +0,0 @@ -/* - * - * Copyright 2021-2024 Software Radio Systems Limited - * - * This file is part of srsRAN. - * - * srsRAN is free software: you can redistribute it and/or modify - * it under the terms of the GNU Affero General Public License as - * published by the Free Software Foundation, either version 3 of - * the License, or (at your option) any later version. - * - * srsRAN is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Affero General Public License for more details. - * - * A copy of the GNU Affero General Public License can be found in - * the LICENSE file in the top-level directory of this distribution - * and at http://www.gnu.org/licenses/. - * - */ - -#include "../test_utils/config_generators.h" -#include "../test_utils/dummy_test_components.h" -#include "lib/scheduler/ue_scheduling/ue_pdsch_param_candidate_searcher.h" -#include - -using namespace srsran; - -class ue_pdsch_param_candidate_searcher_test : public ::testing::Test -{ -protected: - ue_pdsch_param_candidate_searcher_test() : - cell_cfg(*[this]() { - cell_cfg_list.emplace(to_du_cell_index(0), - std::make_unique( - sched_cfg, test_helpers::make_default_sched_cell_configuration_request())); - return cell_cfg_list[to_du_cell_index(0)].get(); - }()), - logger(srslog::fetch_basic_logger("SCHED", true)), - next_slot(test_helpers::generate_random_slot_point(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs)) - { - logger.set_level(srslog::basic_levels::debug); - srslog::init(); - - // Create UE. - sched_ue_creation_request_message ue_creation_req = test_helpers::create_default_sched_ue_creation_request(); - ue_creation_req.ue_index = to_du_ue_index(0); - ue_creation_req.crnti = to_rnti(0x4601 + (unsigned)ue_creation_req.ue_index); - for (lcid_t lcid : std::array{uint_to_lcid(1), uint_to_lcid(2), uint_to_lcid(4)}) { - ue_creation_req.cfg.lc_config_list->push_back(config_helpers::create_default_logical_channel_config(lcid)); - } - ue_ded_cfg.emplace(ue_creation_req.ue_index, ue_creation_req.crnti, cell_cfg_list, ue_creation_req.cfg); - ue_ptr = std::make_unique( - ue_creation_command{*ue_ded_cfg, ue_creation_req.starts_in_fallback, harq_timeout_handler}); - ue_cc = &ue_ptr->get_cell(to_ue_cell_index(0)); - } - - void run_slot() { next_slot++; } - - void handle_harq_newtx(harq_id_t harq_id, unsigned k1 = 4) - { - const search_space_info& ss = ue_cc->cfg().search_space(to_search_space_id(2)); - - pdsch_information pdsch{ue_ptr->crnti, - &ss.bwp->dl_common->generic_params, - ss.coreset, - vrb_alloc{vrb_interval{0, 5}}, - ss.pdsch_time_domain_list[0].symbols, - {}, - {}, - 0, - 1, - false, - search_space_set_type::ue_specific, - dci_dl_format::f1_1, - harq_id, - std::nullopt}; - - ue_cc->harqs.dl_harq(harq_id).new_tx(next_slot, k1, 4, 0, 15, 1); - ue_cc->harqs.dl_harq(harq_id).save_alloc_params(dl_harq_sched_context{dci_dl_rnti_config_type::c_rnti_f1_1}, pdsch); - } - - const scheduler_expert_config sched_cfg = config_helpers::make_default_scheduler_expert_config(); - const scheduler_ue_expert_config& expert_cfg{sched_cfg.ue}; - cell_common_configuration_list cell_cfg_list; - const cell_configuration& cell_cfg; - scheduler_harq_timeout_dummy_handler harq_timeout_handler; - std::optional ue_ded_cfg; - - srslog::basic_logger& logger; - - std::unique_ptr ue_ptr; - ue_cell* ue_cc = nullptr; - - slot_point next_slot; -}; - -TEST_F(ue_pdsch_param_candidate_searcher_test, when_no_pending_newtx_bytes_then_zero_candidates) -{ - ue_pdsch_param_candidate_searcher searcher(*ue_ptr, to_ue_cell_index(0), false, slot_point{0, 0}, logger); - EXPECT_EQ(searcher.begin(), searcher.end()); -} - -TEST_F(ue_pdsch_param_candidate_searcher_test, when_no_pending_retxs_then_zero_candidates) -{ - ue_pdsch_param_candidate_searcher searcher2(*ue_ptr, to_ue_cell_index(0), true, slot_point{0, 0}, logger); - EXPECT_EQ(searcher2.begin(), searcher2.end()); -} - -TEST_F(ue_pdsch_param_candidate_searcher_test, when_pending_newtx_bytes_then_there_are_newtx_candidates) -{ - ue_ptr->handle_dl_buffer_state_indication(dl_buffer_state_indication_message{ue_ptr->ue_index, uint_to_lcid(4), 100}); - - ue_pdsch_param_candidate_searcher searcher(*ue_ptr, to_ue_cell_index(0), false, next_slot, logger); - EXPECT_NE(searcher.begin(), searcher.end()); - - std::set harqs_visited; - std::set candidates_visited; - for (const ue_pdsch_param_candidate_searcher::candidate& candidate : searcher) { - harqs_visited.insert(candidate.harq().id); - EXPECT_TRUE(candidate.harq().empty()); - ASSERT_EQ(&ue_cc->harqs.dl_harq(candidate.harq().id), &candidate.harq()); - EXPECT_EQ(&ue_cc->cfg().search_space(candidate.ss().cfg->get_id()), &candidate.ss()); - EXPECT_LT(candidate.pdsch_td_res_index(), candidate.ss().pdsch_time_domain_list.size()); - EXPECT_TRUE(candidates_visited.insert(candidate).second) << "Iterating through the same candidate twice"; - } - ASSERT_EQ(harqs_visited.size(), 1) << "In case of newTx, we should not iterate through multiple HARQs"; -} - -TEST_F(ue_pdsch_param_candidate_searcher_test, when_harqs_with_pending_retx_exist_then_there_are_retx_candidates) -{ - unsigned nof_rexts = test_rgen::uniform_int(1, 16); - std::vector harq_ids(nof_rexts); - std::iota(harq_ids.begin(), harq_ids.end(), 0); - std::shuffle(harq_ids.begin(), harq_ids.end(), test_rgen::get()); - - for (unsigned hid : harq_ids) { - handle_harq_newtx(to_harq_id(hid)); - } - dl_harq_process& first_h = ue_cc->harqs.dl_harq(to_harq_id(harq_ids[0])); - - while (first_h.slot_ack() != next_slot) { - // Status: There shouldn't be candidates for reTx. - ue_pdsch_param_candidate_searcher searcher(*ue_ptr, to_ue_cell_index(0), true, next_slot, logger); - EXPECT_EQ(searcher.begin(), searcher.end()); - - run_slot(); - } - - // Action: NACK the HARQs. - for (unsigned hid : harq_ids) { - ue_cc->harqs.dl_harq(to_harq_id(hid)).ack_info(0, srsran::mac_harq_ack_report_status::nack, std::nullopt); - EXPECT_TRUE(ue_cc->harqs.dl_harq(to_harq_id(hid)).has_pending_retx()); - } - - // Status: There should be candidates for reTx. - ue_pdsch_param_candidate_searcher searcher(*ue_ptr, to_ue_cell_index(0), true, next_slot, logger); - EXPECT_NE(searcher.begin(), searcher.end()); - - std::set harqs_visited; - std::set candidates_visited; - for (const ue_pdsch_param_candidate_searcher::candidate& candidate : searcher) { - harqs_visited.insert(candidate.harq().id); - EXPECT_TRUE(candidate.harq().has_pending_retx()); - ASSERT_EQ(&ue_cc->harqs.dl_harq(candidate.harq().id), &candidate.harq()); - EXPECT_EQ(&ue_cc->cfg().search_space(candidate.ss().cfg->get_id()), &candidate.ss()); - EXPECT_LT(candidate.pdsch_td_res_index(), candidate.ss().pdsch_time_domain_list.size()); - EXPECT_TRUE(candidates_visited.insert(candidate).second) << "Iterating through the same candidate twice"; - } - ASSERT_EQ(harqs_visited.size(), nof_rexts) << "HARQ candidates should match the number of pending reTxs"; -} diff --git a/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp b/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp new file mode 100644 index 0000000000..7d18ea87d9 --- /dev/null +++ b/tests/unittests/scheduler/ue_scheduling/ue_pxsch_alloc_param_candidate_searcher_test.cpp @@ -0,0 +1,101 @@ +/* + * + * Copyright 2021-2024 Software Radio Systems Limited + * + * This file is part of srsRAN. + * + * srsRAN is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * srsRAN is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * A copy of the GNU Affero General Public License can be found in + * the LICENSE file in the top-level directory of this distribution + * and at http://www.gnu.org/licenses/. + * + */ + +#include "../test_utils/config_generators.h" +#include "../test_utils/dummy_test_components.h" +#include "lib/scheduler/ue_scheduling/ue_pdsch_alloc_param_candidate_searcher.h" +#include "lib/scheduler/ue_scheduling/ue_pusch_alloc_param_candidate_searcher.h" +#include + +using namespace srsran; + +class ue_pxsch_alloc_param_candidate_searcher_test : public ::testing::Test +{ +protected: + ue_pxsch_alloc_param_candidate_searcher_test() : + cell_cfg(*[this]() { + cell_cfg_list.emplace(to_du_cell_index(0), + std::make_unique( + sched_cfg, test_helpers::make_default_sched_cell_configuration_request())); + return cell_cfg_list[to_du_cell_index(0)].get(); + }()), + logger(srslog::fetch_basic_logger("SCHED", true)), + next_slot(test_helpers::generate_random_slot_point(cell_cfg.dl_cfg_common.init_dl_bwp.generic_params.scs)) + { + logger.set_level(srslog::basic_levels::debug); + srslog::init(); + + sched_ue_creation_request_message ue_creation_req = test_helpers::create_default_sched_ue_creation_request(); + ue_creation_req.ue_index = to_du_ue_index(0); + ue_creation_req.crnti = to_rnti(0x4601 + (unsigned)ue_creation_req.ue_index); + for (lcid_t lcid : std::array{uint_to_lcid(1), uint_to_lcid(2), uint_to_lcid(4)}) { + ue_creation_req.cfg.lc_config_list->push_back(config_helpers::create_default_logical_channel_config(lcid)); + } + ue_ded_cfg.emplace(ue_creation_req.ue_index, ue_creation_req.crnti, cell_cfg_list, ue_creation_req.cfg); + ue_ptr = std::make_unique( + ue_creation_command{*ue_ded_cfg, ue_creation_req.starts_in_fallback, harq_timeout_handler}); + ue_cc = &ue_ptr->get_cell(to_ue_cell_index(0)); + } + + void run_slot() { next_slot++; } + + const scheduler_expert_config sched_cfg = config_helpers::make_default_scheduler_expert_config(); + const scheduler_ue_expert_config& expert_cfg{sched_cfg.ue}; + cell_common_configuration_list cell_cfg_list; + const cell_configuration& cell_cfg; + scheduler_harq_timeout_dummy_handler harq_timeout_handler; + std::optional ue_ded_cfg; + + srslog::basic_logger& logger; + + std::unique_ptr ue_ptr; + ue_cell* ue_cc = nullptr; + + slot_point next_slot; +}; + +TEST_F(ue_pxsch_alloc_param_candidate_searcher_test, only_searchspaces_in_ue_dedicated_cfg_is_considered) +{ + const harq_id_t h_id = to_harq_id(0); + span ss_list = ue_cc->cfg().cfg_dedicated().init_dl_bwp.pdcch_cfg->search_spaces; + + ue_pdsch_alloc_param_candidate_searcher dl_searcher( + *ue_ptr, to_du_cell_index(0), ue_cc->harqs.dl_harq(h_id), slot_point{0, 0}); + ASSERT_TRUE(not dl_searcher.is_empty()); + for (const auto& candidate : dl_searcher) { + bool ss_present_in_ue_ded_cfg = + std::find_if(ss_list.begin(), ss_list.end(), [&candidate](const search_space_configuration& ss_cfg) { + return ss_cfg.get_id() == candidate.ss().cfg->get_id(); + }) != ss_list.end(); + ASSERT_TRUE(ss_present_in_ue_ded_cfg); + } + ue_pusch_alloc_param_candidate_searcher ul_searcher( + *ue_ptr, to_du_cell_index(0), ue_cc->harqs.ul_harq(h_id), slot_point{0, 0}); + ASSERT_TRUE(not dl_searcher.is_empty()); + for (const auto& candidate : ul_searcher) { + bool ss_present_in_ue_ded_cfg = + std::find_if(ss_list.begin(), ss_list.end(), [&candidate](const search_space_configuration& ss_cfg) { + return ss_cfg.get_id() == candidate.ss().cfg->get_id(); + }) != ss_list.end(); + ASSERT_TRUE(ss_present_in_ue_ded_cfg); + } +} diff --git a/tests/unittests/security/security_test.cpp b/tests/unittests/security/security_test.cpp index 8941245464..e0e2cfd816 100644 --- a/tests/unittests/security/security_test.cpp +++ b/tests/unittests/security/security_test.cpp @@ -31,7 +31,7 @@ using namespace srsran::security; /// Converts a hex string (e.g. 01FA02) to a sec_as_key. sec_key make_sec_key(std::string hex_str) { - byte_buffer key_buf = make_byte_buffer(hex_str); + byte_buffer key_buf = make_byte_buffer(hex_str).value(); sec_key key = {}; std::copy(key_buf.begin(), key_buf.end(), key.begin()); return key; @@ -40,7 +40,7 @@ sec_key make_sec_key(std::string hex_str) /// Converts a hex string (e.g. 01FA02) to a sec_128_as_key. sec_128_key make_sec_128_key(std::string hex_str) { - byte_buffer key_buf = make_byte_buffer(hex_str); + byte_buffer key_buf = make_byte_buffer(hex_str).value(); sec_128_key key = {}; std::copy(key_buf.begin(), key_buf.end(), key.begin()); return key; @@ -77,8 +77,8 @@ TEST(security_nea1_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -109,8 +109,8 @@ TEST(security_nea1_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -136,8 +136,8 @@ TEST(security_nea1_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -167,8 +167,8 @@ TEST(security_nea1_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -200,8 +200,8 @@ TEST(security_nea1_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -245,8 +245,8 @@ TEST(security_nea1_test, testset6) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -270,8 +270,8 @@ TEST(security_nia1_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -296,8 +296,8 @@ TEST(security_nia1_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -323,8 +323,8 @@ TEST(security_nia1_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -351,8 +351,8 @@ TEST(security_nia1_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -378,8 +378,8 @@ TEST(security_nia1_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -410,8 +410,8 @@ TEST(security_nia1_test, testset6) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -473,8 +473,8 @@ TEST(security_nia1_test, testset7) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -499,8 +499,8 @@ TEST(security_nea2_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -528,8 +528,8 @@ TEST(security_nea2_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -553,8 +553,8 @@ TEST(security_nea2_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -582,8 +582,8 @@ TEST(security_nea2_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -613,8 +613,8 @@ TEST(security_nea2_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -656,8 +656,8 @@ TEST(security_nea2_test, testset6) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -684,8 +684,8 @@ TEST(security_nia2_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -710,8 +710,8 @@ TEST(security_nia2_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check { @@ -751,8 +751,8 @@ TEST(security_nia2_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -781,8 +781,8 @@ TEST(security_nia2_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -809,8 +809,8 @@ TEST(security_nia2_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check { @@ -851,8 +851,8 @@ TEST(security_nia2_test, testset6) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -886,8 +886,8 @@ TEST(security_nia2_test, testset7) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -949,8 +949,8 @@ TEST(security_nia2_test, testset8) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(ik_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mact_buf = make_byte_buffer(mact_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mact_buf = make_byte_buffer(mact_cstr).value(); // Apply integrity check { @@ -989,8 +989,8 @@ TEST(security_nea3_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -1020,8 +1020,8 @@ TEST(security_nea3_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -1055,8 +1055,8 @@ TEST(security_nea3_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -1096,8 +1096,8 @@ TEST(security_nea3_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -1141,8 +1141,8 @@ TEST(security_nea3_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer plaintext = make_byte_buffer(plaintext_cstr); - byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr); + byte_buffer plaintext = make_byte_buffer(plaintext_cstr).value(); + byte_buffer ciphertext = make_byte_buffer(ciphertext_cstr).value(); // Apply ciphering and compare results byte_buffer_view plaintext_view{plaintext}; @@ -1168,8 +1168,8 @@ TEST(security_nia3_test, testset1) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mac_buf = make_byte_buffer(mac_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mac_buf = make_byte_buffer(mac_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -1196,8 +1196,8 @@ TEST(security_nia3_test, testset2) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mac_buf = make_byte_buffer(mac_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mac_buf = make_byte_buffer(mac_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -1225,8 +1225,8 @@ TEST(security_nia3_test, testset3) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mac_buf = make_byte_buffer(mac_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mac_buf = make_byte_buffer(mac_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -1258,8 +1258,8 @@ TEST(security_nia3_test, testset4) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mac_buf = make_byte_buffer(mac_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mac_buf = make_byte_buffer(mac_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; @@ -1299,8 +1299,8 @@ TEST(security_nia3_test, testset5) // Pack hex strings into srsran types sec_128_key key = make_sec_128_key(key_cstr); security_direction dir = static_cast(direction); - byte_buffer message = make_byte_buffer(message_cstr); - byte_buffer mac_buf = make_byte_buffer(mac_cstr); + byte_buffer message = make_byte_buffer(message_cstr).value(); + byte_buffer mac_buf = make_byte_buffer(mac_cstr).value(); // Apply integrity check byte_buffer_view message_view{message}; diff --git a/tests/unittests/support/network/CMakeLists.txt b/tests/unittests/support/network/CMakeLists.txt index 839f7c63da..4e8370ab4c 100644 --- a/tests/unittests/support/network/CMakeLists.txt +++ b/tests/unittests/support/network/CMakeLists.txt @@ -18,8 +18,8 @@ # and at http://www.gnu.org/licenses/. # -set_directory_properties(PROPERTIES LABELS "io_broker") +set_directory_properties(PROPERTIES LABELS "io_broker;tsan") add_executable(network_test transport_layer_address_test.cpp io_broker_epoll_test.cpp io_timer_source_test.cpp) target_link_libraries(network_test srsran_network srsran_support srslog gtest gtest_main) -gtest_discover_tests(network_test) \ No newline at end of file +add_test(network_test network_test) \ No newline at end of file diff --git a/tests/unittests/support/network/io_broker_epoll_test.cpp b/tests/unittests/support/network/io_broker_epoll_test.cpp index 0288d6bca9..f211156880 100644 --- a/tests/unittests/support/network/io_broker_epoll_test.cpp +++ b/tests/unittests/support/network/io_broker_epoll_test.cpp @@ -22,6 +22,7 @@ #include "srsran/adt/optional.h" #include "srsran/support/io/io_broker_factory.h" +#include "srsran/support/io/unique_fd.h" #include #include #include @@ -45,11 +46,11 @@ class io_broker_epoll : public ::testing::Test void data_receive_callback() { - std::lock_guard lock(rx_mutex); // receive data on provided fd char rx_buf[1024]; - int bytes = read(socket_fd, rx_buf, sizeof(rx_buf)); + int bytes = read(socket_fd.value(), rx_buf, sizeof(rx_buf)); + std::lock_guard lock(rx_mutex); total_rx_bytes += bytes; if (socket_type == SOCK_DGRAM) { @@ -74,9 +75,9 @@ class io_broker_epoll : public ::testing::Test } // create server socket - socket_fd = socket(AF_UNIX, SOCK_DGRAM, 0); + socket_fd = unique_fd{socket(AF_UNIX, SOCK_DGRAM, 0)}; socket_type = SOCK_DGRAM; - ASSERT_NE(socket_fd, -1); + ASSERT_TRUE(socket_fd.is_open()); // prepare server address // memset(&server_addr, 0, sizeof(struct sockaddr_un)); @@ -84,7 +85,7 @@ class io_broker_epoll : public ::testing::Test strncpy(server_addr_un.sun_path, socket_filename.c_str(), socket_filename.length()); // bind server - ret = bind(socket_fd, (struct sockaddr*)&server_addr_un, sizeof(server_addr_un)); + ret = bind(socket_fd.value(), (struct sockaddr*)&server_addr_un, sizeof(server_addr_un)); ASSERT_NE(ret, -1); // listen+accept? @@ -96,7 +97,7 @@ class io_broker_epoll : public ::testing::Test strncpy(client_addr_un.sun_path, socket_filename.c_str(), socket_filename.length()); // connect client to server_filename - ret = connect(socket_fd, (struct sockaddr*)&client_addr_un, sizeof(client_addr_un)); + ret = connect(socket_fd.value(), (struct sockaddr*)&client_addr_un, sizeof(client_addr_un)); // perror("socket failed"); ASSERT_NE(ret, -1); } @@ -131,13 +132,13 @@ class io_broker_epoll : public ::testing::Test void create_af_init_sockets(int type) { // create server socket - socket_fd = socket(AF_INET, type, 0); + socket_fd = unique_fd{socket(AF_INET, type, 0)}; socket_type = type; - ASSERT_NE(socket_fd, -1); + ASSERT_TRUE(socket_fd.is_open()); // configure socket as reusable to allow multiple runs int enable = 1; - ASSERT_NE(setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)), -1); + ASSERT_NE(setsockopt(socket_fd.value(), SOL_SOCKET, SO_REUSEADDR, &enable, sizeof(int)), -1); // prepare server address memset(&server_addr_in, 0, sizeof(struct sockaddr_in)); @@ -146,12 +147,12 @@ class io_broker_epoll : public ::testing::Test server_addr_in.sin_port = htons(0); // bind server - int ret = bind(socket_fd, (struct sockaddr*)&server_addr_in, sizeof(server_addr_in)); + int ret = bind(socket_fd.value(), (struct sockaddr*)&server_addr_in, sizeof(server_addr_in)); // perror("socket failed"); ASSERT_NE(ret, -1); // get bind port - std::optional port = get_bind_port(socket_fd); + std::optional port = get_bind_port(socket_fd.value()); ASSERT_TRUE(port.has_value()); ASSERT_NE(port.value(), 0); // update server address @@ -164,34 +165,35 @@ class io_broker_epoll : public ::testing::Test client_addr_in.sin_port = htons(port.value()); // connect client to server - ret = connect(socket_fd, (struct sockaddr*)&server_addr_in, sizeof(server_addr_in)); + ret = connect(socket_fd.value(), (struct sockaddr*)&server_addr_in, sizeof(server_addr_in)); ASSERT_NE(ret, -1); } void add_socket_to_epoll() { fd_handle = epoll_broker->register_fd( - socket_fd, [this]() { data_receive_callback(); }, [this](io_broker::error_code code) { error_callback(code); }); + socket_fd.value(), + [this]() { data_receive_callback(); }, + [this](io_broker::error_code code) { error_callback(code); }); ASSERT_TRUE(fd_handle.registered()); } void rem_socket_from_epoll() { - if (socket_fd >= 0) { + if (socket_fd.is_open()) { EXPECT_TRUE(fd_handle.reset()); - EXPECT_NE(close(socket_fd), -1); - socket_fd = -1; + ASSERT_TRUE(socket_fd.close()); } } void send_on_socket() const { // send text - int ret = send(socket_fd, tx_buf.c_str(), tx_buf.length(), 0); + int ret = send(socket_fd.value(), tx_buf.c_str(), tx_buf.length(), 0); ASSERT_EQ(ret, tx_buf.length()); } - void run_tx_rx_test(std::chrono::milliseconds timeout_ms = std::chrono::milliseconds(1000)) + void run_tx_rx_test() { const int count = 5; int run = count; @@ -201,14 +203,12 @@ class io_broker_epoll : public ::testing::Test // wait until all bytes are received std::unique_lock lock(rx_mutex); - if (!rx_cvar.wait_for(lock, timeout_ms, [this]() { return total_rx_bytes >= tx_buf.length() * count; })) { - FAIL() << "Timeout: received only " << total_rx_bytes << " of " << tx_buf.length() * count << " Bytes."; - } + rx_cvar.wait(lock, [this]() { return total_rx_bytes >= tx_buf.length() * count; }); ASSERT_EQ(total_rx_bytes, tx_buf.length() * count); } std::unique_ptr epoll_broker; - int socket_fd = 0; + unique_fd socket_fd; int socket_type = 0; io_broker::subscriber fd_handle; @@ -261,7 +261,7 @@ TEST_F(io_broker_epoll, reentrant_handle_and_deregistration) std::future fut = p.get_future(); io_broker::subscriber handle; - handle = this->epoll_broker->register_fd(socket_fd, [&]() { + handle = this->epoll_broker->register_fd(socket_fd.value(), [&]() { auto* p_copy = &p; bool ret = handle.reset(); p_copy->set_value(ret); diff --git a/utils/trx_srsran/trx_srsran.cpp b/utils/trx_srsran/trx_srsran.cpp index 2439d90b5a..f83d7cc817 100644 --- a/utils/trx_srsran/trx_srsran.cpp +++ b/utils/trx_srsran/trx_srsran.cpp @@ -165,7 +165,7 @@ struct trx_srsran_session_context { std::array rx_port_channel_gain; unsigned tx_samples_per_packet; // Transmit noise spectral density in dB/Hz if present. Otherwise, noise generator is disabled. - optional noise_spd = nullopt; + std::optional noise_spd = std::nullopt; // Random generator. std::mt19937 rgen; // Random distribution for AWGN.