diff --git a/CMakeLists.txt b/CMakeLists.txt index faa3d9dc35..b78b50e5f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,6 +23,7 @@ cmake_minimum_required (VERSION 3.16) project (OpenAirInterface LANGUAGES C CXX) +set(CMAKE_CXX_STANDARD 17) ######################################################### # Base directories, compatible with legacy OAI building # @@ -30,6 +31,10 @@ project (OpenAirInterface LANGUAGES C CXX) set (OPENAIR_DIR ${CMAKE_SOURCE_DIR}) include("cmake_targets/macros.cmake") +if(NOT DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_SOURCE_CACHE ~/.cache/cpm/) +endif() +include("cmake_targets/CPM.cmake") ############################## ### CCache: reduce compilation time @@ -288,6 +293,8 @@ set_property(CACHE KPM_VERSION PROPERTY STRINGS "KPM_V2_03" "KPM_V3_00") message(STATUS "Selected KPM Version: ${KPM_VERSION}") +add_boolean_option(ENABLE_IMSCOPE OFF "Enable phy scope based on imgui" ON) + ################################################## # ASN.1 grammar C code generation & dependencies # ################################################## @@ -1066,7 +1073,6 @@ set(PHY_SRC_UE ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_ulsch_coding.c ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_decoding.c - ${OPENAIR1_DIR}/PHY/NR_UE_TRANSPORT/nr_dlsch_llr_computation.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_tbs_tools.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_prach_common.c ${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_sch_dmrs.c @@ -2340,30 +2346,24 @@ if(ENABLE_TESTS) find_package(GTest) if (NOT GTest_FOUND) message(STATUS "GTest package not found, will download googletest automatically. To prevent that install google test on your system (libgtest-dev)") - include(FetchContent) - FetchContent_Declare( - googletest - GIT_REPOSITORY https://github.com/google/googletest.git - GIT_TAG 58d77fa8070e8cec2dc1ed015d66b454c8d78850 # 1.12.1 + CPMAddPackage( + NAME googletest + GITHUB_REPOSITORY google/googletest + GIT_TAG release-1.12.1 + VERSION 1.12.1 + OPTIONS "INSTALL_GTEST OFF" "gtest_force_shared_crt" "BUILD_GMOCK OFF" ) - set(BUILD_GMOCK OFF) - set(INSTALL_GTEST OFF) - FetchContent_MakeAvailable(googletest) - add_library(GTest::gtest ALIAS gtest) - add_library(GTest::gtest_main ALIAS gtest_main) endif() find_package(benchmark) if (NOT benchmark_FOUND) message(STATUS "benchmark package not found, will download benchmark automatically. To prevent that install google benchmark on your system (libbenchmark-dev)") - include(FetchContent) - set(BENCHMARK_ENABLE_TESTING OFF) - FetchContent_Declare( - benchmark - GIT_REPOSITORY https://github.com/google/benchmark.git - GIT_TAG v1.9.0 + CPMAddPackage( + NAME benchmark + GITHUB_REPOSITORY google/benchmark + VERSION 1.9.0 + OPTIONS "BENCHMARK_ENABLE_TESTING OFF" ) - FetchContent_MakeAvailable(benchmark) endif() endif() diff --git a/ci-scripts/cls_containerize.py b/ci-scripts/cls_containerize.py index b4acdad5ad..03aed32929 100644 --- a/ci-scripts/cls_containerize.py +++ b/ci-scripts/cls_containerize.py @@ -450,9 +450,7 @@ class Containerize(): cmd.cd(lSourcePath) # if asterix, copy the entitlement and subscription manager configurations if self.host == 'Red Hat': - cmd.run('mkdir -p ./etc-pki-entitlement ./rhsm-conf ./rhsm-ca') - cmd.run('cp /etc/rhsm/rhsm.conf ./rhsm-conf/') - cmd.run('cp /etc/rhsm/ca/redhat-uep.pem ./rhsm-ca/') + cmd.run('mkdir -p ./etc-pki-entitlement') cmd.run('cp /etc/pki/entitlement/*.pem ./etc-pki-entitlement/') baseImage = 'ran-base' diff --git a/ci-scripts/conf_files/neighbour-config.conf b/ci-scripts/conf_files/neighbour-config.conf index 12b023de7a..875129365e 100644 --- a/ci-scripts/conf_files/neighbour-config.conf +++ b/ci-scripts/conf_files/neighbour-config.conf @@ -62,12 +62,12 @@ nr_measurement_configuration = { }; A3 = ({ - cell_id = -1; #Default + physCellId = -1; #Default offset = 10; hysteresis = 0; timeToTrigger = 1 }, { - cell_id = 2; + physCellId = 2; offset = 5; hysteresis = 1; timeToTrigger = 2 diff --git a/ci-scripts/conf_files/nrue.uicc.yaml b/ci-scripts/conf_files/nrue.uicc.yaml new file mode 100644 index 0000000000..518315c7ff --- /dev/null +++ b/ci-scripts/conf_files/nrue.uicc.yaml @@ -0,0 +1,28 @@ +uicc0: + imsi: 208990100001100 + key: fec86ba6eb707ed08905757b1bb44b8f + opc: C42449363BBAD02B66D16BC975D77CC1 + dnn: oai + nssai_sst: 1 + +#/* configuration for channel modelisation */ +#/* To be included in main config file when */ +#/* channel modelisation is used (rfsimulator with chanmod options enabled) */ +channelmod: + max_chan: 10; + modellist: modellist_rfsimu_1 + modellist_rfsimu_1: + - model_name: rfsimu_channel_enB0 + type: AWGN + ploss_dB: 20 + noise_power_dB: -4 + forgetfact: 0 + offset: 0 + ds_tdl: 0 + - model_name: rfsimu_channel_ue0 + type: AWGN + ploss_dB: 20 + nose_power_dB: -2 + forgetfact: 0 + offset: 0 + ds_tdl: 0 diff --git a/ci-scripts/docker/Dockerfile.unittest.ubuntu22 b/ci-scripts/docker/Dockerfile.unittest.ubuntu22 index 4401761c02..627b0d07f7 100644 --- a/ci-scripts/docker/Dockerfile.unittest.ubuntu22 +++ b/ci-scripts/docker/Dockerfile.unittest.ubuntu22 @@ -30,7 +30,8 @@ FROM ran-base:develop AS ran-tests RUN apt-get update && \ DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \ DEBIAN_FRONTEND=noninteractive apt-get install --yes \ - libgtest-dev + libgtest-dev \ + libyaml-cpp-dev RUN rm -Rf /oai-ran WORKDIR /oai-ran diff --git a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml index 06c945a250..edee7895ca 100644 --- a/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml +++ b/ci-scripts/yaml_files/5g_rfsimulator/docker-compose.yaml @@ -118,7 +118,7 @@ services: devices: - /dev/net/tun:/dev/net/tun volumes: - - ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf + - ../../conf_files/nrue.uicc.yaml:/opt/oai-nr-ue/etc/nr-ue.yaml healthcheck: test: /bin/bash -c "pgrep nr-uesoftmodem" interval: 10s diff --git a/cmake_targets/CPM.cmake b/cmake_targets/CPM.cmake new file mode 100644 index 0000000000..20013e1870 --- /dev/null +++ b/cmake_targets/CPM.cmake @@ -0,0 +1,24 @@ +# SPDX-License-Identifier: MIT +# +# SPDX-FileCopyrightText: Copyright (c) 2019-2023 Lars Melchior and contributors + +set(CPM_DOWNLOAD_VERSION 0.40.1) +set(CPM_HASH_SUM "117cbf2711572f113bab262933eb5187b08cfc06dce0714a1ee94f2183ddc3ec") + +if(CPM_SOURCE_CACHE) + set(CPM_DOWNLOAD_LOCATION "${CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +elseif(DEFINED ENV{CPM_SOURCE_CACHE}) + set(CPM_DOWNLOAD_LOCATION "$ENV{CPM_SOURCE_CACHE}/cpm/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +else() + set(CPM_DOWNLOAD_LOCATION "${CMAKE_BINARY_DIR}/cmake/CPM_${CPM_DOWNLOAD_VERSION}.cmake") +endif() + +# Expand relative path. This is important if the provided path contains a tilde (~) +get_filename_component(CPM_DOWNLOAD_LOCATION ${CPM_DOWNLOAD_LOCATION} ABSOLUTE) + +file(DOWNLOAD + https://github.com/cpm-cmake/CPM.cmake/releases/download/v${CPM_DOWNLOAD_VERSION}/CPM.cmake + ${CPM_DOWNLOAD_LOCATION} EXPECTED_HASH SHA256=${CPM_HASH_SUM} +) + +include(${CPM_DOWNLOAD_LOCATION}) diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index cd7e326be8..5568c159d1 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -46,7 +46,7 @@ BUILD_DOXYGEN=0 DISABLE_HARDWARE_DEPENDENCY="False" CMAKE_BUILD_TYPE="RelWithDebInfo" CMAKE_CMD="$CMAKE" -OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 websrv oai_iqplayer" +OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_cuda ldpc_t2 websrv oai_iqplayer imscope" TARGET_LIST="" function print_help() { @@ -497,7 +497,7 @@ function main() { # add some default libraries that should always be built # for eNB, gNB, UEs, simulators if [[ $gNB == 1 || $eNB == 1 || $UE == 1 || $nrUE == 1 || $SIMUS_PHY == 1 || $RU == 1 ]]; then - TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts" + TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml" fi mkdir -p $DIR/$BUILD_DIR/build diff --git a/cmake_targets/tools/build_helper b/cmake_targets/tools/build_helper index 30cb1f0b00..bcd151b086 100755 --- a/cmake_targets/tools/build_helper +++ b/cmake_targets/tools/build_helper @@ -128,6 +128,7 @@ check_supported_distribution() { "rocky9.1") return 0 ;; "rocky9.2") return 0 ;; "rocky9.3") return 0 ;; + "rocky9.4") return 0 ;; esac return 1 } @@ -589,8 +590,9 @@ check_install_oai_software() { libtool \ patch \ openssl \ - zlib1g-dev \ - xxd + zlib1g-dev \ + xxd \ + libyaml-cpp-dev elif [[ "$OS_BASEDISTRO" == "fedora" ]]; then if [[ "$OS_DISTRO" == "rhel" ]] || [[ "$OS_DISTRO" == "centos" ]] || [[ "$OS_DISTRO" == "rocky" ]]; then @@ -625,7 +627,8 @@ check_install_oai_software() { blas \ blas-devel \ vim-common \ - zlib-devel + zlib-devel \ + yaml-cpp-devel fi install_asn1c_from_source $1 diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt index 6c2d123397..14c4ac582a 100644 --- a/common/CMakeLists.txt +++ b/common/CMakeLists.txt @@ -1,3 +1,4 @@ add_subdirectory(utils) +add_subdirectory(config/yaml) configure_file(oai_version.h.in oai_version.h @ONLY) diff --git a/common/config/config_load_configmodule.c b/common/config/config_load_configmodule.c index 411e737976..71cab2a33a 100644 --- a/common/config/config_load_configmodule.c +++ b/common/config/config_load_configmodule.c @@ -293,8 +293,12 @@ configmodule_interface_t *load_configmodule(int argc, } else if ( i == 1 ) { /* -O argument doesn't contain ":" separator, assume -O option, default cfgmode to libconfig with one parameter, the path to the configuration file cfgmode must not be NULL */ - modeparams=cfgmode; - cfgmode=strdup(CONFIG_LIBCONFIGFILE); + modeparams = cfgmode; + if (strstr(modeparams, ".yaml") != NULL || strstr(modeparams, ".yml") != NULL) { + cfgmode = strdup("yaml"); + } else { + cfgmode = strdup(CONFIG_LIBCONFIGFILE); + } } static configmodule_interface_t *cfgptr; if (cfgptr) diff --git a/common/config/yaml/CMakeLists.txt b/common/config/yaml/CMakeLists.txt new file mode 100644 index 0000000000..5ff8c23eb1 --- /dev/null +++ b/common/config/yaml/CMakeLists.txt @@ -0,0 +1,25 @@ +find_package(yaml-cpp REQUIRED PATHS /usr/lib/aarch64-linux-gnu/pkgconfig/) +# include(FetchContent) + +# FetchContent_Declare( +# yaml-cpp +# GIT_REPOSITORY https://github.com/jbeder/yaml-cpp.git +# GIT_TAG 0.8.0 +# ) +# FetchContent_GetProperties(yaml-cpp) + +# if(NOT yaml-cpp_POPULATED) +# message(STATUS "Fetching yaml-cpp...") +# FetchContent_Populate(yaml-cpp) +# add_subdirectory(${yaml-cpp_SOURCE_DIR} ${yaml-cpp_BINARY_DIR}) +# endif() + +add_library(params_yaml_static config_yaml.cpp) +target_link_libraries(params_yaml_static PUBLIC UTIL yaml-cpp) +if (ENABLE_TESTS) + add_subdirectory(tests) +endif() + +add_library(params_yaml MODULE config_yaml.cpp) +target_link_libraries(params_yaml PUBLIC UTIL yaml-cpp) +set_target_properties(params_yaml PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/common/config/yaml/config_yaml.cpp b/common/config/yaml/config_yaml.cpp new file mode 100644 index 0000000000..c2af51e2db --- /dev/null +++ b/common/config/yaml/config_yaml.cpp @@ -0,0 +1,327 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "yaml-cpp/yaml.h" +extern "C" { +#include "common/config/config_load_configmodule.h" +#include "common/config/config_userapi.h" +void *config_allocate_new(configmodule_interface_t *cfg, int sz, bool autoFree); +void config_check_valptr(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int elt_sz, int nb_elt); +} +#include +#include +#include +#include +#include + +namespace config_yaml { +class YamlConfig { + public: + YAML::Node config; + YamlConfig(std::string filename) + { + config = YAML::LoadFile(filename); + } +}; + +void SetDefault(configmodule_interface_t *cfg, paramdef_t *param) +{ + switch (param->type) { + case TYPE_INT: + *param->iptr = param->defintval; + break; + case TYPE_UINT: + *param->uptr = param->defuintval; + break; + case TYPE_STRING: + if (param->defstrval != nullptr) { + if (param->numelt == 0) { + config_check_valptr(cfg, param, 1, strlen(param->defstrval) + 1); + } + sprintf(*param->strptr, "%s", param->defstrval); + } + break; + case TYPE_INT8: + *param->i8ptr = param->defintval; + break; + case TYPE_UINT8: + *param->i8ptr = param->defuintval; + break; + case TYPE_INT16: + *param->i16ptr = param->defintval; + break; + case TYPE_UINT16: + *param->u16ptr = param->defuintval; + break; + case TYPE_INT64: + *param->i64ptr = param->defint64val; + break; + case TYPE_UINT64: + *param->u64ptr = param->defint64val; + break; + case TYPE_DOUBLE: + *param->dblptr = param->defdblval; + break; + case TYPE_MASK: + *param->uptr = param->defuintval; + break; + case TYPE_STRINGLIST: + if (param->defstrlistval != nullptr) { + param->strlistptr = param->defstrlistval; + } + break; + case TYPE_INTARRAY: + if (param->defintarrayval) { + param->iptr = param->defintarrayval; + } + break; + case TYPE_UINTARRAY: + if (param->defintarrayval) { + param->uptr = (uint32_t *)param->defintarrayval; + } + break; + default: + AssertFatal(false, "Unhandled type %d", param->type); + } + param->paramflags |= PARAMFLAG_PARAMSETDEF; +} + +void SetNonDefault(configmodule_interface_t *cfg, const YAML::Node &node, paramdef_t *param) +{ + auto optname = std::string(param->optname); + switch (param->type) { + case TYPE_INT: + *param->iptr = node[optname].as(); + break; + case TYPE_UINT: + *param->uptr = node[optname].as(); + break; + case TYPE_STRING: { + std::string setting = node[optname].as(); + config_check_valptr(cfg, param, 1, setting.length() + 1); + sprintf(*param->strptr, "%s", setting.c_str()); + break; + } + case TYPE_UINT8: + *param->i8ptr = node[optname].as(); + break; + case TYPE_INT16: + *param->i16ptr = node[optname].as(); + break; + case TYPE_UINT16: + *param->u16ptr = node[optname].as(); + break; + case TYPE_INT64: + *param->i64ptr = node[optname].as(); + break; + case TYPE_UINT64: + *param->u64ptr = node[optname].as(); + break; + case TYPE_DOUBLE: + *param->dblptr = node[optname].as(); + break; + case TYPE_MASK: + *param->uptr = node[optname].as(); + break; + case TYPE_STRINGLIST: { + if (node[optname].IsSequence()) { + config_check_valptr(cfg, param, sizeof(char *), node[optname].size()); + int i = 0; + for (const auto &elem : node[optname]) { + sprintf(param->strlistptr[i], "%s", elem.as().c_str()); + i++; + } + param->numelt = i; + } else { + param->numelt = 0; + } + break; + } + case TYPE_INTARRAY: { + if (node[optname].IsSequence() && node[optname].size() > 0) { + param->numelt = node[optname].size(); + param->iptr = + (int32_t *)config_allocate_new(cfg, param->numelt * sizeof(*param->iptr), !(param->paramflags & PARAMFLAG_NOFREE)); + + for (int i = 0; i < param->numelt; i++) { + param->iptr[i] = node[optname][i].as(); + } + } else { + param->numelt = 0; + } + break; + } + case TYPE_UINTARRAY: { + if (node[optname].IsSequence() && node[optname].size() > 0) { + param->numelt = node[optname].size(); + param->uptr = + (uint32_t *)config_allocate_new(cfg, param->numelt * sizeof(*param->uptr), !(param->paramflags & PARAMFLAG_NOFREE)); + + for (int i = 0; i < param->numelt; i++) { + param->uptr[i] = node[optname][i].as(); + } + } else { + param->numelt = 0; + } + break; + } + default: + AssertFatal(false, "Unhandled type %d", param->type); + } + param->paramflags |= PARAMFLAG_PARAMSET; +} + +void GetParams(configmodule_interface_t *cfg, const YAML::Node &node, paramdef_t *params, int num_params) +{ + for (auto i = 0; i < num_params; i++) { + if (node && node[std::string(params[i].optname)]) { + SetNonDefault(cfg, node, ¶ms[i]); + } else { + SetDefault(cfg, ¶ms[i]); + } + } +} + +static YamlConfig *config; +static YAML::Node invalid_node; +} // namespace config_yaml + +extern "C" int config_yaml_init(configmodule_interface_t *cfg) +{ + char **cfgP = cfg->cfgP; + cfg->numptrs = 0; + pthread_mutex_init(&cfg->memBlocks_mutex, NULL); + memset(cfg->oneBlock, 0, sizeof(cfg->oneBlock)); + + config_yaml::config = new config_yaml::YamlConfig(std::string(cfgP[0])); + return 0; +} + +extern "C" void config_yaml_end(configmodule_interface_t *cfg) +{ + delete config_yaml::config; +} + +YAML::Node find_node(const std::string &prefix, YAML::Node node) +{ + // Iterate through each key in the prefix + std::stringstream ss(prefix); + std::string key; + YAML::Node current = node; + + while (std::getline(ss, key, '.')) { + if (key.at(0) == '[' && key.back() == ']') { + // The key is an index to a sequence + if (!current.IsSequence()) { + throw std::invalid_argument("Incorrect yaml file structure"); + } + int index = std::stoi(key.substr(1, key.size() - 2)); + current = current[index]; + continue; + } + if (!current.IsMap()) { + throw std::invalid_argument("Incorrect yaml file structure"); + } + if (!current[key]) { + return config_yaml::invalid_node; + } + current = current[key]; + } + return current; +} + +extern "C" int config_yaml_get(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix) +{ + std::string p; + if (prefix != nullptr) { + p = std::string(prefix); + } + auto node = find_node(p, YAML::Clone(config_yaml::config->config)); + if (node == config_yaml::invalid_node) { + return -1; + } + for (auto i = 0; i < numoptions; i++) { + if (cfgoptions[i].type != TYPE_STRING && cfgoptions[i].type != TYPE_INTARRAY && cfgoptions[i].type != TYPE_UINTARRAY + && cfgoptions[i].voidptr == nullptr) { + config_check_valptr(cfg, &cfgoptions[i], sizeof(void *), 1); + } + } + config_yaml::GetParams(cfg, node, cfgoptions, numoptions); + return 0; +} + +extern "C" int config_yaml_getlist(configmodule_interface_t *cfg, + paramlist_def_t *ParamList, + paramdef_t *params, + int numparams, + char *prefix) +{ + char path[512]; + if (prefix != nullptr) { + sprintf(path, "%s.%s", prefix, ParamList->listname); + } else { + sprintf(path, "%s", ParamList->listname); + } + ParamList->numelt = 0; + auto node = find_node(path, YAML::Clone(config_yaml::config->config)); + if (node == config_yaml::invalid_node) { + return -1; + } + + if (!node.IsSequence()) { + return -1; + } + ParamList->numelt = node.size(); + + if (ParamList->numelt > 0 && params != NULL) { + ParamList->paramarray = static_cast(config_allocate_new(cfg, ParamList->numelt * sizeof(paramdef_t *), true)); + + for (int i = 0; i < ParamList->numelt; i++) { + ParamList->paramarray[i] = static_cast(config_allocate_new(cfg, numparams * sizeof(paramdef_t), true)); + memcpy(ParamList->paramarray[i], params, sizeof(paramdef_t) * numparams); + + for (int j = 0; j < numparams; j++) { + ParamList->paramarray[i][j].strptr = NULL; + if (ParamList->paramarray[i][j].type != TYPE_STRING) { + config_check_valptr(cfg, &ParamList->paramarray[i][j], sizeof(void *), 1); + } + } + + config_yaml::GetParams(cfg, node[i], ParamList->paramarray[i], numparams); + } + } + + return ParamList->numelt; +} + +extern "C" void config_yaml_write_parsedcfg(configmodule_interface_t *cfg) +{ + (void)cfg; +} + +extern "C" int config_yaml_set(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix) +{ + (void)cfg; + (void)cfgoptions; + (void)numoptions; + (void)prefix; + return 0; +} diff --git a/common/config/yaml/tests/CMakeLists.txt b/common/config/yaml/tests/CMakeLists.txt new file mode 100644 index 0000000000..b28bb8d47f --- /dev/null +++ b/common/config/yaml/tests/CMakeLists.txt @@ -0,0 +1,11 @@ +add_executable(test_yaml_config test_yaml_config.cpp) +target_link_libraries(test_yaml_config PRIVATE params_yaml_static GTest::gtest) +add_dependencies(tests test_yaml_config) +add_test(NAME test_yaml_config + COMMAND ./test_yaml_config) +configure_file(test1.yaml test1.yaml COPYONLY) +configure_file(test_recursion.yaml test_recursion.yaml COPYONLY) +configure_file(test_list.yaml test_list.yaml COPYONLY) +configure_file(test_string.yaml test_string.yaml COPYONLY) +configure_file(test_list_of_mappings.yml test_list_of_mappings.yml COPYONLY) +configure_file(test_int_array.yaml test_int_array.yaml COPYONLY) diff --git a/common/config/yaml/tests/test1.yaml b/common/config/yaml/tests/test1.yaml new file mode 100644 index 0000000000..7b064b0d38 --- /dev/null +++ b/common/config/yaml/tests/test1.yaml @@ -0,0 +1,4 @@ +test: + value1: 1 + value2: 2 + value3: 3 diff --git a/common/config/yaml/tests/test_int_array.yaml b/common/config/yaml/tests/test_int_array.yaml new file mode 100644 index 0000000000..498db5d31c --- /dev/null +++ b/common/config/yaml/tests/test_int_array.yaml @@ -0,0 +1,6 @@ +test: + array: [1, 2, 3, 4] + array2: + - 1 + - 2 + - 3 diff --git a/common/config/yaml/tests/test_list.yaml b/common/config/yaml/tests/test_list.yaml new file mode 100644 index 0000000000..a7d4b38a59 --- /dev/null +++ b/common/config/yaml/tests/test_list.yaml @@ -0,0 +1,13 @@ +test: + - + value1: 1 + value2: 2 + value3: 3 + - + value1: 4 + value2: 5 + value3: 6 + - + value1: 7 + value2: 8 + value3: 9 diff --git a/common/config/yaml/tests/test_list_of_mappings.yml b/common/config/yaml/tests/test_list_of_mappings.yml new file mode 100644 index 0000000000..e8bc8c9f00 --- /dev/null +++ b/common/config/yaml/tests/test_list_of_mappings.yml @@ -0,0 +1,14 @@ +test: + list: + - value1: 1 + value2: 3 + list: + - value1 + - value2 + - value3 + - value: 2 + value2: 6 + list: + - value4 + - value5 + - value6 diff --git a/common/config/yaml/tests/test_recursion.yaml b/common/config/yaml/tests/test_recursion.yaml new file mode 100644 index 0000000000..0cf2c723ce --- /dev/null +++ b/common/config/yaml/tests/test_recursion.yaml @@ -0,0 +1,8 @@ +test: + test1: + test2: + test3: + test4: + value1: 1 + value2: 2 + value3: 3 diff --git a/common/config/yaml/tests/test_string.yaml b/common/config/yaml/tests/test_string.yaml new file mode 100644 index 0000000000..e093ea3880 --- /dev/null +++ b/common/config/yaml/tests/test_string.yaml @@ -0,0 +1,6 @@ +test: + stringvalue: testvalue + stringlist: + - testvalue1 + - testvalue2 + - testvalue3 diff --git a/common/config/yaml/tests/test_yaml_config.cpp b/common/config/yaml/tests/test_yaml_config.cpp new file mode 100644 index 0000000000..921a455dfd --- /dev/null +++ b/common/config/yaml/tests/test_yaml_config.cpp @@ -0,0 +1,373 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "gtest/gtest.h" +extern "C" { +#include "common/config/config_paramdesc.h" +// TODO: Should use minimal_lib for this but for some reason it doesn't link properly +uint64_t get_softmodem_optmask(void) +{ + return 0; +} +configmodule_interface_t *uniqCfg; +void exit_function(const char *file, const char *function, const int line, const char *s, const int assert) +{ + if (assert) { + abort(); + } else { + exit(EXIT_SUCCESS); + } +} + +#include "common/utils/LOG/log.h" +#include "common/config/config_load_configmodule.h" +#include "common/config/config_userapi.h" +} +#include +#include +#include +#include +#include + +extern "C" { +int config_yaml_init(configmodule_interface_t *cfg); +void config_yaml_end(configmodule_interface_t *cfg); +int config_yaml_get(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix); +int config_yaml_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, paramdef_t *params, int numparams, char *prefix); +void config_yaml_write_parsedcfg(configmodule_interface_t *cfg); +int config_yaml_set(configmodule_interface_t *cfg, paramdef_t *cfgoptions, int numoptions, char *prefix); +} + +TEST(yaml_config, yaml_basic) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test1.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + + +TEST(yaml_config, yaml_get_existing_values) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test1.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + // Testing paremters present in the test node + paramdef_t p = {0}; + for (auto i = 1; i <= 3; i++) { + sprintf(p.optname, "%s%d", "value", i); + uint16_t value; + p.type = TYPE_UINT16; + p.u16ptr = &value; + char prefix[] = "test"; + config_yaml_get(cfg, &p, 1, prefix); + EXPECT_EQ(value, i); + } + + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, yaml_get_non_existing_values) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test1.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + // Testing paremters present in the test node + paramdef_t p = {0}; + for (auto i = 4; i <= 5; i++) { + sprintf(p.optname, "%s%d", "value", i); + uint16_t value; + p.type = TYPE_UINT16; + p.u16ptr = &value; + p.defuintval = i; + char prefix[] = "test"; + config_yaml_get(cfg, &p, 1, prefix); + EXPECT_EQ(value, i); + } + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_high_recusion) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_recursion.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + // Testing paremters present in the test node + paramdef_t p = {0}; + for (auto i = 1; i <= 3; i++) { + sprintf(p.optname, "%s%d", "value", i); + uint16_t value; + p.type = TYPE_UINT16; + p.u16ptr = &value; + char prefix[] = "test.test1.test2.test3.test4"; + EXPECT_EQ(config_yaml_get(cfg, &p, 1, prefix), 0); + EXPECT_EQ(value, i); + } + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_list) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_list.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramlist_def_t param_list = {0}; + sprintf(param_list.listname, "%s", "test"); + paramdef_t params[3] = {0}; + uint16_t value[3]; + for (auto i = 0; i < 3; i++) { + sprintf(params[i].optname, "%s%d", "value", i+1); + params[i].type = TYPE_UINT16; + params[i].u16ptr = &value[i]; + } + + + config_yaml_getlist(cfg, ¶m_list, params, 3, nullptr); + + for (auto i = 0; i < 3; i++) { + for (auto j = 0; j < 3; j++) { + EXPECT_EQ(*param_list.paramarray[i][j].u16ptr, i * 3 + j + 1); + } + } + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_string_auto_alloc) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_string.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRING; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringvalue"); + param.defstrval = nullptr; + + // Test automatic allocation of strings + char prefix[] = "test"; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_NE(param.strptr, nullptr); + EXPECT_EQ(strcmp(*param.strptr, "testvalue"), 0); + EXPECT_EQ(cfg->numptrs, 2); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_string_no_realloc) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_string.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRING; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringvalue"); + param.defstrval = nullptr; + + // check that if we at least have a non-null pointer to pointer, only one buffer is allocated + char prefix[] = "test"; + char* non_null_pointer_to_pointer = nullptr; + param.strptr = &non_null_pointer_to_pointer; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_EQ(param.strptr, &non_null_pointer_to_pointer); + EXPECT_EQ(strcmp(*param.strptr, "testvalue"), 0); + EXPECT_EQ(cfg->numptrs, 1); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_string_pointer_available) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_string.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRING; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringvalue"); + param.defstrval = nullptr; + + // Test that if a strptr is provided, config module doesnt reallocate it + char prefix[] = "test"; + char *container = (char *)malloc(sizeof(char) * 30); + param.strptr = &container; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_EQ(param.strptr, &container); + EXPECT_EQ(strcmp(*param.strptr, "testvalue"), 0); + EXPECT_EQ(cfg->numptrs, 0); + free(container); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_string_default_value) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_string.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRING; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringvalue"); + param.defstrval = nullptr; + + // Test that if defstrval is not null and value is missing in yaml defstrval is set + char prefix[] = "test"; + char default_value[] = "default"; + param.defstrval = default_value; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringvalue_missing"); + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_NE(param.strptr, nullptr); + EXPECT_EQ(strcmp(*param.strptr, "default"), 0); + EXPECT_EQ(cfg->numptrs, 2) << " 2 pointers required, 1 for the string, one for the pointer-to string"; + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_stringlist) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_string.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRINGLIST; + param.strptr = nullptr; + sprintf(param.optname, "%s", "stringlist"); + param.defstrval = nullptr; + + // Test automatic allocation of string lists + char prefix[] = "test"; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_NE(param.strptr, nullptr); + for (auto i = 0; i < param.numelt; i++) { + std::cout << (param.strlistptr)[i] << std::endl; + } + // config_check_valptr allocates maximum list size regardless of the number of elements in list + EXPECT_EQ(cfg->numptrs, MAX_LIST_SIZE + 1); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_list_of_mappings) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_list_of_mappings.yml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_STRINGLIST; + param.strlistptr = nullptr; + sprintf(param.optname, "%s", "list"); + param.defstrval = nullptr; + + // Test automatic allocation of string lists + char prefix1[] = "test.list.[0]"; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix1), 0); + EXPECT_NE(param.strptr, nullptr); + EXPECT_STREQ("value1", param.strlistptr[0]); + EXPECT_STREQ("value2", param.strlistptr[1]); + EXPECT_STREQ("value3", param.strlistptr[2]); + + char prefix2[] = "test.list.[1]"; + param.strlistptr = nullptr; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix2), 0); + EXPECT_NE(param.strptr, nullptr); + EXPECT_STREQ("value4", param.strlistptr[0]); + EXPECT_STREQ("value5", param.strlistptr[1]); + EXPECT_STREQ("value6", param.strlistptr[2]); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +TEST(yaml_config, test_int_array) { + configmodule_interface_t *cfg = static_cast(calloc(1, sizeof(*cfg))); + cfg->cfgP[0] = strdup("test_int_array.yaml"); + EXPECT_EQ(config_yaml_init(cfg), 0); + + paramdef_t param = {0}; + param.type = TYPE_INTARRAY; + param.iptr = nullptr; + sprintf(param.optname, "%s", "array"); + param.defintarrayval = nullptr; + + // Test automatic allocation of int arrays + char prefix[] = "test"; + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_NE(param.iptr, nullptr); + ASSERT_EQ(param.numelt, 4); + EXPECT_EQ(1, param.iptr[0]); + EXPECT_EQ(2, param.iptr[1]); + EXPECT_EQ(3, param.iptr[2]); + EXPECT_EQ(4, param.iptr[3]); + + param.uptr = nullptr; + param.numelt = 0; + sprintf(param.optname, "%s", "array2"); + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_NE(param.uptr, nullptr); + ASSERT_EQ(param.numelt, 3); + EXPECT_EQ(1U, param.uptr[0]); + EXPECT_EQ(2U, param.uptr[1]); + EXPECT_EQ(3U, param.uptr[2]); + + param.uptr = nullptr; + param.numelt = 0; + sprintf(param.optname, "%s", "non-existent-array"); + EXPECT_EQ(config_yaml_get(cfg, ¶m, 1, prefix), 0); + EXPECT_EQ(param.uptr, nullptr); + ASSERT_EQ(param.numelt, 0); + + config_yaml_end(cfg); + free(cfg->cfgP[0]); + end_configmodule(cfg); +} + +int main(int argc, char** argv) +{ + logInit(); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} + diff --git a/common/utils/LOG/DOC/rtusage.md b/common/utils/LOG/DOC/rtusage.md index 04ff9647a8..b3de6cd71e 100644 --- a/common/utils/LOG/DOC/rtusage.md +++ b/common/utils/LOG/DOC/rtusage.md @@ -1,5 +1,5 @@ ## configuring the logging facility -The logging facility is fully configurable and it uses the [config module](../../../config/config.md) to get its parameters at init time. The [telnet server](../../telnetsrv/DOC/telnetsrv.md) includes a set of commands which can be used to dynamically modify the logging facility behavior +The logging facility is fully configurable and it uses the [config module](../../../../common/config/DOC/config.md) to get its parameters at init time. The [telnet server](../../telnetsrv/DOC/telnetsrv.md) includes a set of commands which can be used to dynamically modify the logging facility behavior All logging facility parameters are defined in the log_config section. Some parameters are global to the logging facility, they modify the way messages are printed to stdout. Conversely, some parameters are specific to a component and only modify the behavior for messages issued by a given component. A third type of parameters can be used to activate conditional debug code or dump messages or buffers. diff --git a/common/utils/T/DOC/T/wireshark.md b/common/utils/T/DOC/T/wireshark.md index 67587d139f..af11535bdc 100644 --- a/common/utils/T/DOC/T/wireshark.md +++ b/common/utils/T/DOC/T/wireshark.md @@ -103,7 +103,7 @@ of wireshark. Adapt as necessary. 2) Then, in the menu, choose `Edit->Preferences`. 3) In the preference window, unroll `Protocols` and configure the following: -**MAC** +**MAC-NR** Go to `MAC-NR`. Select: @@ -117,7 +117,7 @@ For `Source of LCID -> drb channel settings`: Type `AM, SN Len=18`, same thing for DL RLC Bearer Type. * Click OK. -**RLC** +**RLC-NR** Now, go to `RLC-NR`. Select: @@ -129,7 +129,7 @@ Now, go to `RLC-NR`. Select: You don't need to select `May see RLC headers only` and `Try to reassemble UM frames`. -**PDCP** +**PDCP-NR** Now, go to `PDCP-NR`. Select what you need in there. @@ -146,6 +146,12 @@ You are done with the preferences. You can click OK. 4) Then, in the menu `Analyze`, choose `Enabled Protocols...`. 5) In the new window search for `nr` and select `mac_nr_udp` to have `MAC-NR over UDP`. +**NAS-5GS** + +Under `NAS-5GS`, select: + +* `Try to detect and decode 5G-EA0 ciphered messages` + And that's it. Maybe other settings can be changed, but those steps should be enough for a start. diff --git a/common/utils/nr/nr_common.c b/common/utils/nr/nr_common.c index c1ed6c7546..f851c26eee 100644 --- a/common/utils/nr/nr_common.c +++ b/common/utils/nr/nr_common.c @@ -1161,9 +1161,9 @@ void nr_timer_stop(NR_timer_t *timer) timer->counter = 0; } -bool is_nr_timer_active(NR_timer_t timer) +bool nr_timer_is_active(const NR_timer_t *timer) { - return timer.active; + return timer->active; } bool nr_timer_tick(NR_timer_t *timer) @@ -1173,23 +1173,23 @@ bool nr_timer_tick(NR_timer_t *timer) timer->counter += timer->step; if (timer->target == UINT_MAX) // infinite target, never expires return false; - expired = nr_timer_expired(*timer); + expired = nr_timer_expired(timer); if (expired) timer->active = false; } return expired; } -bool nr_timer_expired(NR_timer_t timer) +bool nr_timer_expired(const NR_timer_t *timer) { - if (timer.target == UINT_MAX) // infinite target, never expires + if (timer->target == UINT_MAX) // infinite target, never expires return false; - return (timer.counter >= timer.target); + return timer->counter >= timer->target; } -uint32_t nr_timer_elapsed_time(NR_timer_t timer) +uint32_t nr_timer_elapsed_time(const NR_timer_t *timer) { - return timer.counter; + return timer->counter; } void nr_timer_setup(NR_timer_t *timer, const uint32_t target, const uint32_t step) diff --git a/common/utils/nr/nr_common.h b/common/utils/nr/nr_common.h index fcefa29461..3c04bca581 100644 --- a/common/utils/nr/nr_common.h +++ b/common/utils/nr/nr_common.h @@ -163,19 +163,19 @@ void nr_timer_setup(NR_timer_t *timer, const uint32_t target, const uint32_t ste * @param timer Timer to be checked * @return Indication if the timer is expired or not */ -bool nr_timer_expired(NR_timer_t timer); +bool nr_timer_expired(const NR_timer_t *timer); /** * @brief To check if a timer is active * @param timer Timer to be checked * @return Indication if the timer is active or not */ -bool is_nr_timer_active(NR_timer_t timer); +bool nr_timer_is_active(const NR_timer_t *timer); /** * @brief To return how much time has passed since start of timer * @param timer Timer to be checked * @return Time passed since start of timer */ -uint32_t nr_timer_elapsed_time(NR_timer_t timer); +uint32_t nr_timer_elapsed_time(const NR_timer_t *timer); extern const nr_bandentry_t nr_bandtable[]; @@ -254,6 +254,9 @@ int get_smallest_supported_bandwidth_index(int scs, frequency_range_t frequency_ #define CEILIDIV(a,b) ((a+b-1)/b) #define ROUNDIDIV(a,b) (((a<<1)+b)/(b<<1)) +// Align up to a multiple of 16 +#define ALIGN_UP_16(a) ((a + 15) & ~15) + #ifdef __cplusplus #ifdef min #undef min diff --git a/common/utils/nr/tests/test_nr_common.cpp b/common/utils/nr/tests/test_nr_common.cpp index fede91b8ee..c780736a51 100644 --- a/common/utils/nr/tests/test_nr_common.cpp +++ b/common/utils/nr/tests/test_nr_common.cpp @@ -29,13 +29,13 @@ TEST(nr_common, nr_timer) { NR_timer_t timer; nr_timer_setup(&timer, 10, 1); nr_timer_start(&timer); - EXPECT_TRUE(is_nr_timer_active(timer)); - EXPECT_FALSE(nr_timer_expired(timer)); + EXPECT_TRUE(nr_timer_is_active(&timer)); + EXPECT_FALSE(nr_timer_expired(&timer)); for (auto i = 0; i < 10; i++) { nr_timer_tick(&timer); } - EXPECT_FALSE(is_nr_timer_active(timer)); - EXPECT_TRUE(nr_timer_expired(timer)); + EXPECT_FALSE(nr_timer_is_active(&timer)); + EXPECT_TRUE(nr_timer_expired(&timer)); } diff --git a/common/utils/ocp_itti/intertask_interface.cpp b/common/utils/ocp_itti/intertask_interface.cpp index 9d22e06d7d..b59de2e0fc 100644 --- a/common/utils/ocp_itti/intertask_interface.cpp +++ b/common/utils/ocp_itti/intertask_interface.cpp @@ -320,7 +320,7 @@ typedef struct timer_elm_s { (char *)itti_get_task_name(task_id), -1, OAI_PRIORITY_RT); - LOG_I(ITTI,"Created Posix thread %s\n", itti_get_task_name(task_id) ); + LOG_D(ITTI,"Created Posix thread %s\n", itti_get_task_name(task_id) ); return 0; } diff --git a/doc/BUILD.md b/doc/BUILD.md index 3e06e79d01..dc5b1361f2 100644 --- a/doc/BUILD.md +++ b/doc/BUILD.md @@ -42,6 +42,8 @@ Running the [build_oai](../cmake_targets/build_oai) script also generates some The build system for OAI uses [cmake](https://cmake.org/) which is a tool to generate makefiles. The `build_oai` script is a wrapper using `cmake` and `make`/`ninja` to ease the oai build and use. It logs the `cmake` and `ninja`/`make` commands it executes. The file describing how to build the executables from source files is the [CMakeLists.txt](../CMakeLists.txt), it is used as input by cmake to generate the makefiles. +cmake is further extended by using [CPM](https://github.com/cpm-cmake/CPM.cmake). CPM is a cmake script that handles external code dependencies. It is setup to cache downloaded code in `~/.cache/cpm`. While most external dependencies should be handled by system package managers, CPM has the advantage of using any code that is available in a public git repository. + The oai softmodem supports many use cases, and new ones are regularly added. Most of them are accessible using the configuration file or the command line options and continuous effort is done to avoid introducing build options as it makes tests and usage more complicated than run-time options. The following functionalities, originally requiring a specific build are now accessible by configuration or command line options: - s1, noS1 diff --git a/doc/Doxyfile b/doc/Doxyfile index 5f377964cf..0d53026705 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -2256,7 +2256,6 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/pucch_nr.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/sss_nr.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/dci_nr.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_llr_computation.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/nr_prach.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair1/PHY/NR_UE_TRANSPORT/nr_ue_rf_helpers.c \ diff --git a/doc/F1-design.md b/doc/F1-design.md index 22781650f1..4690639d5b 100644 --- a/doc/F1-design.md +++ b/doc/F1-design.md @@ -18,38 +18,58 @@ The F1 interface is the functional split of 3GPP between the CU (centralized unit: PDCP, RRC, SDAP) and the DU (distributed unit: RLC, MAC, PHY). It is -standardized in TS 38.470 - 38.473 for 5G NR. No equivalent for 4G exists. +standardized in TS 38.470 - 38.473 for 5G NR. -We assume that each DU handles only one cell. Multiple DUs connected to one CU -are supported. Mobility over F1 is not yet supported. +F1 specs: +* 3GPP TS 38.470 F1 general aspects and principles +* 3GPP TS 38.471 F1 layer 1 -# Control plane status (F1-C) +F1-C: +* 3GPP TS 38.472 F1 signalling transport +* 3GPP TS 38.473 F1 Application Protocol (F1AP) -## Implementation Status +F1-U: +* 3GPP TS 38.474 F1 Data Transport -Note that OAI uses F1 "internally". That means, that even **if you run a -monolithic gNB, the internal information exchange uses F1**. You can therefore -expect that everything working in a monolithic deployment should also work in -F1. The current implementation is based on R16.3. +No equivalent for 4G exists. -Note that NSA does NOT follow this logic yet, and does not work with F1. +## Control plane (F1-C) -The following messages are currently implemented: +The interface F1-C is designed for the exchange of signalling messages between the +Radio Network Layer (RNL) and the Transport Network Layer (TNL). It +consists of F1 Application Protocol messages (F1-AP) exchanged over SCTP. -- F1 Setup Request and F1 Setup Response/Failure -- Initial UL RRC, UL/DL RRC Message Transfer -- F1 UE Context management messages for the "good case" - * Setup Request/Response - * Modification Request/Response - * Release Request/Command/Complete +## Data plane (F1-U) -All other messages are not implemented or not working reliably: +F1-U uses GTP-U for information exchange. -- F1 CU/DU configuration updade -- Paging messages -- Warning Message transmissions +# OAI Implementation Status -## High-level F1-C code structure +The implementation of F1AP messages is seamlessly integrated into OAI, supporting both Monolithic SA +and CU/DU functional split modes. The F1 code is therefore always compiled with nr-softmodem. +This architecture ensures that even *when operating a monolithic gNB, internal information exchange +always utilizes F1AP messages*. +The major difference lies in the CU/DU split scenario, where ASN.1-encoded F1AP messages (F1-C) are +exchanges over SCTP, via a socket interface. + +This is the current status: + +- gNB-CU/gNB-DU split +- Supported deployments: + * SA + * Single cell per DU + * Multiple DUs connected to one CU (both CP and UP) +- Not supported: + * Mobility + * NSA + +## F1-C + +### F1AP messages + +Refer to [FEATURE_SET.md](FEATURE_SET.md#gNB-F1AP) to learn about the current F1AP implementation status. + +### High-level F1-C code structure The F1 interface is used internally between CU (mostly RRC) and DU (mostly MAC) to exchange information. In DL, the CU sends messages as defined by the @@ -69,38 +89,94 @@ dedicated handler). In F1, the DU task receives the ITTI message, encodes using ASN.1, and sends it over a network socket. The CU task decodes, and sends the same ITTI message to the RRC task as done directly in the monolithic case. +Summary of callbacks and handlers: + +| Entity | Callback definition | Callback F1 Implementation | Callback Monolithic SA Implementation | Handler | +|--------|---------------------|----------------------------|---------------------------------------|----------------------| +| CU/RRC | `mac_rrc_dl.h` | `mac_rrc_dl_f1ap.c` | `mac_rrc_dl_direct.c` |No handler (use ITTI) | +| DU/MAC | `mac_rrc_ul.h` | `mac_rrc_ul_f1ap.c` | `mac_rrc_ul_direct.c` |`mac_rrc_dl_handler.c`| + +A sequence diagram for downlink F1AP messages over the OAI CU/DU functional split: + +```mermaid +sequenceDiagram + box rgba(17,158,189,255) CU/RRC + participant TASK_RRC_GNB + participant TASK_CU_F1 + end + Note over TASK_RRC_GNB: MAC/RRC callback + TASK_RRC_GNB->>+TASK_CU_F1: F1AP message (ITTI) + Note over TASK_CU_F1: F1 message encoding + Note over TASK_CU_F1: ASN.1 encoding + box Grey DU/MAC + participant TASK_DU_F1 + participant MAC + end + Note over TASK_DU_F1: F1AP DL message handler + TASK_CU_F1->>+TASK_DU_F1: SCTP (ITTI) + Note over TASK_DU_F1: F1 message decoding + Note over TASK_DU_F1: ASN.1 decoding + TASK_DU_F1->>+MAC: F1AP message (function call) + Note over MAC: MAC DL message handler ``` - +-------------+ - | | - | CU/RRC | - | | - +-------------+ - | ^ - Callback def: mac_rrc_dl.h | | No handler needed: - F1 impl: mac_rrc_dl_f1ap.c | | RRC has ITTI -Monolithic: mac_rrc_dl_direct.c | | - | | - DL | | UL - | | - | | Callback def: mac_rrc_ul.h - Message handler: | | F1 impl: mac_rrc_ul_f1ap.c - mac_rrc_dl_handler.c | | Monolithic: mac_rrc_ul_direct.c - v | - +-------------+ - | | - | DU/MAC | - | | - +-------------+ +and for the uplink F1AP messages: + +```mermaid +sequenceDiagram + box rgba(17,158,189,255) CU/RRC + participant TASK_RRC_GNB + participant TASK_CU_F1 + end + box Grey DU/MAC + participant TASK_DU_F1 + participant TASK_MAC_GNB + end + Note over TASK_MAC_GNB: MAC/RRC callback + TASK_MAC_GNB->>+TASK_DU_F1: F1AP message (ITTI) + Note over TASK_DU_F1: F1 message encoding + Note over TASK_DU_F1: ASN.1 encoding + Note over TASK_CU_F1: F1AP UL message handler + TASK_DU_F1->>+TASK_CU_F1: SCTP (ITTI) + Note over TASK_CU_F1: F1 message decoding + Note over TASK_CU_F1: ASN.1 decoding + TASK_CU_F1->>+TASK_RRC_GNB: F1AP message (ITTI) ``` -# Data plane status (F1-U) +Alternative sequence handling (e.g. Monolithic), for downlink: -F1-U uses GTP-U for information exchange. The GTP protocol uses some extensions -for NR-U protocol to report buffer status, but this is not properly tested and -might malfunction. One limitation is that the current implementation -acknowledges each packet individually. +```mermaid +sequenceDiagram + box rgba(17,158,189,255) RRC + participant TASK_RRC_GNB + end + Note over TASK_RRC_GNB: mac_rrc_dl_direct.c callback + box Grey MAC + participant TASK_MAC_GNB + end + TASK_RRC_GNB->>+TASK_MAC_GNB: raw F1AP message + Note over TASK_MAC_GNB: mac_rrc_dl_handler.c +``` +and for the uplink: -Multiple DUs at one CU are supported in the data plane (as in the control plane). +```mermaid +sequenceDiagram + box rgba(17,158,189,255) RRC + participant TASK_RRC_GNB + end + box Grey MAC + participant TASK_MAC_GNB + end + Note over TASK_MAC_GNB: mac_rrc_ul_direct.c callback + TASK_MAC_GNB->>+TASK_RRC_GNB: raw F1AP message (ITTI) +``` + +## F1-U + +Current status: + +* Buffer status report over GTP-U +* Each packet is acknowledged individually +* Support of multiple DUs per CU # How to run diff --git a/doc/FEATURE_SET.md b/doc/FEATURE_SET.md index 17d3f3b801..5469687ade 100644 --- a/doc/FEATURE_SET.md +++ b/doc/FEATURE_SET.md @@ -197,13 +197,17 @@ These modes of operation are supported: ## gNB F1AP - Integration of F1AP messages and procedures for the control plane exchanges between the CU and DU entities according to 38.473 Rel. 16 - - F1 Setup request/response/failure - - F1 DL/UL RRC message transfer - - F1 Initial UL RRC message transfer - - F1 UE Context setup request/response - - F1 UE Context modification request/response - - F1 UE Context modification required - - F1 UE Context release req/cmd/complete + - F1 Interface Management: + * F1 Setup request/response/failure + - F1 RRC Message Transfer: + * F1 Initial UL RRC Message Transfer + * F1 DL RRC Message Transfer + * F1 UL RRC Message Transfer + - F1 UE Context Management: + * F1 UE Context setup request/response + * F1 UE Context modification request/response + * F1 UE Context modification required + * F1 UE Context release req/cmd/complete - F1 gNB CU configuration update - F1 Reset (handled at DU only, full reset only) - Interface with RRC diff --git a/doc/NR_SA_Tutorial_OAI_nrUE.md b/doc/NR_SA_Tutorial_OAI_nrUE.md index 8c1b0afaf1..043820d11b 100644 --- a/doc/NR_SA_Tutorial_OAI_nrUE.md +++ b/doc/NR_SA_Tutorial_OAI_nrUE.md @@ -159,6 +159,41 @@ cd ~/openairinterface5g/cmake_targets/ran_build/build sudo ./nr-uesoftmodem -r 32 --numerology 3 --band 257 -C 27533280000 --sa --uicc0.imsi 001010000000001 --ssb 72 --rfsim ``` +### Connection to an NG-Core + +A configuration file can be fed to the nrUE command line in order to connect to the local NGC. + +The nrUE configuration file (e.g. [ue.conf](../targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf)) is structured in a key-value format and contains the relevant UICC parameters that are necessary to authenticate the UE to the local 5GC. E.g.: + +```shell +uicc0 = { + imsi = "001010000000001"; + key = "fec86ba6eb707ed08905757b1bb44b8f"; + opc = "C42449363BBAD02B66D16BC975D77CC1"; + dnn = "oai"; + nssai_sst = 1; +} +``` + +| **Parameter** | **Description** | **Default Value** | +|---------------|-----------------|-------------------| +| **IMSI** | Unique identifier for the UE within the mobile network. Used by the network to identify the UE during authentication. It ensures that the UE is correctly identified by the network. | 001010000000001 | +| **key** | Cryptographic key shared between the UE and the network, used for encryption during the authentication process. | `fec86ba6eb707ed08905757b1bb44b8f` | +| **OPC** | Operator key for the Milenage Authentication and Key Agreement algorithm used for encryption during the authentication process. | Ensures secure communication between the UE and the network by matching the encryption keys. | `C42449363BBAD02B66D16BC975D77CC1` | +| **DNN** | Specifies the name of the data network the UE wishes to connect to, similar to an APN in 4G networks. | `oai` | +| **NSSAI** | Allows the UE to select the appropriate network slice, which provides different QoS. | `1` | + +The UE configuration must match the one of the network's AMF. The nrUE can connect by default to OAI CN5G with no need to provide the configuration file. + +When running the `nr-uesoftmodem`, one can specify the nrUE configuration file using the `-O` option. E.g.: + +```bash +sudo ./nr-uesoftmodem --rfsim --rfsimulator.serveraddr 127.0.0.1 --sa -r 106 --numerology 1 --band 78 -C 3619200000 -O ~/nrue.uicc.conf +``` +The CL option `--uicc0.imsi` can override the IMSI value in the configuration file if necessary (e.g. when running multiple UEs): `--uicc0.imsi 001010000000001`. + +More details available at [ci-scripts/yaml_files/5g_rfsimulator/README.md](../ci-scripts/yaml_files/5g_rfsimulator/README.md). + ## 5.2 End-to-end connectivity test - Ping test from the UE host to the CN5G ```bash diff --git a/doc/README.md b/doc/README.md index 636c30d26b..4b05242e59 100644 --- a/doc/README.md +++ b/doc/README.md @@ -106,7 +106,7 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs. ## Special-purpose libraries -- OAI has a scope based on Xforms, described in [this README](../openair1/PHY/TOOLS/readme.md) +- OAI has two scopes: one based on Xforms and one based on imgui, described in [this README](../openair1/PHY/TOOLS/readme.md) - OAI comes with an integrated [telnet server](../common/utils/telnetsrv/DOC/telnethelp.md) to monitor and control - OAI comes with an integrated [web server](../common/utils/websrv/DOC/websrv.md) diff --git a/doc/RUNMODEM.md b/doc/RUNMODEM.md index 378fe3be16..1bc6268199 100644 --- a/doc/RUNMODEM.md +++ b/doc/RUNMODEM.md @@ -102,47 +102,65 @@ Command line parameters for UE in `--sa` mode: - `--band` : NR band number (default value 78) - `--ssb` : SSB start subcarrier (default value 516) -**Optional parameters**: -- `-E`: use three-quarter sampling for split 8 sample rate. Required for - certain radios (e.g., 40MHz with B210). If used on the gNB, it is a good idea - to use for the UE as well (and vice versa). -- `--ue-scan-carrier` : scan for cells in current bandwidth. This option can be used if the SSB position of the gNB is unknown. If multiple cells are detected, the UE will try to connect to the first cell. By default, this option is disabled and the UE attempts to only decode SSB given by `--ssb`. +To simplify the configuration for the user testing OAI UE with OAI gNB, the latter prints the following LOG that guides the user to correctly set some of the UE command line parameters: -To simplify the configuration for the user testing OAI UE with OAI gNB, the latter prints the following LOG that guides the user to correctly set some of the UE command line parameters. - -``` +```shell [PHY] Command line parameters for OAI UE: -C 3319680000 -r 106 --numerology 1 --ssb 516 ``` You can run this, using USRPs, on two separate machines: -``` +```shell sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --sa sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --sa ``` -With the RFsimulator (on the same machine): +With the **RFsimulator** (on the same machine), just add the option `--rfsim` to both gNB and NR UE command lines. -```bash -sudo ./nr-softmodem -O ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --rfsim --sa -sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --rfsim --sa -``` +UE capabilities can be passed according to the [UE Capabilities](#UE-Capabilities) section. -Additionally, at UE side `--uecap_file` option can be used to pass the UE Capabilities input file (path location + filename), e.g. `--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml` +A detailed tutorial is provided at this page [NR_SA_Tutorial_OAI_nrUE.md](./NR_SA_Tutorial_OAI_nrUE.md). -## Some useful NR-UE parameters +## Optional NR-UE command line options -Some other useful paramters of the UE are +Here are some useful command line options for the NR UE: -- `--ue-fo-compensation`: enables the frequency offset compenstation at the UE. This is useful when running over the air and/or without an external clock/time source -- `--usrp-args`: this is the equivalend paramter of `sdr_addrs` field in the gNB config file and can be used to identify the USRP and set some basic paramters (like the clock source) -- `--clock-source`: sets the clock-source (internal or external). -- `--time-source`: sets the time-source (internal or external). +| Parameter | Description | +|--------------------------|---------------------------------------------------------------------------------------------------------------| +| `--ue-scan-carrier` | Scan for cells in current bandwidth. This option can be used if the SSB position of the gNB is unknown. If multiple cells are detected, the UE will try to connect to the first cell. By default, this option is disabled and the UE attempts to only decode SSB given by `--ssb`. | +| `--ue-fo-compensation` | Enables the frequency offset compensation at the UE. Useful when running over the air and/or without an external clock/time source. | +| `--usrp-args` | Equivalent to the `sdr_addrs` field in the gNB config file. Used to identify the USRP and set some basic parameters (like the clock source). | +| `--clock-source` | Sets the clock source (internal or external). | +| `--time-source` | Sets the time source (internal or external). | -You can see all options by typing -``` +You can view all available options by typing: + +```shell ./nr-uesoftmodem --help ``` +## Common gNB and NR UE command line options + +### Three-quarter sampling + +The command line option `-E` can be used to enable three-quarter sampling for split 8 sample rate. Required for certain radios (e.g., 40MHz with B210). If used on the gNB, it is a good idea to use for the UE as well (and vice versa). + +### UE Capabilities + +The `--uecap_file` option can be used to pass the UE Capabilities input file (path location + filename), e.g.`--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml` for 1 layer or e.g. `--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports2.xml` for 2 layers. + +This option is available for the following combinations of operation modes and gNB/nrUE softmodems: + +| Mode | Executable | Description | +|------------|----------------|-----------------------------------------------------| +| SA | nr-uesoftmodem | Send UE capabilities from the UE to the gNB via RRC | +| phy-test | nr-softmodem | Mimic the reception of UE capabilities by the gNB | +| do-ra | nr-softmodem | Mimic the reception of UE capabilities by the gNB | + +e.g. + +```shell +sudo ./nr-uesoftmodem --sa -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 2 --ue-nb-ant-rx 2 --uecap_file /opt/oai-nr-ue/etc/uecap.xml +``` ## How to run a NTN configuration @@ -310,7 +328,7 @@ In summary: * `scp usera@machineA:/the/path/where/you/launched/nr-softmodem/r*config.raw userb@machineB:/the/path/where/you/will/launch/nr-uesoftmodem/` * Obviously this operation should be done before launching the `nr-uesoftmodem` executable. -In phy-test mode it is possible to mimic the reception of UE Capabilities at gNB by passing through the command line parameter `--uecap_file` the location and file name of the input UE Capability file, e.g. `--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml` (1 layer) or `--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports2.xml` (2 layers). +In phy-test mode it is possible to mimic the reception of UE Capabilities at gNB through the command line parameter `--uecap_file`. Refer to the [UE Capabilities](#UE-Capabilities) section for more details. ## noS1 setup with OAI UE @@ -346,7 +364,7 @@ The do-ra flag is used to ran the NR Random Access procedures in contention-free In order to run the RA, the `--do-ra` flag is needed for both the gNB and the UE. -In do-ra mode it is possible to mimic the reception of UE Capabilities at gNB by passing through the command line parameter `--uecap_file` the location and file name of the input UE Capability file, e.g. `--uecap_file ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml` +In do-ra mode it is possible to mimic the reception of UE Capabilities at gNB through the command line parameter `--uecap_file`. Refer to the [UE Capabilities](#UE-Capabilities) section for more details. To run using the RFsimulator: diff --git a/doc/UnitTests.md b/doc/UnitTests.md index c2e1285c4f..aa6b7b1123 100644 --- a/doc/UnitTests.md +++ b/doc/UnitTests.md @@ -25,7 +25,8 @@ ninja tests ``` Then, you can run `ctest` to run all tests: -``` + +```bash $ ctest Test project /home/richie/w/ctest/build Start 1: nr_rlc_tests @@ -36,6 +37,8 @@ Test project /home/richie/w/ctest/build Total Test time (real) = 0.06 sec ``` +The user can see all available tests by typing `ctest -N` and then run a specific with `ctest -R `, e.g. `ctest -R nr_rlc_tests`. + A couple of interesting variables are `--verbose`, `--output-on-failure`. # How to add a new test @@ -59,7 +62,8 @@ process is In the simplest case, in an existing `CMakeLists.txt`, you might add the following: -``` + +```bash if(ENABLE_TESTS) add_executable(my_test mytest.c) add_dependencies(tests my_test) diff --git a/docker/Dockerfile.base.rocky b/docker/Dockerfile.base.rocky index 0e9d630bac..44be527344 100644 --- a/docker/Dockerfile.base.rocky +++ b/docker/Dockerfile.base.rocky @@ -35,6 +35,7 @@ ENV UHD_VERSION=4.4.0.0 RUN dnf install 'dnf-command(config-manager)' -y && \ dnf install epel-release -y && crb enable && \ dnf update -y && \ + cat /etc/os-release && \ dnf install -y \ #gcc needed for build_oai gcc gcc-c++ \ @@ -42,9 +43,22 @@ RUN dnf install 'dnf-command(config-manager)' -y && \ file \ psmisc \ git \ + meson \ + wget \ + xz \ + pkg-config \ + pcre2-devel \ + m4 \ + python3-devel \ + numactl-libs \ + numactl-devel \ + gcc-toolset-12-gcc \ + gcc-toolset-12-gcc-c++ \ # python3-pip and pyyaml are used for conf template generation python3-pip && \ - pip3 install --ignore-installed pyyaml + pip3 install --ignore-installed pyyaml && \ + echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \ + echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local-lib.conf # Add "Tini - A tiny but valid init for containers", https://github.com/krallin/tini # it will be copied into target containers, to print exit numbers and handle signals properly @@ -65,7 +79,8 @@ WORKDIR /oai-ran COPY oaienv . #run build_oai -I to get the builder image -RUN /bin/sh oaienv && \ +RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai -I --install-optional-packages -w USRP + scl enable gcc-toolset-12 "\ + ./build_oai -I --install-optional-packages -w USRP" diff --git a/docker/Dockerfile.base.ubuntu22.cross-arm64 b/docker/Dockerfile.base.ubuntu22.cross-arm64 index 74af391660..1bcba7bdd6 100644 --- a/docker/Dockerfile.base.ubuntu22.cross-arm64 +++ b/docker/Dockerfile.base.ubuntu22.cross-arm64 @@ -66,6 +66,7 @@ RUN apt-get update && \ libssl-dev:arm64 \ libtool:arm64 \ zlib1g-dev:arm64 \ + libyaml-cpp-dev:arm64 \ # cross build libs and compilers libc6-dev-i386 \ gcc-11-aarch64-linux-gnu \ diff --git a/docker/Dockerfile.build.rocky b/docker/Dockerfile.build.rocky index 3fe164b10d..1126c42057 100644 --- a/docker/Dockerfile.build.rocky +++ b/docker/Dockerfile.build.rocky @@ -35,4 +35,12 @@ COPY . . RUN /bin/sh oaienv && \ cd cmake_targets && \ mkdir -p log && \ - ./build_oai --eNB --gNB --RU --UE --nrUE --ninja --build-lib "telnetsrv enbscope uescope nrscope" -w USRP -t Ethernet --noavx512 -c + CXXFLAGS="-Werror -fstack-protector-strong" CFLAGS="-Werror -fstack-protector-strong" ./build_oai -c --ninja \ + --eNB --gNB --RU --UE --nrUE \ + --build-lib "telnetsrv enbscope uescope nrscope" \ + -w USRP -t Ethernet \ + --noavx512 && \ + echo "---- ldd on executables ----" && \ + ldd ran_build/build/*softmodem* ran_build/build/nr-cuup ran_build/build/oairu && \ + echo "---- ldd on shared libraries ----" && \ + ldd ran_build/build/*.so diff --git a/docker/Dockerfile.build.ubuntu22.cross-arm64 b/docker/Dockerfile.build.ubuntu22.cross-arm64 index 349a4f791c..faadf79eb2 100644 --- a/docker/Dockerfile.build.ubuntu22.cross-arm64 +++ b/docker/Dockerfile.build.ubuntu22.cross-arm64 @@ -41,6 +41,8 @@ RUN /bin/sh oaienv && \ ninja ldpc_generators generate_T > ../../log/host-tools.txt 2>&1 && \ echo "====== End of log for host-tools ======" && \ cd ../build-cross/ && \ + # install missing libyaml-cpp-dev for arm64 + apt install -y libyaml-cpp-dev:arm64 && \ cmake ../../.. -GNinja -DCMAKE_TOOLCHAIN_FILE=../../../cmake_targets/cross-arm.cmake -DNATIVE_DIR=../build && \ echo "====== Start of log for cross-build executables ======" && \ # making always passing so we can analyze the all.txt file diff --git a/docker/Dockerfile.gNB.fhi72.rocky b/docker/Dockerfile.gNB.fhi72.rocky new file mode 100644 index 0000000000..a98c0b1948 --- /dev/null +++ b/docker/Dockerfile.gNB.fhi72.rocky @@ -0,0 +1,117 @@ +#/* +# * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more +# * contributor license agreements. See the NOTICE file distributed with +# * this work for additional information regarding copyright ownership. +# * The OpenAirInterface Software Alliance licenses this file to You under +# * the OAI Public License, Version 1.1 (the "License"); you may not use this file +# * except in compliance with the License. +# * You may obtain a copy of the License at +# * +# * http://www.openairinterface.org/?page_id=698 +# * +# * Unless required by applicable law or agreed to in writing, software +# * distributed under the License is distributed on an "AS IS" BASIS, +# * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# * See the License for the specific language governing permissions and +# * limitations under the License. +# *------------------------------------------------------------------------------- +# * For more information about the OpenAirInterface (OAI) Software Alliance: +# * contact@openairinterface.org +# */ +#--------------------------------------------------------------------- +# +# Dockerfile for the Open-Air-Interface 7.2 Fronthaul +# Valid for Rocky 9 +# +#--------------------------------------------------------------------- + +FROM ran-base:latest AS gnb-base + +FROM ran-build-fhi72:latest AS gnb-build + +#start from scratch for target executable +FROM docker.io/rockylinux:9 AS oai-gnb-fhi72 +ENV TZ=Europe/Paris + +RUN dnf update -y && \ + dnf install -y \ + procps-ng \ + libXpm \ + libX11 \ + lksctp-tools \ + xz \ + numactl-libs \ + numactl-devel \ + pkg-config \ + pciutils \ + tzdata \ + gdb \ + python3 \ + python3-pip \ + net-tools \ + iputils && \ + pip3 install six && \ + pip3 install requests && \ + echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \ + echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local-lib.conf + +WORKDIR /opt/oai-gnb/bin +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/nr-softmodem ./ +COPY ./docker/scripts/gnb_entrypoint.sh ./entrypoint.sh + +COPY --from=gnb-build \ + /oai-ran/cmake_targets/ran_build/build/librfsimulator.so \ + /oai-ran/cmake_targets/ran_build/build/liboran_fhlib_5g.so \ + /oai-ran/cmake_targets/ran_build/build/liboai_transpro.so \ + /oai-ran/cmake_targets/ran_build/build/libcoding.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_libconfig.so \ + /oai-ran/cmake_targets/ran_build/build/libdfts.so \ + /oai-ran/cmake_targets/ran_build/build/libldpc*.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ + /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ci.so \ + /usr/local/lib/ + +## Libxran library +COPY --from=gnb-build \ + /opt/phy/fhi_lib/lib/build/libxran.so \ + /opt/phy/fhi_lib/lib/build/ + +## Copy all DPDK libraries as liboran_fhlib_5g.so require most of them +COPY --from=gnb-build /usr/local/lib64/ /usr/local/lib64/ + +## Copy E2 SM models +COPY --from=gnb-build /usr/local/lib64/flexric /usr/local/lib64/flexric + +# Copying file for configuration module +COPY --from=gnb-base \ + /lib64/libconfig.so.11 \ + /lib64/libforms.so.2 \ + /lib64/libblas.so.3 \ + /lib64/libcblas.so.3 \ + /lib64/liblapack.so.3 \ + /lib64/liblapacke.so.3 \ + /lib64/libicudata.so.67 \ + /lib64/libicui18n.so.67 \ + /lib64/libicuuc.so.67 \ + /lib64/ + +RUN ldconfig && \ + echo "---- ldd on executable nr-softmodem ----" && \ + ldd /opt/oai-gnb/bin/nr-softmodem && \ + echo "---- ldd on shared libraries ----" && \ + ldd /usr/local/lib/librfsimulator.so \ + /usr/local/lib/liboran_fhlib_5g.so \ + /usr/local/lib/liboai_transpro.so \ + /usr/local/lib/libcoding.so \ + /usr/local/lib/libparams_libconfig.so \ + /usr/local/lib/libdfts.so \ + /usr/local/lib/libldpc*.so \ + /usr/local/lib/libtelnetsrv.so \ + /usr/local/lib/libtelnetsrv_ci.so \ + /opt/phy/fhi_lib/lib/build/libxran.so + +WORKDIR /opt/oai-gnb +COPY --from=gnb-base /tini /tini +ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-gnb/bin/entrypoint.sh"] +CMD ["/opt/oai-gnb/bin/nr-softmodem", "-O", "/opt/oai-gnb/etc/gnb.conf"] diff --git a/docker/Dockerfile.nrUE.rhel9 b/docker/Dockerfile.nrUE.rhel9 index 7279809fdb..8ab486bd85 100644 --- a/docker/Dockerfile.nrUE.rhel9 +++ b/docker/Dockerfile.nrUE.rhel9 @@ -34,6 +34,9 @@ FROM ran-build:latest AS nr-ue-build FROM registry.access.redhat.com/ubi9/ubi:latest AS oai-nr-ue ENV TZ=Europe/Paris +# Install EPEL for RHEL9 +RUN dnf install -y https://dl.fedoraproject.org/pub/epel/epel-release-latest-9.noarch.rpm + RUN dnf update -y && \ dnf install -y \ lksctp-tools \ @@ -47,7 +50,8 @@ RUN dnf update -y && \ python3 \ python3-pip \ libXpm \ - libX11 && \ + libX11 \ + yaml-cpp-devel && \ pip3 install six && \ pip3 install requests && \ echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \ @@ -70,6 +74,7 @@ COPY --from=nr-ue-build \ /oai-ran/cmake_targets/ran_build/build/libldpc*.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \ /usr/local/lib/ COPY --from=nr-ue-base \ @@ -117,5 +122,5 @@ RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai WORKDIR /opt/oai-nr-ue COPY --from=nr-ue-base /tini /tini -CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem", "-O", "/opt/oai-nr-ue/etc/nr-ue.conf"] +CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem"] ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-nr-ue/bin/entrypoint.sh"] diff --git a/docker/Dockerfile.nrUE.rocky b/docker/Dockerfile.nrUE.rocky index ec5ecf6a99..d0455527fa 100644 --- a/docker/Dockerfile.nrUE.rocky +++ b/docker/Dockerfile.nrUE.rocky @@ -71,6 +71,7 @@ COPY --from=nr-ue-build \ /oai-ran/cmake_targets/ran_build/build/libldpc*.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \ /usr/local/lib/ COPY --from=nr-ue-base \ @@ -118,5 +119,5 @@ RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai WORKDIR /opt/oai-nr-ue COPY --from=nr-ue-base /tini /tini -CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem", "-O", "/opt/oai-nr-ue/etc/nr-ue.conf"] +CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem"] ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-nr-ue/bin/entrypoint.sh"] diff --git a/docker/Dockerfile.nrUE.ubuntu22 b/docker/Dockerfile.nrUE.ubuntu22 index 74ae9620b0..dc0d425956 100644 --- a/docker/Dockerfile.nrUE.ubuntu22 +++ b/docker/Dockerfile.nrUE.ubuntu22 @@ -64,7 +64,8 @@ RUN apt-get update && \ iputils-ping \ iproute2 \ iperf3 \ - iperf && \ + iperf \ + libyaml-cpp-dev && \ # if the --sanitize option was used to build, additional packages are required /bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \ #libasan5 \ # see below @@ -97,6 +98,7 @@ COPY --from=nr-ue-build \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_ciUE.so \ /oai-ran/cmake_targets/ran_build/build/libtelnetsrv_5Gue.so \ + /oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \ /usr/local/lib/ # Now we are copying from builder-image the UHD files. @@ -123,5 +125,5 @@ RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai WORKDIR /opt/oai-nr-ue COPY --from=nr-ue-base /tini /tini -CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem", "-O", "/opt/oai-nr-ue/etc/nr-ue.conf"] +CMD ["/opt/oai-nr-ue/bin/nr-uesoftmodem"] ENTRYPOINT ["/tini", "-v", "--", "/opt/oai-nr-ue/bin/entrypoint.sh"] diff --git a/docker/scripts/nr_ue_entrypoint.sh b/docker/scripts/nr_ue_entrypoint.sh index 31382a9b07..26ae6da7f7 100755 --- a/docker/scripts/nr_ue_entrypoint.sh +++ b/docker/scripts/nr_ue_entrypoint.sh @@ -9,8 +9,13 @@ echo "==================================" echo "/proc/sys/kernel/core_pattern=$(cat /proc/sys/kernel/core_pattern)" if [ ! -f $CONFIGFILE ]; then - echo "No configuration file found: please mount at $CONFIGFILE" - exit 255 + echo "No configuration file $CONFIGFILE found: attempting to find YAML config" + YAML_CONFIGFILE=$PREFIX/etc/nr-ue.yaml + if [ ! -f $YAML_CONFIGFILE ]; then + echo "No configuration file $YAML_CONFIGFILE found. Please mount either at $CONFIGFILE or $YAML_CONFIGFILE" + exit 255 + fi + CONFIGFILE=$YAML_CONFIGFILE fi echo "==================================" @@ -35,6 +40,9 @@ while [[ $# -gt 0 ]]; do shift done +new_args+=("-O") +new_args+=("$CONFIGFILE") + # enable printing of stack traces on assert export OAI_GDBSTACKS=1 diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 6f49537b3a..c28b741406 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -734,14 +734,19 @@ int main( int argc, char **argv ) { printf("ALL RUs ready - init gNBs\n"); for (int idx=0;idxif_inst->sl_ahead = sl_ahead; - if(IS_SOFTMODEM_DOSCOPE) { + if (IS_SOFTMODEM_DOSCOPE || IS_SOFTMODEM_IMSCOPE_ENABLED) { sleep(1); scopeParms_t p; - p.argc=&argc; - p.argv=argv; - p.gNB=RC.gNB[0]; - p.ru=RC.ru[0]; - load_softscope("nr",&p); + p.argc = &argc; + p.argv = argv; + p.gNB = RC.gNB[0]; + p.ru = RC.ru[0]; + if (IS_SOFTMODEM_DOSCOPE) { + load_softscope("nr", &p); + } + if (IS_SOFTMODEM_IMSCOPE_ENABLED) { + load_softscope("im", &p); + } } if (NFAPI_MODE != NFAPI_MODE_PNF && NFAPI_MODE != NFAPI_MODE_VNF && NFAPI_MODE != NFAPI_MODE_AERIAL) { diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 5049e269d2..99ce9040d4 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -521,8 +521,11 @@ int main(int argc, char **argv) init_openair0(); lock_memory_to_ram(); - if(IS_SOFTMODEM_DOSCOPE) { - load_softscope("nr",PHY_vars_UE_g[0][0]); + if (IS_SOFTMODEM_DOSCOPE) { + load_softscope("nr", PHY_vars_UE_g[0][0]); + } + if (IS_SOFTMODEM_IMSCOPE_ENABLED) { + load_softscope("im", PHY_vars_UE_g[0][0]); } for (int inst = 0; inst < NB_UE_INST; inst++) { diff --git a/executables/softmodem-bits.h b/executables/softmodem-bits.h new file mode 100644 index 0000000000..6d527148cd --- /dev/null +++ b/executables/softmodem-bits.h @@ -0,0 +1,61 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#ifndef SOFTMODEM_BITS_H +#define SOFTMODEM_BITS_H + +// clang-format off +#define BIT_0 (1 << 0) +#define BIT_1 (1 << 1) +#define BIT_2 (1 << 2) +#define BIT_10 (1 << 10) +#define BIT_12 (1 << 12) +#define BIT_13 (1 << 13) +#define BIT_15 (1 << 15) +#define BIT_16 (1 << 16) +#define BIT_17 (1 << 17) +#define BIT_18 (1 << 18) +#define BIT_20 (1 << 20) +#define BIT_21 (1 << 21) +#define BIT_22 (1 << 22) +#define BIT_23 (1 << 23) +#define BIT_24 (1 << 24) +#define BIT_25 (1 << 25) + +#define SOFTMODEM_NOS1_BIT BIT_0 +#define SOFTMODEM_NOKRNMOD_BIT BIT_1 +#define SOFTMODEM_NONBIOT_BIT BIT_2 +#define SOFTMODEM_RFSIM_BIT BIT_10 +#define SOFTMODEM_SIML1_BIT BIT_12 +#define SOFTMODEM_DLSIM_BIT BIT_13 +#define SOFTMODEM_DOSCOPE_BIT BIT_15 +#define SOFTMODEM_RECPLAY_BIT BIT_16 +#define SOFTMODEM_TELNETCLT_BIT BIT_17 +#define SOFTMODEM_RECRECORD_BIT BIT_18 +#define SOFTMODEM_ENB_BIT BIT_20 +#define SOFTMODEM_GNB_BIT BIT_21 +#define SOFTMODEM_4GUE_BIT BIT_22 +#define SOFTMODEM_5GUE_BIT BIT_23 +#define SOFTMODEM_NOSTATS_BIT BIT_24 +#define SOFTMODEM_IMSCOPE_BIT BIT_25 +// clang-format on + +#endif // SOFTMODEM_BITS_H diff --git a/executables/softmodem-common.c b/executables/softmodem-common.c index be2a79eb64..d566767902 100644 --- a/executables/softmodem-common.c +++ b/executables/softmodem-common.c @@ -99,6 +99,7 @@ void get_common_options(configmodule_interface_t *cfg, uint32_t execmask) uint32_t start_websrv = 0; uint32_t noS1 = 0, nonbiot = 0; uint32_t rfsim = 0, do_forms = 0; + uint32_t enable_imscope = 0; int nfapi_index = 0; char *logmem_filename = NULL; check_execmask(execmask); @@ -158,6 +159,10 @@ void get_common_options(configmodule_interface_t *cfg, uint32_t execmask) set_softmodem_optmask(SOFTMODEM_DOSCOPE_BIT); } + if (enable_imscope) { + set_softmodem_optmask(SOFTMODEM_IMSCOPE_BIT); + } + if (start_websrv) { load_module_shlib("websrv", NULL, 0, NULL); } diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 110a5c60a7..141afa1a37 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -32,6 +32,7 @@ #ifndef SOFTMODEM_COMMON_H #define SOFTMODEM_COMMON_H #include "openair1/PHY/defs_common.h" +#include "softmodem-bits.h" #ifdef __cplusplus extern "C" { @@ -76,6 +77,7 @@ extern "C" #define CONFIG_HLP_256QAM "Use the 256 QAM mcs table for PDSCH\n" #define CONFIG_HLP_CHESTFREQ "Set channel estimation type in frequency domain. 0-Linear interpolation (default). 1-PRB based averaging of channel estimates in frequency. \n" #define CONFIG_HLP_CHESTTIME "Set channel estimation type in time domain. 0-Symbols take estimates of the last preceding DMRS symbol (default). 1-Symbol based averaging of channel estimates in time. \n" +#define CONFIG_HLP_IMSCOPE "Enable phy scope based on imgui and implot" #define CONFIG_HLP_NONSTOP "Go back to frame sync mode after 100 consecutive PBCH failures\n" //#define CONFIG_HLP_NUMUES "Set the number of UEs for the emulation" @@ -188,6 +190,7 @@ extern int usrp_tx_thread; {"sync-ref", CONFIG_HLP_SYNC_REF, 0, .uptr=&SYNC_REF, .defintval=0, TYPE_UINT, 0}, \ {"A" , CONFIG_HLP_TADV, 0, .iptr=&softmodem_params.command_line_sample_advance,.defintval=0, TYPE_INT, 0}, \ {"E" , CONFIG_HLP_TQFS, PARAMFLAG_BOOL, .iptr=&softmodem_params.threequarter_fs, .defintval=0, TYPE_INT, 0}, \ + {"imscope" , CONFIG_HLP_IMSCOPE, PARAMFLAG_BOOL, .uptr=&enable_imscope, .defintval=0, TYPE_UINT, 0}, \ } // clang-format on @@ -235,6 +238,7 @@ extern int usrp_tx_thread; { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ + { .s5 = { NULL } }, \ } // clang-format on @@ -274,21 +278,6 @@ extern int usrp_tx_thread; /***************************************************************************************************************************************/ -#define SOFTMODEM_NOS1_BIT (1<<0) -#define SOFTMODEM_NONBIOT_BIT (1<<2) -#define SOFTMODEM_RFSIM_BIT (1<<10) -#define SOFTMODEM_SIML1_BIT (1<<12) -#define SOFTMODEM_DLSIM_BIT (1<<13) -#define SOFTMODEM_DOSCOPE_BIT (1<<15) -#define SOFTMODEM_RECPLAY_BIT (1<<16) -#define SOFTMODEM_TELNETCLT_BIT (1<<17) -#define SOFTMODEM_RECRECORD_BIT (1<<18) -#define SOFTMODEM_ENB_BIT (1<<20) -#define SOFTMODEM_GNB_BIT (1<<21) -#define SOFTMODEM_4GUE_BIT (1<<22) -#define SOFTMODEM_5GUE_BIT (1<<23) -#define SOFTMODEM_NOSTATS_BIT (1<<24) - #define SOFTMODEM_FUNC_BITS (SOFTMODEM_ENB_BIT | SOFTMODEM_GNB_BIT | SOFTMODEM_5GUE_BIT | SOFTMODEM_4GUE_BIT) #define MAPPING_SOFTMODEM_FUNCTIONS {{"enb",SOFTMODEM_ENB_BIT},{"gnb",SOFTMODEM_GNB_BIT},{"4Gue",SOFTMODEM_4GUE_BIT},{"5Gue",SOFTMODEM_5GUE_BIT}} @@ -307,6 +296,7 @@ extern int usrp_tx_thread; #define IS_SOFTMODEM_4GUE_BIT ( get_softmodem_optmask() & SOFTMODEM_4GUE_BIT) #define IS_SOFTMODEM_5GUE_BIT ( get_softmodem_optmask() & SOFTMODEM_5GUE_BIT) #define IS_SOFTMODEM_NOSTATS_BIT ( get_softmodem_optmask() & SOFTMODEM_NOSTATS_BIT) +#define IS_SOFTMODEM_IMSCOPE_ENABLED ( get_softmodem_optmask() & SOFTMODEM_IMSCOPE_BIT) typedef struct { uint64_t optmask; diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index d47eec2278..68333c821c 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -199,7 +199,7 @@ int phy_init_nr_gNB(PHY_VARS_gNB *gNB) int n_buf = Prx*max_ul_mimo_layers; int nb_re_pusch = N_RB_UL * NR_NB_SC_PER_RB; - int nb_re_pusch2 = (nb_re_pusch + 15) & ~15; + int nb_re_pusch2 = ALIGN_UP_16(nb_re_pusch); gNB->pusch_vars = (NR_gNB_PUSCH *)malloc16_clear(gNB->max_nb_pusch * sizeof(NR_gNB_PUSCH)); for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) { diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index 75ee35385f..4640ff0746 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -1263,7 +1263,7 @@ static void inner_rx(PHY_VARS_gNB *gNB, int nb_layer = rel15_ul->nrOfLayers; int nb_rx_ant = frame_parms->nb_antennas_rx; int dmrs_symbol_flag = (rel15_ul->ul_dmrs_symb_pos >> symbol) & 0x01; - int buffer_length = (rel15_ul->rb_size * NR_NB_SC_PER_RB + 15) & ~15; + int buffer_length = ALIGN_UP_16(rel15_ul->rb_size * NR_NB_SC_PER_RB); c16_t rxFext[nb_rx_ant][buffer_length] __attribute__((aligned(32))); c16_t chFext[nb_layer][nb_rx_ant][buffer_length] __attribute__((aligned(32))); @@ -1631,7 +1631,7 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, // extract the data in the OFDM frame, to the start of the array int soffset = (slot % RU_RX_SLOT_DEPTH) * frame_parms->symbols_per_slot * frame_parms->ofdm_symbol_size; - nb_re_pusch = (nb_re_pusch + 15) & ~15; + nb_re_pusch = ALIGN_UP_16(nb_re_pusch); int dmrs_symbol; if (gNB->chest_time == 0) dmrs_symbol = get_valid_dmrs_idx_for_channel_est(rel15_ul->ul_dmrs_symb_pos, meas_symbol); @@ -1699,8 +1699,8 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, start_meas(&gNB->rx_pusch_symbol_processing_stats); int numSymbols = gNB->num_pusch_symbols_per_thread; + int total_res = 0; for(uint8_t symbol = rel15_ul->start_symbol_index; symbol < end_symbol; symbol += numSymbols) { - int total_res = 0; for (int s = 0; s < numSymbols; s++) { pusch_vars->ul_valid_re_per_slot[symbol+s] = get_nb_re_pusch(frame_parms,rel15_ul,symbol+s); pusch_vars->llr_offset[symbol+s] = ((symbol+s) == rel15_ul->start_symbol_index) ? @@ -1744,5 +1744,26 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, } stop_meas(&gNB->rx_pusch_symbol_processing_stats); + + // Copy the data to the scope. This cannot be performed in one call to gNBscopeCopy because the data is not contiguous in the + // buffer due to reference symbol extraction and padding. The gNBscopeCopy call is broken up into steps: trylock, copy, unlock. + metadata mt = {.slot = slot, .frame = frame}; + if (gNBTryLockScopeData(gNB, gNBPuschRxIq, sizeof(c16_t), 1, total_res, &mt)) { + int buffer_length = ALIGN_UP_16(rel15_ul->rb_size * NR_NB_SC_PER_RB); + size_t offset = 0; + for (uint8_t symbol = rel15_ul->start_symbol_index; symbol < (rel15_ul->start_symbol_index + rel15_ul->nr_of_symbols); + symbol++) { + gNBscopeCopyUnsafe(gNB, + gNBPuschRxIq, + &pusch_vars->rxdataF_comp[0][symbol * buffer_length], + sizeof(c16_t) * pusch_vars->ul_valid_re_per_slot[symbol], + offset, + symbol - rel15_ul->start_symbol_index); + offset += sizeof(c16_t) * pusch_vars->ul_valid_re_per_slot[symbol]; + } + gNBunlockScopeData(gNB, gNBPuschRxIq) + } + uint32_t total_llrs = total_res * rel15_ul->qam_mod_order * rel15_ul->nrOfLayers; + gNBscopeCopyWithMetadata(gNB, gNBPuschLlr, pusch_vars->llr, sizeof(c16_t), 1, total_llrs, 0, &mt); return 0; } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c index 2529fdd38e..6c5c37385b 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_llr_computation.c @@ -31,357 +31,13 @@ */ #include "PHY/defs_gNB.h" -#include "PHY/defs_nr_common.h" #include "PHY/sse_intrin.h" -#include "PHY/impl_defs_top.h" +#include "nr_phy_common.h" #ifdef __aarch64__ #define USE_128BIT #endif -int16_t saturating_sub(int16_t a, int16_t b) -{ - int32_t result = (int32_t)a - (int32_t)b; - - if (result < INT16_MIN) { - return INT16_MIN; - } else if (result > INT16_MAX) { - return INT16_MAX; - } else { - return (int16_t)result; - } -} - -//---------------------------------------------------------------------------------------------- -// QPSK -//---------------------------------------------------------------------------------------------- -void nr_ulsch_qpsk_llr(int32_t *rxdataF_comp, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol) -{ - c16_t *rxF = (c16_t *)rxdataF_comp; - c16_t *llr32 = (c16_t *)ulsch_llr; - for (int i = 0; i < nb_re; i++) { - llr32[i].r = rxF[i].r >> 3; - llr32[i].i = rxF[i].i >> 3; - } -} - -//---------------------------------------------------------------------------------------------- -// 16-QAM -//---------------------------------------------------------------------------------------------- - -void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, int32_t *ul_ch_mag, int16_t *ulsch_llr, uint32_t nb_re, uint8_t symbol) -{ - simde__m256i *rxF_256 = (simde__m256i *)rxdataF_comp; - simde__m256i *ch_mag = (simde__m256i *)ul_ch_mag; - int64_t *llr_64 = (int64_t *)ulsch_llr; - -#ifndef USE_128BIT - simde__m256i xmm0, xmm1, xmm2; - - for (int i = 0; i < (nb_re >> 3); i++) { - // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| - xmm0 = simde_mm256_abs_epi16(*rxF_256); - // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 - xmm0 = simde_mm256_subs_epi16(*ch_mag, xmm0); - - xmm1 = simde_mm256_unpacklo_epi32(*rxF_256, xmm0); - xmm2 = simde_mm256_unpackhi_epi32(*rxF_256, xmm0); - - // xmm1 |1st 2ed 3rd 4th 9th 10th 13rd 14th| - // xmm2 |5th 6th 7th 8th 11st 12ed 15th 16th| - - *llr_64++ = simde_mm256_extract_epi64(xmm1, 0); - *llr_64++ = simde_mm256_extract_epi64(xmm1, 1); - *llr_64++ = simde_mm256_extract_epi64(xmm2, 0); - *llr_64++ = simde_mm256_extract_epi64(xmm2, 1); - *llr_64++ = simde_mm256_extract_epi64(xmm1, 2); - *llr_64++ = simde_mm256_extract_epi64(xmm1, 3); - *llr_64++ = simde_mm256_extract_epi64(xmm2, 2); - *llr_64++ = simde_mm256_extract_epi64(xmm2, 3); - rxF_256++; - ch_mag++; - } - - nb_re &= 0x7; -#endif - - simde__m128i *rxF_128 = (simde__m128i *)rxF_256; - simde__m128i *ch_mag_128 = (simde__m128i *)ch_mag; - simde__m128i *ulsch_llr_128 = (simde__m128i *)llr_64; - - // Each iteration does 4 RE (gives 16 16bit-llrs) - for (int i = 0; i < (nb_re >> 2); i++) { - // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| - simde__m128i xmm0 = simde_mm_abs_epi16(*rxF_128); - // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 - xmm0 = simde_mm_subs_epi16(*ch_mag_128, xmm0); - - ulsch_llr_128[0] = simde_mm_unpacklo_epi32(*rxF_128, xmm0); // llr128[0] contains the llrs of the 1st,2nd,5th and 6th REs - ulsch_llr_128[1] = simde_mm_unpackhi_epi32(*rxF_128, xmm0); // llr128[1] contains the llrs of the 3rd, 4th, 7th and 8th REs - ulsch_llr_128 += 2; - rxF_128++; - ch_mag_128++; - } - - simde_mm_empty(); - - nb_re &= 0x3; - int16_t *rxDataF_i16 = (int16_t *)rxF_128; - int16_t *ul_ch_mag_i16 = (int16_t *)ch_mag_128; - int16_t *ulsch_llr_i16 = (int16_t *)ulsch_llr_128; - for (uint i = 0U; i < nb_re; i++) { - int16_t real = rxDataF_i16[2 * i]; - int16_t imag = rxDataF_i16[2 * i + 1]; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - ulsch_llr_i16[4 * i] = real; - ulsch_llr_i16[4 * i + 1] = imag; - ulsch_llr_i16[4 * i + 2] = saturating_sub(mag_real, abs(real)); - ulsch_llr_i16[4 * i + 3] = saturating_sub(mag_imag, abs(imag)); - } -} - -//---------------------------------------------------------------------------------------------- -// 64-QAM -//---------------------------------------------------------------------------------------------- - -void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol) -{ - simde__m256i *rxF = (simde__m256i *)rxdataF_comp; - - simde__m256i *ch_maga = (simde__m256i *)ul_ch_mag; - simde__m256i *ch_magb = (simde__m256i *)ul_ch_magb; - - int32_t *llr_32 = (int32_t *)ulsch_llr; - -#ifndef USE_128BIT - simde__m256i xmm0, xmm1, xmm2; - for (int i = 0; i < (nb_re >> 3); i++) { - xmm0 = *rxF; - // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| - xmm1 = simde_mm256_abs_epi16(xmm0); - // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 - xmm1 = simde_mm256_subs_epi16(*ch_maga, xmm1); - xmm2 = simde_mm256_abs_epi16(xmm1); - xmm2 = simde_mm256_subs_epi16(*ch_magb, xmm2); - // xmm0 |1st 4th 7th 10th 13th 16th 19th 22ed| - // xmm1 |2ed 5th 8th 11th 14th 17th 20th 23rd| - // xmm2 |3rd 6th 9th 12th 15th 18th 21st 24th| - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 0); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 0); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 0); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 1); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 1); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 1); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 2); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 2); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 2); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 3); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 3); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 3); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 4); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 4); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 4); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 5); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 5); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 5); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 6); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 6); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 6); - - *llr_32++ = simde_mm256_extract_epi32(xmm0, 7); - *llr_32++ = simde_mm256_extract_epi32(xmm1, 7); - *llr_32++ = simde_mm256_extract_epi32(xmm2, 7); - rxF++; - ch_maga++; - ch_magb++; - } - - nb_re &= 0x7; -#endif - - simde__m128i *rxF_128 = (simde__m128i *)rxF; - simde__m128i *ch_mag_128 = (simde__m128i *)ch_maga; - simde__m128i *ch_magb_128 = (simde__m128i *)ch_magb; - - simde__m64 *llr64 = (simde__m64 *)llr_32; - - // Each iteration does 4 RE (gives 24 16bit-llrs) - for (int i = 0; i < (nb_re >> 2); i++) { - simde__m128i xmm0, xmm1, xmm2; - xmm0 = *rxF_128; - xmm1 = simde_mm_abs_epi16(xmm0); - xmm1 = simde_mm_subs_epi16(*ch_mag_128, xmm1); - xmm2 = simde_mm_abs_epi16(xmm1); - xmm2 = simde_mm_subs_epi16(*ch_magb_128, xmm2); - - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm1, 0), simde_mm_extract_epi32(xmm0, 0)); - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm0, 1), simde_mm_extract_epi32(xmm2, 0)); - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm2, 1), simde_mm_extract_epi32(xmm1, 1)); - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm1, 2), simde_mm_extract_epi32(xmm0, 2)); - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm0, 3), simde_mm_extract_epi32(xmm2, 2)); - *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm2, 3), simde_mm_extract_epi32(xmm1, 3)); - rxF_128++; - ch_mag_128++; - ch_magb_128++; - } - - nb_re &= 0x3; - - int16_t *rxDataF_i16 = (int16_t *)rxF_128; - int16_t *ul_ch_mag_i16 = (int16_t *)ch_mag_128; - int16_t *ul_ch_magb_i16 = (int16_t *)ch_magb_128; - int16_t *llr_i16 = (int16_t *)llr64; - for (int i = 0; i < nb_re; i++) { - int16_t real = rxDataF_i16[2 * i]; - int16_t imag = rxDataF_i16[2 * i + 1]; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - llr_i16[6 * i] = real; - llr_i16[6 * i + 1] = imag; - llr_i16[6 * i + 2] = saturating_sub(mag_real, abs(real)); - llr_i16[6 * i + 3] = saturating_sub(mag_imag, abs(imag)); - int16_t mag_realb = ul_ch_magb_i16[2 * i]; - int16_t mag_imagb = ul_ch_magb_i16[2 * i + 1]; - llr_i16[6 * i + 4] = saturating_sub(mag_realb, abs(llr_i16[6 * i + 2])); - llr_i16[6 * i + 5] = saturating_sub(mag_imagb, abs(llr_i16[6 * i + 3])); - } - simde_mm_empty(); -} - -void nr_ulsch_256qam_llr(int32_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int32_t *ul_ch_magc, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol) -{ - simde__m256i *rxF_256 = (simde__m256i *)rxdataF_comp; - simde__m256i *llr256 = (simde__m256i *)ulsch_llr; - - simde__m256i *ch_maga = (simde__m256i *)ul_ch_mag; - simde__m256i *ch_magb = (simde__m256i *)ul_ch_magb; - simde__m256i *ch_magc = (simde__m256i *)ul_ch_magc; -#ifndef USE_128BIT - simde__m256i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; - - for (int i = 0; i < (nb_re >> 3); i++) { - // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| - xmm0 = simde_mm256_abs_epi16(*rxF_256); - // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 - xmm0 = simde_mm256_subs_epi16(*ch_maga, xmm0); - // xmmtmpD2 contains 16 LLRs - xmm1 = simde_mm256_abs_epi16(xmm0); - xmm1 = simde_mm256_subs_epi16(*ch_magb, xmm1); // contains 16 LLRs - xmm2 = simde_mm256_abs_epi16(xmm1); - xmm2 = simde_mm256_subs_epi16(*ch_magc, xmm2); // contains 16 LLRs - // rxF[i] A0 A1 A2 A3 A4 A5 A6 A7 bits 7,6 - // xmm0 B0 B1 B2 B3 B4 B5 B6 B7 bits 5,4 - // xmm1 C0 C1 C2 C3 C4 C5 C6 C7 bits 3,2 - // xmm2 D0 D1 D2 D3 D4 D5 D6 D7 bits 1,0 - xmm3 = simde_mm256_unpacklo_epi32(*rxF_256, xmm0); // A0 B0 A1 B1 A4 B4 A5 B5 - xmm4 = simde_mm256_unpackhi_epi32(*rxF_256, xmm0); // A2 B2 A3 B3 A6 B6 A7 B7 - xmm5 = simde_mm256_unpacklo_epi32(xmm1, xmm2); // C0 D0 C1 D1 C4 D4 C5 D5 - xmm6 = simde_mm256_unpackhi_epi32(xmm1, xmm2); // C2 D2 C3 D3 C6 D6 C7 D7 - - xmm0 = simde_mm256_unpacklo_epi64(xmm3, xmm5); // A0 B0 C0 D0 A4 B4 C4 D4 - xmm1 = simde_mm256_unpackhi_epi64(xmm3, xmm5); // A1 B1 C1 D1 A5 B5 C5 D5 - xmm2 = simde_mm256_unpacklo_epi64(xmm4, xmm6); // A2 B2 C2 D2 A6 B6 C6 D6 - xmm3 = simde_mm256_unpackhi_epi64(xmm4, xmm6); // A3 B3 C3 D3 A7 B7 C7 D7 - *llr256++ = simde_mm256_permute2x128_si256(xmm0, xmm1, 0x20); // A0 B0 C0 D0 A1 B1 C1 D1 - *llr256++ = simde_mm256_permute2x128_si256(xmm2, xmm3, 0x20); // A2 B2 C2 D2 A3 B3 C3 D3 - *llr256++ = simde_mm256_permute2x128_si256(xmm0, xmm1, 0x31); // A4 B4 C4 D4 A5 B5 C5 D5 - *llr256++ = simde_mm256_permute2x128_si256(xmm2, xmm3, 0x31); // A6 B6 C6 D6 A7 B7 C7 D7 - ch_magc++; - ch_magb++; - ch_maga++; - rxF_256++; - } - - nb_re &= 0x7; -#endif - - simde__m128i *rxF_128 = (simde__m128i *)rxF_256; - simde__m128i *llr_128 = (simde__m128i *)llr256; - - simde__m128i *ch_maga_128 = (simde__m128i *)ch_maga; - simde__m128i *ch_magb_128 = (simde__m128i *)ch_magb; - simde__m128i *ch_magc_128 = (simde__m128i *)ch_magc; - - for (int i = 0; i < (nb_re >> 2); i++) { - simde__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; - // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| - xmm0 = simde_mm_abs_epi16(*rxF_128); - // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 - xmm0 = simde_mm_subs_epi16(*ch_maga_128, xmm0); - xmm1 = simde_mm_abs_epi16(xmm0); - xmm1 = simde_mm_subs_epi16(*ch_magb_128, xmm1); // contains 8 LLRs - xmm2 = simde_mm_abs_epi16(xmm1); - xmm2 = simde_mm_subs_epi16(*ch_magc_128, xmm2); // contains 8 LLRs - // rxF[i] A0 A1 A2 A3 - // xmm0 B0 B1 B2 B3 - // xmm1 C0 C1 C2 C3 - // xmm2 D0 D1 D2 D3 - xmm3 = simde_mm_unpacklo_epi32(*rxF_128, xmm0); // A0 B0 A1 B1 - xmm4 = simde_mm_unpackhi_epi32(*rxF_128, xmm0); // A2 B2 A3 B3 - xmm5 = simde_mm_unpacklo_epi32(xmm1, xmm2); // C0 D0 C1 D1 - xmm6 = simde_mm_unpackhi_epi32(xmm1, xmm2); // C2 D2 C3 D3 - - *llr_128++ = simde_mm_unpacklo_epi64(xmm3, xmm5); // A0 B0 C0 D0 - *llr_128++ = simde_mm_unpackhi_epi64(xmm3, xmm5); // A1 B1 C1 D1 - *llr_128++ = simde_mm_unpacklo_epi64(xmm4, xmm6); // A2 B2 C2 D2 - *llr_128++ = simde_mm_unpackhi_epi64(xmm4, xmm6); // A3 B3 C3 D3 - - rxF_128++; - ch_maga_128++; - ch_magb_128++; - ch_magc_128++; - } - - if (nb_re & 3) { - for (int i = 0; i < (nb_re & 0x3); i++) { - int16_t *rxDataF_i16 = (int16_t *)rxF_128; - int16_t *ul_ch_mag_i16 = (int16_t *)ch_maga_128; - int16_t *ul_ch_magb_i16 = (int16_t *)ch_magb_128; - int16_t *ul_ch_magc_i16 = (int16_t *)ch_magc_128; - int16_t *ulsch_llr_i16 = (int16_t *)llr_128; - int16_t real = rxDataF_i16[2 * i + 0]; - int16_t imag = rxDataF_i16[2 * i + 1]; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - ulsch_llr_i16[8 * i] = real; - ulsch_llr_i16[8 * i + 1] = imag; - ulsch_llr_i16[8 * i + 2] = saturating_sub(mag_real, abs(real)); - ulsch_llr_i16[8 * i + 3] = saturating_sub(mag_imag, abs(imag)); - int16_t magb_real = ul_ch_magb_i16[2 * i]; - int16_t magb_imag = ul_ch_magb_i16[2 * i + 1]; - ulsch_llr_i16[8 * i + 4] = saturating_sub(magb_real, abs(ulsch_llr_i16[8 * i + 2])); - ulsch_llr_i16[8 * i + 5] = saturating_sub(magb_imag, abs(ulsch_llr_i16[8 * i + 3])); - int16_t magc_real = ul_ch_magc_i16[2 * i]; - int16_t magc_imag = ul_ch_magc_i16[2 * i + 1]; - ulsch_llr_i16[8 * i + 6] = saturating_sub(magc_real, abs(ulsch_llr_i16[8 * i + 4])); - ulsch_llr_i16[8 * i + 7] = saturating_sub(magc_imag, abs(ulsch_llr_i16[8 * i + 5])); - } - } - simde_mm_empty(); -} - void nr_ulsch_compute_llr(int32_t *rxdataF_comp, int32_t *ul_ch_mag, int32_t *ul_ch_magb, @@ -391,39 +47,21 @@ void nr_ulsch_compute_llr(int32_t *rxdataF_comp, uint8_t symbol, uint8_t mod_order) { - switch(mod_order){ + switch(mod_order) { case 2: - nr_ulsch_qpsk_llr(rxdataF_comp, - ulsch_llr, - nb_re, - symbol); + nr_qpsk_llr(rxdataF_comp, ulsch_llr, nb_re); break; case 4: - nr_ulsch_16qam_llr(rxdataF_comp, - ul_ch_mag, - ulsch_llr, - nb_re, - symbol); + nr_16qam_llr(rxdataF_comp, ul_ch_mag, ulsch_llr, nb_re); break; case 6: - nr_ulsch_64qam_llr(rxdataF_comp, - ul_ch_mag, - ul_ch_magb, - ulsch_llr, - nb_re, - symbol); + nr_64qam_llr(rxdataF_comp, ul_ch_mag, ul_ch_magb, ulsch_llr, nb_re); break; case 8: - nr_ulsch_256qam_llr(rxdataF_comp, - ul_ch_mag, - ul_ch_magb, - ul_ch_magc, - ulsch_llr, - nb_re, - symbol); + nr_256qam_llr(rxdataF_comp, ul_ch_mag, ul_ch_magb, ul_ch_magc, ulsch_llr, nb_re); break; default: - AssertFatal(1==0,"nr_ulsch_compute_llr: invalid Qm value, symbol = %d, Qm = %d\n",symbol, mod_order); + AssertFatal(false, "nr_ulsch_compute_llr: invalid Qm value, symbol = %d, Qm = %d\n",symbol, mod_order); break; } } diff --git a/openair1/PHY/NR_TRANSPORT/tests/test_llr.cpp b/openair1/PHY/NR_TRANSPORT/tests/test_llr.cpp index 5778bd6c39..bd0c95d2ae 100644 --- a/openair1/PHY/NR_TRANSPORT/tests/test_llr.cpp +++ b/openair1/PHY/NR_TRANSPORT/tests/test_llr.cpp @@ -25,20 +25,9 @@ #include #include extern "C" { -void nr_ulsch_16qam_llr(int32_t *rxdataF_comp, int32_t *ul_ch_mag, int16_t *ulsch_llr, uint32_t nb_re, uint8_t symbol); -void nr_ulsch_64qam_llr(int32_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol); -void nr_ulsch_256qam_llr(int32_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int32_t *ul_ch_magc, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol); +void nr_16qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag_in, int16_t *llr, uint32_t nb_re); +void nr_64qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int16_t *llr, uint32_t nb_re); +void nr_256qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int32_t *ch_mag3, int16_t *llr, uint32_t nb_re); struct configmodule_interface_s; struct configmodule_interface_s *uniqCfg = NULL; @@ -72,74 +61,72 @@ int16_t saturating_sub(int16_t a, int16_t b) } } -void nr_ulsch_16qam_llr_ref(c16_t *rxdataF_comp, int32_t *ul_ch_mag, int16_t *ulsch_llr, uint32_t nb_re, uint8_t symbol) +void nr_16qam_llr_ref(c16_t *rxdataF_comp, int32_t *ch_mag, int16_t *llr, uint32_t nb_re) { - int16_t *ul_ch_mag_i16 = (int16_t *)ul_ch_mag; + int16_t *ch_mag_i16 = (int16_t *)ch_mag; for (auto i = 0U; i < nb_re; i++) { int16_t real = rxdataF_comp[i].r; int16_t imag = rxdataF_comp[i].i; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - ulsch_llr[4 * i] = real; - ulsch_llr[4 * i + 1] = imag; - ulsch_llr[4 * i + 2] = saturating_sub(mag_real, std::abs(real)); - ulsch_llr[4 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr[4 * i] = real; + llr[4 * i + 1] = imag; + llr[4 * i + 2] = saturating_sub(mag_real, std::abs(real)); + llr[4 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); } } -void nr_ulsch_64qam_llr_ref(c16_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol) +void nr_64qam_llr_ref(c16_t *rxdataF_comp, + int32_t *ch_mag, + int32_t *ch_magb, + int16_t *llr, + uint32_t nb_re) { - int16_t *ul_ch_mag_i16 = (int16_t *)ul_ch_mag; - int16_t *ul_ch_magb_i16 = (int16_t *)ul_ch_magb; + int16_t *ch_mag_i16 = (int16_t *)ch_mag; + int16_t *ch_magb_i16 = (int16_t *)ch_magb; for (auto i = 0U; i < nb_re; i++) { int16_t real = rxdataF_comp[i].r; int16_t imag = rxdataF_comp[i].i; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - ulsch_llr[6 * i] = real; - ulsch_llr[6 * i + 1] = imag; - ulsch_llr[6 * i + 2] = saturating_sub(mag_real, std::abs(real)); - ulsch_llr[6 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); - int16_t mag_realb = ul_ch_magb_i16[2 * i]; - int16_t mag_imagb = ul_ch_magb_i16[2 * i + 1]; - ulsch_llr[6 * i + 4] = saturating_sub(mag_realb, std::abs(ulsch_llr[6 * i + 2])); - ulsch_llr[6 * i + 5] = saturating_sub(mag_imagb, std::abs(ulsch_llr[6 * i + 3])); + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr[6 * i] = real; + llr[6 * i + 1] = imag; + llr[6 * i + 2] = saturating_sub(mag_real, std::abs(real)); + llr[6 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); + int16_t mag_realb = ch_magb_i16[2 * i]; + int16_t mag_imagb = ch_magb_i16[2 * i + 1]; + llr[6 * i + 4] = saturating_sub(mag_realb, std::abs(llr[6 * i + 2])); + llr[6 * i + 5] = saturating_sub(mag_imagb, std::abs(llr[6 * i + 3])); } } -void nr_ulsch_256qam_llr_ref(c16_t *rxdataF_comp, - int32_t *ul_ch_mag, - int32_t *ul_ch_magb, - int32_t *ul_ch_magc, - int16_t *ulsch_llr, - uint32_t nb_re, - uint8_t symbol) +void nr_256qam_llr_ref(c16_t *rxdataF_comp, + int32_t *ch_mag, + int32_t *ch_magb, + int32_t *ch_magc, + int16_t *llr, + uint32_t nb_re) { - int16_t *ul_ch_mag_i16 = (int16_t *)ul_ch_mag; - int16_t *ul_ch_magb_i16 = (int16_t *)ul_ch_magb; - int16_t *ul_ch_magc_i16 = (int16_t *)ul_ch_magc; + int16_t *ch_mag_i16 = (int16_t *)ch_mag; + int16_t *ch_magb_i16 = (int16_t *)ch_magb; + int16_t *ch_magc_i16 = (int16_t *)ch_magc; for (auto i = 0U; i < nb_re; i++) { int16_t real = rxdataF_comp[i].r; int16_t imag = rxdataF_comp[i].i; - int16_t mag_real = ul_ch_mag_i16[2 * i]; - int16_t mag_imag = ul_ch_mag_i16[2 * i + 1]; - ulsch_llr[8 * i] = real; - ulsch_llr[8 * i + 1] = imag; - ulsch_llr[8 * i + 2] = saturating_sub(mag_real, std::abs(real)); - ulsch_llr[8 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); - int16_t magb_real = ul_ch_magb_i16[2 * i]; - int16_t magb_imag = ul_ch_magb_i16[2 * i + 1]; - ulsch_llr[8 * i + 4] = saturating_sub(magb_real, std::abs(ulsch_llr[8 * i + 2])); - ulsch_llr[8 * i + 5] = saturating_sub(magb_imag, std::abs(ulsch_llr[8 * i + 3])); - int16_t magc_real = ul_ch_magc_i16[2 * i]; - int16_t magc_imag = ul_ch_magc_i16[2 * i + 1]; - ulsch_llr[8 * i + 6] = saturating_sub(magc_real, std::abs(ulsch_llr[8 * i + 4])); - ulsch_llr[8 * i + 7] = saturating_sub(magc_imag, std::abs(ulsch_llr[8 * i + 5])); + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr[8 * i] = real; + llr[8 * i + 1] = imag; + llr[8 * i + 2] = saturating_sub(mag_real, std::abs(real)); + llr[8 * i + 3] = saturating_sub(mag_imag, std::abs(imag)); + int16_t magb_real = ch_magb_i16[2 * i]; + int16_t magb_imag = ch_magb_i16[2 * i + 1]; + llr[8 * i + 4] = saturating_sub(magb_real, std::abs(llr[8 * i + 2])); + llr[8 * i + 5] = saturating_sub(magb_imag, std::abs(llr[8 * i + 3])); + int16_t magc_real = ch_magc_i16[2 * i]; + int16_t magc_imag = ch_magc_i16[2 * i + 1]; + llr[8 * i + 6] = saturating_sub(magc_real, std::abs(llr[8 * i + 4])); + llr[8 * i + 7] = saturating_sub(magc_imag, std::abs(llr[8 * i + 5])); } } @@ -149,21 +136,21 @@ void test_function_16_qam(AlignedVector512 nb_res) uint32_t nb_re = nb_res[i]; auto rf_data = generate_random_c16(nb_re); auto magnitude_data = generate_random_uint16(nb_re * 2); - AlignedVector512 ulsch_llr_ref; - ulsch_llr_ref.resize(nb_re); - std::fill(ulsch_llr_ref.begin(), ulsch_llr_ref.end(), 0); - nr_ulsch_16qam_llr_ref((c16_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int16_t *)ulsch_llr_ref.data(), nb_re, 0); + AlignedVector512 llr_ref; + llr_ref.resize(nb_re); + std::fill(llr_ref.begin(), llr_ref.end(), 0); + nr_16qam_llr_ref((c16_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int16_t *)llr_ref.data(), nb_re); - AlignedVector512 ulsch_llr; - ulsch_llr.resize(nb_re); - std::fill(ulsch_llr.begin(), ulsch_llr.end(), 0); - nr_ulsch_16qam_llr((int32_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int16_t *)ulsch_llr.data(), nb_re, 0); + AlignedVector512 llr; + llr.resize(nb_re); + std::fill(llr.begin(), llr.end(), 0); + nr_16qam_llr((int32_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int16_t *)llr.data(), nb_re); int num_errors = 0; for (auto i = 0U; i < nb_re; i++) { - EXPECT_EQ(ulsch_llr_ref[i], ulsch_llr[i]) - << "Mismatch 16qam REF " << std::hex << ulsch_llr_ref[i] << " != DUT " << ulsch_llr[i] << " at " << std::dec << i; - if (ulsch_llr_ref[i] != ulsch_llr[i]) { + EXPECT_EQ(llr_ref[i], llr[i]) + << "Mismatch 16qam REF " << std::hex << llr_ref[i] << " != DUT " << llr[i] << " at " << std::dec << i; + if (llr_ref[i] != llr[i]) { num_errors++; } } @@ -178,31 +165,29 @@ void test_function_64_qam(AlignedVector512 nb_res) auto rf_data = generate_random_c16(nb_re); auto magnitude_data = generate_random_uint16(nb_re * 2); auto magnitude_b_data = generate_random_uint16(nb_re * 2); - AlignedVector512 ulsch_llr_ref; - ulsch_llr_ref.resize(nb_re * 3); - std::fill(ulsch_llr_ref.begin(), ulsch_llr_ref.end(), 0); - nr_ulsch_64qam_llr_ref((c16_t *)rf_data.data(), + AlignedVector512 llr_ref; + llr_ref.resize(nb_re * 3); + std::fill(llr_ref.begin(), llr_ref.end(), 0); + nr_64qam_llr_ref((c16_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), - (int16_t *)ulsch_llr_ref.data(), - nb_re, - 0); + (int16_t *)llr_ref.data(), + nb_re); - AlignedVector512 ulsch_llr; - ulsch_llr.resize(nb_re * 3); - std::fill(ulsch_llr.begin(), ulsch_llr.end(), 0); - nr_ulsch_64qam_llr((int32_t *)rf_data.data(), + AlignedVector512 llr; + llr.resize(nb_re * 3); + std::fill(llr.begin(), llr.end(), 0); + nr_64qam_llr((int32_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), - (int16_t *)ulsch_llr.data(), - nb_re, - 0); + (int16_t *)llr.data(), + nb_re); int num_errors = 0; for (auto i = 0U; i < nb_re * 3; i++) { - EXPECT_EQ(ulsch_llr_ref[i], ulsch_llr[i]) - << "Mismatch 64qam REF " << std::hex << ulsch_llr_ref[i] << " != DUT " << ulsch_llr[i] << " at " << std::dec << i; - if (ulsch_llr_ref[i] != ulsch_llr[i]) { + EXPECT_EQ(llr_ref[i], llr[i]) + << "Mismatch 64qam REF " << std::hex << llr_ref[i] << " != DUT " << llr[i] << " at " << std::dec << i; + if (llr_ref[i] != llr[i]) { num_errors++; } } @@ -218,33 +203,31 @@ void test_function_256_qam(AlignedVector512 nb_res) auto magnitude_data = generate_random_uint16(nb_re * 2); auto magnitude_b_data = generate_random_uint16(nb_re * 2); auto magnitude_c_data = generate_random_uint16(nb_re * 2); - AlignedVector512 ulsch_llr_ref; - ulsch_llr_ref.resize(nb_re * 4); - std::fill(ulsch_llr_ref.begin(), ulsch_llr_ref.end(), 0); - nr_ulsch_256qam_llr_ref((c16_t *)rf_data.data(), + AlignedVector512 llr_ref; + llr_ref.resize(nb_re * 4); + std::fill(llr_ref.begin(), llr_ref.end(), 0); + nr_256qam_llr_ref((c16_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), (int32_t *)magnitude_c_data.data(), - (int16_t *)ulsch_llr_ref.data(), - nb_re, - 0); + (int16_t *)llr_ref.data(), + nb_re); - AlignedVector512 ulsch_llr; - ulsch_llr.resize(nb_re * 4); - std::fill(ulsch_llr.begin(), ulsch_llr.end(), 0); - nr_ulsch_256qam_llr((int32_t *)rf_data.data(), + AlignedVector512 llr; + llr.resize(nb_re * 4); + std::fill(llr.begin(), llr.end(), 0); + nr_256qam_llr((int32_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), (int32_t *)magnitude_c_data.data(), - (int16_t *)ulsch_llr.data(), - nb_re, - 0); + (int16_t *)llr.data(), + nb_re); int num_errors = 0; for (auto i = 0U; i < nb_re * 4; i++) { - EXPECT_EQ(ulsch_llr_ref[i], ulsch_llr[i]) - << "Mismatch 256qam REF " << std::hex << ulsch_llr_ref[i] << " != DUT " << ulsch_llr[i] << " at " << std::dec << i; - if (ulsch_llr_ref[i] != ulsch_llr[i]) { + EXPECT_EQ(llr_ref[i], llr[i]) + << "Mismatch 256qam REF " << std::hex << llr_ref[i] << " != DUT " << llr[i] << " at " << std::dec << i; + if (llr_ref[i] != llr[i]) { num_errors++; } } @@ -370,59 +353,57 @@ TEST(test_llr, check_2_res_256_qam) AlignedVector512 magnitude_data = {1, 1, 1, 1}; AlignedVector512 magnitude_b_data = {2, 2, 2, 2}; AlignedVector512 magnitude_c_data = {3, 3, 3, 3}; - AlignedVector512 ulsch_llr_ref; - ulsch_llr_ref.resize(2 * 8); - std::fill(ulsch_llr_ref.begin(), ulsch_llr_ref.end(), 0); - nr_ulsch_256qam_llr_ref((c16_t *)rf_data.data(), + AlignedVector512 llr_ref; + llr_ref.resize(2 * 8); + std::fill(llr_ref.begin(), llr_ref.end(), 0); + nr_256qam_llr_ref((c16_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), (int32_t *)magnitude_c_data.data(), - (int16_t *)ulsch_llr_ref.data(), - 2, - 0); + (int16_t *)llr_ref.data(), + 2); - AlignedVector512 ulsch_llr; - ulsch_llr.resize(2 * 8); - std::fill(ulsch_llr.begin(), ulsch_llr.end(), 0); - nr_ulsch_256qam_llr((int32_t *)rf_data.data(), + AlignedVector512 llr; + llr.resize(2 * 8); + std::fill(llr.begin(), llr.end(), 0); + nr_256qam_llr((int32_t *)rf_data.data(), (int32_t *)magnitude_data.data(), (int32_t *)magnitude_b_data.data(), (int32_t *)magnitude_c_data.data(), - (int16_t *)ulsch_llr.data(), - 2, - 0); + (int16_t *)llr.data(), + 2); printf("\nDUT:\n"); for (auto i = 0U; i < 2; i++) { printf("%d %d %d %d %d %d %d %d\n", - ulsch_llr[i * 8], - ulsch_llr[i * 8 + 1], - ulsch_llr[i * 8 + 2], - ulsch_llr[i * 8 + 3], - ulsch_llr[i * 8 + 4], - ulsch_llr[i * 8 + 5], - ulsch_llr[i * 8 + 6], - ulsch_llr[i * 8 + 7]); + llr[i * 8], + llr[i * 8 + 1], + llr[i * 8 + 2], + llr[i * 8 + 3], + llr[i * 8 + 4], + llr[i * 8 + 5], + llr[i * 8 + 6], + llr[i * 8 + 7]); } printf("\nREF:\n"); for (auto i = 0U; i < 2; i++) { printf("%d %d %d %d %d %d %d %d\n", - ulsch_llr_ref[i * 8], - ulsch_llr_ref[i * 8 + 1], - ulsch_llr_ref[i * 8 + 2], - ulsch_llr_ref[i * 8 + 3], - ulsch_llr_ref[i * 8 + 4], - ulsch_llr_ref[i * 8 + 5], - ulsch_llr_ref[i * 8 + 6], - ulsch_llr_ref[i * 8 + 7]); + llr_ref[i * 8], + llr_ref[i * 8 + 1], + llr_ref[i * 8 + 2], + llr_ref[i * 8 + 3], + llr_ref[i * 8 + 4], + llr_ref[i * 8 + 5], + llr_ref[i * 8 + 6], + llr_ref[i * 8 + 7]); } int num_errors = 0; for (auto i = 0U; i < 2 * 8; i++) { - EXPECT_EQ(ulsch_llr_ref[i], ulsch_llr[i]) - << "Mismatch 256qam REF " << std::hex << ulsch_llr_ref[i] << " != DUT " << ulsch_llr[i] << " at " << std::dec << i; - if (ulsch_llr_ref[i] != ulsch_llr[i]) { + EXPECT_EQ(llr_ref[i], llr[i]) + << "Mismatch 256qam REF " << std::hex << llr_ref[i] << " != DUT " << llr[i] << " at " << std::dec << i; + if (llr_ref[i] != llr[i]) { num_errors++; } } diff --git a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c index d10f5b5b42..53c302e789 100644 --- a/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c +++ b/openair1/PHY/NR_UE_ESTIMATION/nr_dl_channel_estimation.c @@ -1633,7 +1633,7 @@ void nr_pdsch_ptrs_processing(PHY_VARS_NR_UE *ue, nr_slot_rx, symbol, frame_parms->ofdm_symbol_size, - (int16_t *)(rxdataF_comp[0][aarx] + symbol * nb_re_pdsch), + (int16_t *)(rxdataF_comp[0][aarx] + symbol * rx_size_symbol), gold, (int16_t *)&phase_per_symbol[symbol], &ptrs_re_symbol[symbol]); @@ -1654,21 +1654,21 @@ void nr_pdsch_ptrs_processing(PHY_VARS_NR_UE *ue, } #ifdef DEBUG_DL_PTRS LOG_M("ptrsEst.m","est",ptrs_phase_per_slot[aarx],frame_parms->symbols_per_slot,1,1 ); - LOG_M("rxdataF_bf_ptrs_comp.m", "bf_ptrs_cmp", rxdataF_comp[0][aarx] + (*startSymbIndex) * NR_NB_SC_PER_RB * (*nb_rb), (*nb_rb) * NR_NB_SC_PER_RB * (*nbSymb), 1, 1); + LOG_M("rxdataF_bf_ptrs_comp.m", "bf_ptrs_cmp", rxdataF_comp[0][aarx] + (*startSymbIndex) * rx_size_symbol, rx_size_symbol * (*nbSymb), 1, 1); #endif /*------------------------------------------------------------------------------------------------------- */ /* 3) Compensated DMRS based estimated signal with PTRS estimation */ /*--------------------------------------------------------------------------------------------------------*/ - for(uint8_t i = *startSymbIndex; i< symbInSlot ;i++) { + for(uint8_t i = *startSymbIndex; i < symbInSlot; i++) { /* DMRS Symbol has 0 phase so no need to rotate the respective symbol */ /* Skip rotation if the slot processing is wrong */ - if((!is_dmrs_symbol(i,*dmrsSymbPos)) && (ret == 0)) { + if((!is_dmrs_symbol(i, *dmrsSymbPos)) && (ret == 0)) { #ifdef DEBUG_DL_PTRS printf("[PHY][DL][PTRS]: Rotate Symbol %2d with %d + j* %d\n", i, phase_per_symbol[i].r,phase_per_symbol[i].i); #endif - rotate_cpx_vector((c16_t *)&rxdataF_comp[0][aarx][(i * (*nb_rb) * NR_NB_SC_PER_RB)], + rotate_cpx_vector((c16_t *)&rxdataF_comp[0][aarx][i * rx_size_symbol], &phase_per_symbol[i], - (c16_t *)&rxdataF_comp[0][aarx][(i * (*nb_rb) * NR_NB_SC_PER_RB)], + (c16_t *)&rxdataF_comp[0][aarx][i * rx_size_symbol], ((*nb_rb) * NR_NB_SC_PER_RB), 15); }// if not DMRS Symbol diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index 6537e4b840..ee83cccad0 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -28,6 +28,7 @@ * \note * \warning */ +#include "nr_phy_common.h" #include "PHY/defs_nr_UE.h" #include "PHY/phy_extern.h" #include "nr_transport_proto_ue.h" @@ -101,27 +102,20 @@ static void nr_dlsch_layer_demapping(int16_t *llr_cw[2], int16_t llr_layers[][sz]); /* compute LLR */ -static int nr_dlsch_llr(uint32_t rx_size_symbol, - int nbRx, - uint sz, - int16_t layer_llr[][sz], - NR_DL_FRAME_PARMS *frame_parms, - int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], - int32_t dl_ch_mag[rx_size_symbol], - int32_t dl_ch_magb[rx_size_symbol], - int32_t dl_ch_magr[rx_size_symbol], - NR_DL_UE_HARQ_t *dlsch0_harq, - NR_DL_UE_HARQ_t *dlsch1_harq, - unsigned char harq_pid, - unsigned char first_symbol_flag, - unsigned char symbol, - unsigned short nb_rb, - int32_t codeword_TB0, - int32_t codeword_TB1, - uint32_t len, - uint8_t nr_slot_rx, - NR_UE_DLSCH_t dlsch[2], - uint32_t llr_offset[NR_SYMBOLS_PER_SLOT]); +static void nr_dlsch_llr(uint32_t rx_size_symbol, + int nbRx, + uint sz, + int16_t layer_llr[][sz], + int32_t rxdataF_comp[][nbRx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], + int32_t dl_ch_mag[rx_size_symbol], + int32_t dl_ch_magb[rx_size_symbol], + int32_t dl_ch_magr[rx_size_symbol], + NR_DL_UE_HARQ_t *dlsch0_harq, + NR_DL_UE_HARQ_t *dlsch1_harq, + unsigned char symbol, + uint32_t len, + NR_UE_DLSCH_t dlsch[2], + uint32_t llr_offset_symbol); /** \fn nr_dlsch_extract_rbs \brief This function extracts the received resource blocks, both channel estimates and data symbols, for the current @@ -220,17 +214,16 @@ void nr_dlsch_scale_channel(uint32_t rx_size_symbol, uint8_t pilots, uint32_t len, unsigned short nb_rb); -void nr_dlsch_detection_mrc(uint32_t rx_size_symbol, - short n_tx, - short n_rx, - int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], - int ***rho, - int32_t dl_ch_mag[][n_rx][rx_size_symbol], - int32_t dl_ch_magb[][n_rx][rx_size_symbol], - int32_t dl_ch_magr[][n_rx][rx_size_symbol], - unsigned char symbol, - unsigned short nb_rb, - int length); +static void nr_dlsch_detection_mrc(uint32_t rx_size_symbol, + short n_tx, + short n_rx, + int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], + int ***rho, + int32_t dl_ch_mag[][n_rx][rx_size_symbol], + int32_t dl_ch_magb[][n_rx][rx_size_symbol], + int32_t dl_ch_magr[][n_rx][rx_size_symbol], + unsigned char symbol, + int length); static bool overlap_csi_symbol(fapi_nr_dl_config_csirs_pdu_rel15_t *csi_pdu, int symbol) { @@ -595,7 +588,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, dl_ch_magb, dl_ch_magr, symbol, - nb_rb_pdsch, nb_re_pdsch); if (nl >= 2) // Apply MMSE for 2, 3, and 4 Tx layers nr_dlsch_mmse(rx_size_symbol, @@ -628,7 +620,7 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, /* Store the valid DL RE's */ - dl_valid_re[symbol-1] = nb_re_pdsch; + dl_valid_re[symbol] = nb_re_pdsch; int startSymbIdx = 0; int nbSymb = 0; int pduBitmap = 0; @@ -656,10 +648,10 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, (nb_rb_pdsch * 12), dlsch[0].rnti, dlsch); - dl_valid_re[symbol - 1] -= ptrs_re_per_slot[0][symbol]; + dl_valid_re[symbol] -= ptrs_re_per_slot[0][symbol]; } /* at last symbol in a slot calculate LLR's for whole slot */ - if(symbol == (startSymbIdx + nbSymb -1)) { + if(symbol == (startSymbIdx + nbSymb - 1)) { const uint32_t rx_llr_layer_size = (G + dlsch[0].Nl - 1) / dlsch[0].Nl; if (dlsch[0].Nl == 0 || rx_llr_layer_size == 0 || rx_llr_layer_size > 10 * 1000 * 1000) { @@ -668,42 +660,32 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, } int16_t layer_llr[dlsch[0].Nl][rx_llr_layer_size]; - for(int i = startSymbIdx; i < startSymbIdx+nbSymb; i++) { - /* re evaluating the first symbol flag as LLR's are done in symbol loop */ - if(i == startSymbIdx && i < 3) - first_symbol_flag = 1; - else - first_symbol_flag = 0; + for(int i = startSymbIdx; i < startSymbIdx + nbSymb; i++) { /* Calculate LLR's for each symbol */ start_meas_nr_ue_phy(ue, DLSCH_LLR_STATS); nr_dlsch_llr(rx_size_symbol, nbRx, rx_llr_layer_size, layer_llr, - frame_parms, rxdataF_comp, dl_ch_mag[0][0], dl_ch_magb[0][0], dl_ch_magr[0][0], dlsch0_harq, dlsch1_harq, - harq_pid, - first_symbol_flag, i, - nb_rb_pdsch, - codeword_TB0, - codeword_TB1, - dl_valid_re[i - 1], - nr_slot_rx, + dl_valid_re[i], dlsch, - llr_offset); + llr_offset[i]); + if (i < startSymbIdx + nbSymb - 1) // up to the penultimate symbol + llr_offset[i + 1] = dl_valid_re[i] * dlsch_config->qamModOrder + llr_offset[i]; stop_meas_nr_ue_phy(ue, DLSCH_LLR_STATS); } start_meas_nr_ue_phy(ue, DLSCH_LAYER_DEMAPPING); nr_dlsch_layer_demapping(llr, dlsch[0].Nl, - dlsch[0].dlsch_config.qamModOrder, + dlsch_config->qamModOrder, G, codeword_TB0, codeword_TB1, @@ -736,6 +718,24 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, } */ #endif + if (UEScopeHasTryLock(ue)) { + metadata mt = {.frame = proc->frame_rx, .slot = proc->nr_slot_rx }; + int total_valid_res = 0; + for (int i = startSymbIdx; i < startSymbIdx + nbSymb; i++) { + total_valid_res = dl_valid_re[i - 1]; + } + if (UETryLockScopeData(ue, pdschRxdataF_comp, sizeof(c16_t), 1, total_valid_res, &mt)) { + size_t offset = 0; + for (int i = startSymbIdx; i < startSymbIdx + nbSymb; i++) { + size_t data_size = sizeof(c16_t) * dl_valid_re[i - i]; + UEscopeCopyUnsafe(ue, pdschRxdataF_comp, &rxdataF_comp[0][0][rx_size_symbol * i], data_size, offset, i - startSymbIdx); + offset += data_size; + } + UEunlockScopeData(ue, pdschRxdataF_comp) + } + } else { + UEscopeCopy(ue, pdschRxdataF_comp, rxdataF_comp[0], sizeof(c16_t), nbRx, rx_size_symbol * NR_SYMBOLS_PER_SLOT, 0); + } } if (meas_enabled) { @@ -759,7 +759,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, T_INT(frame_parms->symbols_per_slot), T_BUFFER(&rxdataF_comp[gNB_id][0], 2 * /* ulsch[UE_id]->harq_processes[harq_pid]->nb_rb */ frame_parms->N_RB_UL * 12 * 2)); #endif - UEscopeCopy(ue, pdschRxdataF_comp, rxdataF_comp[0], sizeof(c16_t), nbRx, rx_size_symbol * NR_SYMBOLS_PER_SLOT, 0); if (ue->phy_sim_pdsch_rxdataF_comp) for (int a = 0; a < nbRx; a++) { @@ -876,15 +875,13 @@ static void nr_dlsch_channel_compensation(uint32_t rx_size_symbol, QAM_amp128r = simde_mm_set1_epi16(QAM256_n3); } - // printf("comp: rxdataF_comp %p, symbol %d\n",rxdataF_comp[0],symbol); - for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(l * frame_parms->nb_antennas_rx) + aarx]; dl_ch_mag128 = (simde__m128i *)dl_ch_mag[l][aarx]; dl_ch_mag128b = (simde__m128i *)dl_ch_magb[l][aarx]; dl_ch_mag128r = (simde__m128i *)dl_ch_magr[l][aarx]; rxdataF128 = (simde__m128i *)rxdataF_ext[aarx]; - rxdataF_comp128 = (simde__m128i *)(rxdataF_comp[l][aarx] + symbol * nb_rb * 12); + rxdataF_comp128 = (simde__m128i *)(rxdataF_comp[l][aarx] + symbol * rx_size_symbol); for (int rb = 0; rb < nb_rb_0; rb++) { if (mod_order > 2) { @@ -1259,37 +1256,34 @@ static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz, } } -void nr_dlsch_detection_mrc(uint32_t rx_size_symbol, - short n_tx, - short n_rx, - int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], - int ***rho, - int32_t dl_ch_mag[][n_rx][rx_size_symbol], - int32_t dl_ch_magb[][n_rx][rx_size_symbol], - int32_t dl_ch_magr[][n_rx][rx_size_symbol], - unsigned char symbol, - unsigned short nb_rb, - int length) { - unsigned char aatx, aarx; - int i; +static void nr_dlsch_detection_mrc(uint32_t rx_size_symbol, + short n_tx, + short n_rx, + int32_t rxdataF_comp[][n_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT], + int ***rho, + int32_t dl_ch_mag[][n_rx][rx_size_symbol], + int32_t dl_ch_magb[][n_rx][rx_size_symbol], + int32_t dl_ch_magr[][n_rx][rx_size_symbol], + unsigned char symbol, + int length) +{ simde__m128i *rxdataF_comp128_0,*rxdataF_comp128_1,*dl_ch_mag128_0,*dl_ch_mag128_1,*dl_ch_mag128_0b,*dl_ch_mag128_1b,*dl_ch_mag128_0r,*dl_ch_mag128_1r; - uint32_t nb_rb_0 = length/12 + ((length%12)?1:0); - if (n_rx>1) { - for (aatx=0; aatx 1) { + for (int aatx = 0; aatx < n_tx; aatx++) { + rxdataF_comp128_0 = (simde__m128i *)(rxdataF_comp[aatx][0] + symbol * rx_size_symbol); dl_ch_mag128_0 = (simde__m128i *)dl_ch_mag[aatx][0]; dl_ch_mag128_0b = (simde__m128i *)dl_ch_magb[aatx][0]; dl_ch_mag128_0r = (simde__m128i *)dl_ch_magr[aatx][0]; - for (aarx=1; aarxr = rxF->r >> 3; - llr32->i = rxF->i >> 3; - LOG_D(PHY,"dlsch_qpsk_llr %d : (%d,%d)\n", i, llr32->r, llr32->i); - rxF++; - llr32++; - } - return(0); -} - - -//---------------------------------------------------------------------------------------------- -// 16-QAM -//---------------------------------------------------------------------------------------------- - -void nr_dlsch_16qam_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t *rxdataF_comp, - int16_t *dlsch_llr, - int32_t *dl_ch_mag, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb) -{ - - simde__m128i *rxF = (simde__m128i*)&rxdataF_comp[(symbol*nb_rb*12)]; - simde__m128i *ch_mag; - simde__m128i llr128[2]; - uint32_t *llr32; - - - int i; - unsigned char len_mod4=0; - - - llr32 = (uint32_t*)dlsch_llr; - ch_mag = (simde__m128i *)dl_ch_mag; - - - // printf("len=%d\n", len); - len_mod4 = len&3; - // printf("len_mod4=%d\n", len_mod4); - len>>=2; // length in quad words (4 REs) - // printf("len>>=2=%d\n", len); - len+=(len_mod4==0 ? 0 : 1); - // printf("len+=%d\n", len); - for (i=0; i>2; // length in quad words (4 REs) - len2+=((len_mod4==0)?0:1); - - for (i=0; i>2; // length in quad words (4 REs) - len2+=((len_mod4==0)?0:1); - - for (i=0; i>2; i+=2) { - // in each iteration, we take 8 complex samples - simde__m128i xmm0 = rho01_128i[i]; // 4 symbols - simde__m128i xmm1 = rho01_128i[i+1]; - - // put (rho_r + rho_i)/2sqrt2 in rho_rpi - // put (rho_r - rho_i)/2sqrt2 in rho_rmi - - xmm0 =simde_mm_shufflelo_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shufflehi_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shuffle_epi32(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflelo_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflehi_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shuffle_epi32(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - //xmm0 = [Re(0,1) Re(2,3) Im(0,1) Im(2,3)] - //xmm1 = [Re(4,5) Re(6,7) Im(4,5) Im(6,7)] - simde__m128i xmm2 = simde_mm_unpacklo_epi64(xmm0, xmm1); // Re(rho) - simde__m128i xmm3 = simde_mm_unpackhi_epi64(xmm0, xmm1); // Im(rho) - simde__m128i rho_rpi = simde_mm_adds_epi16(xmm2, xmm3); // rho = Re(rho) + Im(rho) - simde__m128i rho_rmi = simde_mm_subs_epi16(xmm2, xmm3); // rho* = Re(rho) - Im(rho) - - // divide by sqrt(8), no shift needed ONE_OVER_SQRT_8 = Q1.16 - rho_rpi =simde_mm_mulhi_epi16(rho_rpi,ONE_OVER_SQRT_8); - rho_rmi =simde_mm_mulhi_epi16(rho_rmi,ONE_OVER_SQRT_8); - // Compute LLR for first bit of stream 0 - - // Compute real and imaginary parts of MF output for stream 0 - xmm0 = stream0_128i_in[i]; - xmm1 = stream0_128i_in[i+1]; - - xmm0 =simde_mm_shufflelo_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shufflehi_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shuffle_epi32(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflelo_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflehi_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shuffle_epi32(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - //xmm0 = [Re(0,1) Re(2,3) Im(0,1) Im(2,3)] - //xmm1 = [Re(4,5) Re(6,7) Im(4,5) Im(6,7)] - simde__m128i y0r = simde_mm_unpacklo_epi64(xmm0, xmm1); // = [y0r(1),y0r(2),y0r(3),y0r(4)] - simde__m128i y0i = simde_mm_unpackhi_epi64(xmm0, xmm1); - - simde__m128i y0r_over2 = simde_mm_srai_epi16(y0r, 1); // divide by 2 - simde__m128i y0i_over2 = simde_mm_srai_epi16(y0i, 1); // divide by 2 - // Compute real and imaginary parts of MF output for stream 1 - xmm0 = stream1_128i_in[i]; - xmm1 = stream1_128i_in[i+1]; - - xmm0 =simde_mm_shufflelo_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shufflehi_epi16(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm0 =simde_mm_shuffle_epi32(xmm0,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflelo_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shufflehi_epi16(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - xmm1 =simde_mm_shuffle_epi32(xmm1,0xd8); // SIMDE_MM_SHUFFLE(0,2,1,3)); - //xmm0 = [Re(0,1) Re(2,3) Im(0,1) Im(2,3)] - //xmm1 = [Re(4,5) Re(6,7) Im(4,5) Im(6,7)] - simde__m128i y1r = simde_mm_unpacklo_epi64(xmm0, xmm1); //[y1r(1),y1r(2),y1r(3),y1r(4)] - simde__m128i y1i = simde_mm_unpackhi_epi64(xmm0, xmm1); //[y1i(1),y1i(2),y1i(3),y1i(4)] - - simde__m128i y1r_over2 = simde_mm_srai_epi16(y1r, 1); // divide by 2 - simde__m128i y1i_over2 = simde_mm_srai_epi16(y1i, 1); // divide by 2 - - // Compute the terms for the LLR of first bit - - xmm0 =simde_mm_setzero_si128(); // ZERO - - // 1 term for numerator of LLR - xmm3 =simde_mm_subs_epi16(y1r_over2,rho_rpi); - A =simde_mm_abs_epi16(xmm3); // A = |y1r/2 - rho/sqrt(8)| - xmm2 =simde_mm_adds_epi16(A,y0i_over2); // = |y1r/2 - rho/sqrt(8)| + y0i/2 - xmm3 =simde_mm_subs_epi16(y1i_over2,rho_rmi); - B =simde_mm_abs_epi16(xmm3); // B = |y1i/2 - rho*/sqrt(8)| - simde__m128i logmax_num_re0 =simde_mm_adds_epi16(B,xmm2); // = |y1r/2 - rho/sqrt(8)|+|y1i/2 - rho*/sqrt(8)| + y0i/2 - - // 2 term for numerator of LLR - xmm3 =simde_mm_subs_epi16(y1r_over2,rho_rmi); - C =simde_mm_abs_epi16(xmm3); // C = |y1r/2 - rho*/4| - xmm2 =simde_mm_subs_epi16(C,y0i_over2); // = |y1r/2 - rho*/4| - y0i/2 - xmm3 =simde_mm_adds_epi16(y1i_over2,rho_rpi); - D =simde_mm_abs_epi16(xmm3); // D = |y1i/2 + rho/4| - xmm2 =simde_mm_adds_epi16(xmm2,D); // |y1r/2 - rho*/4| + |y1i/2 + rho/4| - y0i/2 - logmax_num_re0 =simde_mm_max_epi16(logmax_num_re0,xmm2); // max, numerator done - - // 1 term for denominator of LLR - xmm3 =simde_mm_adds_epi16(y1r_over2,rho_rmi); - E =simde_mm_abs_epi16(xmm3); // E = |y1r/2 + rho*/4| - xmm2 =simde_mm_adds_epi16(E,y0i_over2); // = |y1r/2 + rho*/4| + y0i/2 - xmm3 =simde_mm_subs_epi16(y1i_over2,rho_rpi); - F =simde_mm_abs_epi16(xmm3); // F = |y1i/2 - rho/4| - simde__m128i logmax_den_re0 =simde_mm_adds_epi16(F,xmm2); // = |y1r/2 + rho*/4| + |y1i/2 - rho/4| + y0i/2 - - // 2 term for denominator of LLR - xmm3 =simde_mm_adds_epi16(y1r_over2,rho_rpi); - G =simde_mm_abs_epi16(xmm3); // G = |y1r/2 + rho/4| - xmm2 =simde_mm_subs_epi16(G,y0i_over2); // = |y1r/2 + rho/4| - y0i/2 - xmm3 =simde_mm_adds_epi16(y1i_over2,rho_rmi); - H =simde_mm_abs_epi16(xmm3); // H = |y1i/2 + rho*/4| - xmm2 =simde_mm_adds_epi16(xmm2,H); // = |y1r/2 + rho/4| + |y1i/2 + rho*/4| - y0i/2 - logmax_den_re0 =simde_mm_max_epi16(logmax_den_re0,xmm2); // max, denominator done - - // Compute the terms for the LLR of first bit - - // 1 term for nominator of LLR - xmm2 = simde_mm_adds_epi16(A,y0r_over2); - simde__m128i logmax_num_im0 = simde_mm_adds_epi16(B, xmm2); // = |y1r/2 - rho/4| + |y1i/2 - rho*/4| + y0r/2 - - // 2 term for nominator of LLR - xmm2 =simde_mm_subs_epi16(E,y0r_over2); - xmm2 =simde_mm_adds_epi16(xmm2,F); // = |y1r/2 + rho*/4| + |y1i/2 - rho/4| - y0r/2 - - logmax_num_im0 =simde_mm_max_epi16(logmax_num_im0,xmm2); // max, nominator done - - // 1 term for denominator of LLR - xmm2 =simde_mm_adds_epi16(C,y0r_over2); - simde__m128i logmax_den_im0 =simde_mm_adds_epi16(D,xmm2); // = |y1r/2 - rho*/4| + |y1i/2 + rho/4| - y0r/2 - - xmm2 =simde_mm_subs_epi16(G,y0r_over2); - xmm2 =simde_mm_adds_epi16(xmm2,H); // = |y1r/2 + rho/4| + |y1i/2 + rho*/4| - y0r/2 - - logmax_den_im0 =simde_mm_max_epi16(logmax_den_im0,xmm2); // max, denominator done - - // LLR of first bit [L1(1), L1(2), L1(3), L1(4)] - y0r =simde_mm_adds_epi16(y0r,logmax_num_re0); - y0r =simde_mm_subs_epi16(y0r,logmax_den_re0); - - // LLR of second bit [L2(1), L2(2), L2(3), L2(4)] - y0i =simde_mm_adds_epi16(y0i,logmax_num_im0); - y0i =simde_mm_subs_epi16(y0i,logmax_den_im0); - - simde_mm_storeu_si128(&stream0_128i_out[i],simde_mm_unpacklo_epi16(y0r,y0i)); // = [L1(1), L2(1), L1(2), L2(2)] - - if (i<((length>>1) - 1)) // false if only 2 REs remain - simde_mm_storeu_si128(&stream0_128i_out[i+1],simde_mm_unpackhi_epi16(y0r,y0i)); - - } - -} diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index d09978728a..82819551e4 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -442,8 +442,9 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, // legacy code use int16, but it is complex16 if (ue) { - UEscopeCopy(ue, pbchRxdataF_comp, pbch_unClipped, sizeof(struct complex16), frame_parms->nb_antennas_rx, pbch_e_rx_idx / 2, 0); - UEscopeCopy(ue, pbchLlr, pbch_e_rx, sizeof(int16_t), frame_parms->nb_antennas_rx, pbch_e_rx_idx, 0); + metadata meta = {.slot = proc->nr_slot_rx, .frame = proc->frame_rx}; + UEscopeCopyWithMetadata(ue, pbchRxdataF_comp, pbch_unClipped, sizeof(struct complex16), frame_parms->nb_antennas_rx, pbch_e_rx_idx / 2, 0, &meta); + UEscopeCopyWithMetadata(ue, pbchLlr, pbch_e_rx, sizeof(int16_t), frame_parms->nb_antennas_rx, pbch_e_rx_idx, 0, &meta); } #ifdef DEBUG_PBCH for (int cnt = 0; cnt < 864 ; cnt++) diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h index 9b7fbb1023..6c26a3e59d 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -52,116 +52,6 @@ */ void nr_ue_dlsch_init(NR_UE_DLSCH_t *dlsch_list, int num_dlsch, uint8_t max_ldpc_iterations); -/** \brief This function computes the LLRs for ML (max-logsum approximation) dual-stream QPSK/QPSK reception. - @param stream0_in Input from channel compensated (MR combined) stream 0 - @param stream1_in Input from channel compensated (MR combined) stream 1 - @param stream0_out Output from LLR unit for stream0 - @param rho01 Cross-correlation between channels (MR combined) - @param length in complex channel outputs*/ -void nr_qpsk_qpsk(int16_t *stream0_in, - int16_t *stream1_in, - int16_t *stream0_out, - int16_t *rho01, - int32_t length); - -/** \brief This function perform LLR computation for dual-stream (QPSK/QPSK) transmission. - @param frame_parms Frame descriptor structure - @param rxdataF_comp Compensated channel output - @param rxdataF_comp_i Compensated channel output for interference - @param rho_i Correlation between channel of signal and inteference - @param dlsch_llr llr output - @param symbol OFDM symbol index in sub-frame - @param len - @param first_symbol_flag flag to indicate this is the first symbol of the dlsch - @param nb_rb number of RBs for this allocation - @param pbch_pss_sss_adj Number of channel bits taken by PBCH/PSS/SSS - @param llr128p pointer to pointer to symbol in dlsch_llr*/ -int32_t nr_dlsch_qpsk_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t **rxdataF_comp, - int32_t **rxdataF_comp_i, - int32_t **rho_i, - int16_t *dlsch_llr, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb, - uint16_t pbch_pss_sss_adj, - int16_t **llr128p); - -/** \brief This function generates log-likelihood ratios (decoder input) for single-stream QPSK received waveforms - @param frame_parms Frame descriptor structure - @param rxdataF_comp Compensated channel output - @param dlsch_llr llr output - @param symbol OFDM symbol index in sub-frame - @param len - @param first_symbol_flag - @param nb_rb number of RBs for this allocation -*/ -int32_t nr_dlsch_qpsk_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t *rxdataF_comp, - int16_t *dlsch_llr, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb); - -/** - \brief This function generates log-likelihood ratios (decoder input) for single-stream 16QAM received waveforms - @param frame_parms Frame descriptor structure - @param rxdataF_comp Compensated channel output - @param dlsch_llr llr output - @param dl_ch_mag Squared-magnitude of channel in each resource element position corresponding to allocation and weighted for - mid-point in 16QAM constellation - @param len - @param symbol OFDM symbol index in sub-frame - @param first_symbol_flag - @param nb_rb number of RBs for this allocation -*/ - -void nr_dlsch_16qam_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t *rxdataF_comp, - int16_t *dlsch_llr, - int32_t *dl_ch_mag, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb); -/** - \brief This function generates log-likelihood ratios (decoder input) for single-stream 16QAM received waveforms - @param frame_parms Frame descriptor structure - @param rxdataF_comp Compensated channel output - @param dlsch_llr llr output - @param dl_ch_mag Squared-magnitude of channel in each resource element position corresponding to allocation, weighted by first - mid-point of 64-QAM constellation - @param dl_ch_magb Squared-magnitude of channel in each resource element position corresponding to allocation, weighted by second - mid-point of 64-QAM constellation - @param symbol OFDM symbol index in sub-frame - @param len - @param first_symbol_flag - @param nb_rb number of RBs for this allocation -*/ - -void nr_dlsch_64qam_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t *rxdataF_comp, - int16_t *dlsch_llr, - int32_t *dl_ch_mag, - int32_t *dl_ch_magb, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb); - -void nr_dlsch_256qam_llr(NR_DL_FRAME_PARMS *frame_parms, - int32_t *rxdataF_comp, - int16_t *dlsch_llr, - int32_t *dl_ch_mag, - int32_t *dl_ch_magb, - int32_t *dl_ch_magr, - uint8_t symbol, - uint32_t len, - uint8_t first_symbol_flag, - uint16_t nb_rb); - void nr_dlsch_deinterleaving(uint8_t symbol, uint8_t start_symbol, uint16_t L, diff --git a/openair1/PHY/TOOLS/CMakeLists.txt b/openair1/PHY/TOOLS/CMakeLists.txt index 93ef4f5cc7..ada3d8c863 100644 --- a/openair1/PHY/TOOLS/CMakeLists.txt +++ b/openair1/PHY/TOOLS/CMakeLists.txt @@ -63,3 +63,7 @@ endif() if (ENABLE_TESTS) add_subdirectory(tests) endif() + +if (ENABLE_IMSCOPE) + add_subdirectory(imscope) +endif() diff --git a/openair1/PHY/TOOLS/imscope/CMakeLists.txt b/openair1/PHY/TOOLS/imscope/CMakeLists.txt new file mode 100644 index 0000000000..60b2dc69ce --- /dev/null +++ b/openair1/PHY/TOOLS/imscope/CMakeLists.txt @@ -0,0 +1,34 @@ + +CPMAddPackage("gh:ocornut/imgui#v1.90.9") +add_library(imgui + ${imgui_SOURCE_DIR}/imgui_draw.cpp + ${imgui_SOURCE_DIR}/imgui.cpp + ${imgui_SOURCE_DIR}/imgui_widgets.cpp + ${imgui_SOURCE_DIR}/imgui_tables.cpp + ${imgui_SOURCE_DIR}/imgui_demo.cpp +) +target_include_directories(imgui PUBLIC ${imgui_SOURCE_DIR}) + +add_library(imgui_opengl_renderer ${imgui_SOURCE_DIR}/backends/imgui_impl_opengl3.cpp) +target_include_directories(imgui_opengl_renderer PUBLIC ${imgui_SOURCE_DIR}/backends/) +target_link_libraries(imgui_opengl_renderer PUBLIC imgui) + +add_library(imgui_glfw_backend ${imgui_SOURCE_DIR}/backends/imgui_impl_glfw.cpp) +target_include_directories(imgui_glfw_backend PUBLIC ${imgui_SOURCE_DIR}/backends/) +target_link_libraries(imgui_glfw_backend PUBLIC imgui) + +find_package(OpenGL REQUIRED) +find_package(glfw3 3.3 REQUIRED) + +CPMAddPackage("gh:epezent/implot#v0.16") +add_library(implot + ${implot_SOURCE_DIR}/implot.cpp + ${implot_SOURCE_DIR}/implot_demo.cpp + ${implot_SOURCE_DIR}/implot_items.cpp +) +target_link_libraries(implot PUBLIC imgui) +target_include_directories(implot PUBLIC ${implot_SOURCE_DIR}) + +add_library(imscope MODULE imscope.cpp ../phy_scope_interface.c) +target_link_libraries(imscope PUBLIC imgui_glfw_backend glfw imgui_opengl_renderer OpenGL::OpenGL implot UTIL) +set_target_properties(imscope PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) diff --git a/openair1/PHY/TOOLS/imscope/imscope.cpp b/openair1/PHY/TOOLS/imscope/imscope.cpp new file mode 100644 index 0000000000..8205c85b9e --- /dev/null +++ b/openair1/PHY/TOOLS/imscope/imscope.cpp @@ -0,0 +1,847 @@ +/* + * Licensed to the OpenAirInterface (OAI) Software Alliance under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The OpenAirInterface Software Alliance licenses this file to You under + * the OAI Public License, Version 1.1 (the "License"); you may not use this file + * except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.openairinterface.org/?page_id=698 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + *------------------------------------------------------------------------------- + * For more information about the OpenAirInterface (OAI) Software Alliance: + * contact@openairinterface.org + */ + +#include "imgui.h" +#include "imgui_impl_glfw.h" +#include "imgui_impl_opengl3.h" +#include +#define GL_SILENCE_DEPRECATION +#if defined(IMGUI_IMPL_OPENGL_ES2) +#include +#endif +#include // Will drag system OpenGL headers + +#include "implot.h" +#include "openair1/PHY/defs_nr_UE.h" +extern "C" { +#include "openair1/PHY/TOOLS/phy_scope_interface.h" +uint64_t get_softmodem_optmask(void); +} +#include +#include +#include +#include +#include +#include +#include +#include "executables/softmodem-bits.h" + +#define MAX_OFFSETS 14 +#define NR_MAX_RB 273 +#define N_SC_PER_RB NR_NB_SC_PER_RB + +static std::vector rb_boundaries; + +void copyDataThreadSafe(void *scopeData, + enum scopeDataType type, + void *dataIn, + int elementSz, + int colSz, + int lineSz, + int offset, + metadata *meta); +bool tryLockScopeData(enum scopeDataType type, int elementSz, int colSz, int lineSz, metadata *meta); +void copyDataUnsafeWithOffset(enum scopeDataType type, void *dataIn, size_t size, size_t offset, int copy_index); +void unlockScopeData(enum scopeDataType type); + +static void glfw_error_callback(int error, const char *description) +{ + fprintf(stderr, "GLFW Error %d: %s\n", error, description); +} + +typedef struct ImScopeData { + std::mutex write_mutex; + scopeGraphData_t *scope_graph_data; + bool is_data_ready; + metadata meta; + uint64_t time_taken_in_ns; + uint64_t time_taken_in_ns_per_offset[MAX_OFFSETS]; + size_t data_copied_per_offset[MAX_OFFSETS]; +} ImScopeData; + +typedef struct MovingAverageTimer { + uint64_t sum = 0; + float average = 0; + float last_update_time = 0; + void UpdateAverage(float time) + { + if (time > last_update_time + 1) { + float new_average = sum / (float)((time - last_update_time) / 1000); + average = 0.95 * average + 0.05 * new_average; + sum = 0; + } + } + void Add(uint64_t ns) + { + sum += ns; + } +} MovingAverageTimer; + +MovingAverageTimer iq_procedure_timer; + +static ImScopeData scope_array[EXTRA_SCOPE_TYPES]; + +typedef struct IQData { + std::vector real; + std::vector imag; + int16_t max_iq; + std::vector power; + float max_power; + float timestamp; + int nonzero_count; + int len; + metadata meta; + + bool TryCollect(ImScopeData *imscope_data, float time, float epsilon) + { + if (imscope_data->is_data_ready) { + iq_procedure_timer.Add(imscope_data->time_taken_in_ns); + timestamp = time; + scopeGraphData_t *iq_header = imscope_data->scope_graph_data; + len = iq_header->lineSz; + real.reserve(len); + imag.reserve(len); + power.reserve(len); + c16_t *source = (c16_t *)(iq_header + 1); + max_iq = 0; + nonzero_count = 0; + for (auto i = 0; i < len; i++) { + real[i] = source[i].r; + imag[i] = source[i].i; + max_iq = std::max(max_iq, (int16_t)std::abs(source[i].r)); + max_iq = std::max(max_iq, (int16_t)std::abs(source[i].i)); + nonzero_count = std::abs(source[i].r) > epsilon || std::abs(source[i].i) > epsilon ? nonzero_count + 1 : nonzero_count; + power[i] = std::sqrt(std::pow(source[i].r, 2) + std::pow(source[i].i, 2)); + } + meta = imscope_data->meta; + imscope_data->is_data_ready = false; + return true; + } + return false; + } +} IQData; + +class LLRPlot { + int len = 0; + float timestamp = 0; + std::vector llr; + bool frozen = false; + bool next = false; + metadata meta; + + public: + void Draw(float time, enum scopeDataType type, const char *label) + { + ImGui::BeginGroup(); + if (ImGui::Button(frozen ? "Unfreeze" : "Freeze")) { + frozen = !frozen; + next = false; + } + if (frozen) { + ImGui::SameLine(); + ImGui::BeginDisabled(next); + if (ImGui::Button("Load next histogram")) { + next = true; + } + ImGui::EndDisabled(); + } + + ImScopeData &scope_data = scope_array[type]; + if (ImPlot::BeginPlot(label)) { + if (!frozen || next) { + if (scope_data.is_data_ready) { + iq_procedure_timer.Add(scope_data.time_taken_in_ns); + timestamp = time; + const int16_t *tmp = (int16_t *)(scope_data.scope_graph_data + 1); + len = scope_data.scope_graph_data->lineSz; + llr.reserve(len); + for (auto i = 0; i < len; i++) { + llr[i] = tmp[i]; + } + meta = scope_data.meta; + scope_data.is_data_ready = false; + if (frozen) { + next = false; + } + } + } + + ImPlot::PlotLine(label, llr.data(), len); + ImPlot::EndPlot(); + } + std::stringstream ss; + if (meta.slot != -1) { + ss << " slot: " << meta.slot; + } + if (meta.frame != -1) { + ss << " frame: " << meta.frame; + } + if (!ss.str().empty()) { + ImGui::Text("Data for %s", ss.str().c_str()); + } + ImGui::Text("Data is %.2f seconds old", time - timestamp); + ImGui::EndGroup(); + } +}; + +class IQHist { + private: + bool frozen = false; + bool next = false; + float range = 100; + int num_bins = 33; + std::string label; + float min_nonzero_percentage = 0.9; + float epsilon = 0.0; + bool auto_adjust_range = true; + int plot_type = 0; + + public: + IQHist(const char *label_) + { + label = label_; + }; + bool ShouldReadData(void) + { + return !frozen || next; + } + float GetEpsilon(void) + { + return epsilon; + } + void Draw(IQData *iq_data, float time, bool new_data) + { + if (new_data && frozen && next) { + // Evaluate if new data matches filter settings + if (((float)iq_data->nonzero_count / (float)iq_data->len) > min_nonzero_percentage) { + next = false; + } + } + ImGui::BeginGroup(); + ImGui::Checkbox("auto adjust range", &auto_adjust_range); + if (auto_adjust_range) { + if (range < iq_data->max_iq * 1.1) { + range = iq_data->max_iq * 1.1; + } + } + ImGui::BeginDisabled(auto_adjust_range); + ImGui::SameLine(); + ImGui::DragFloat("Range", &range, 1, 0.1, std::numeric_limits::max()); + ImGui::EndDisabled(); + + ImGui::DragInt("Number of bins", &num_bins, 1, 33, 101); + if (ImGui::Button(frozen ? "Unfreeze" : "Freeze")) { + frozen = !frozen; + next = false; + } + + if (frozen) { + ImGui::SameLine(); + ImGui::BeginDisabled(next); + if (ImGui::Button("Load next histogram")) { + next = true; + } + ImGui::EndDisabled(); + ImGui::Text("Filter parameters"); + ImGui::DragFloat("%% nonzero elements", &min_nonzero_percentage, 1, 0.0, 100); + ImGui::DragFloat("epsilon", &epsilon, 1, 0.0, 3000); + } + const char *items[] = {"Histogram", "Scatter", "RMS"}; + ImGui::Combo("Select plot type", &plot_type, items, sizeof(items) / sizeof(items[0])); + if (plot_type == 0) { + if (ImPlot::BeginPlot(label.c_str(), {(float)ImGui::GetWindowWidth() * 0.3f, (float)ImGui::GetWindowWidth() * 0.3f})) { + ImPlot::PlotHistogram2D(label.c_str(), + iq_data->real.data(), + iq_data->imag.data(), + iq_data->len, + num_bins, + num_bins, + ImPlotRect(-range, range, -range, range)); + ImPlot::EndPlot(); + } + } else if (plot_type == 1) { + if (ImPlot::BeginPlot(label.c_str(), {(float)ImGui::GetWindowWidth() * 0.3f, (float)ImGui::GetWindowWidth() * 0.3f})) { + // Limit the amount of data plotted with scatterplot (issue with vertices/draw call) + ImPlot::PlotScatter(label.c_str(), iq_data->real.data(), iq_data->imag.data(), std::min(16000, iq_data->len)); + ImPlot::EndPlot(); + } + } else if (plot_type == 2) { + if (ImPlot::BeginPlot(label.c_str())) { + ImPlot::PlotLine(label.c_str(), iq_data->power.data(), iq_data->len); + ImPlot::EndPlot(); + } + } + ImGui::Text("Maximum value = %d, nonzero elements/total %d/%d", iq_data->max_iq, iq_data->nonzero_count, iq_data->len); + ImGui::Text("Data is %.2f seconds old", time - iq_data->timestamp); + std::stringstream ss; + if (iq_data->meta.slot != -1) { + ss << " slot: " << iq_data->meta.slot; + } + if (iq_data->meta.frame != -1) { + ss << " frame: " << iq_data->meta.frame; + } + if (!ss.str().empty()) { + ImGui::Text("Data for %s", ss.str().c_str()); + } + ImGui::EndGroup(); + } +}; + +class IQSlotHeatmap { + private: + bool frozen = false; + bool next = false; + float timestamp = 0; + std::vector power; + ImScopeData *scope_data; + std::string label; + int len = 0; + float max = 0; + float stop_at_min = 1000; + + public: + IQSlotHeatmap(ImScopeData *scope_data_, const char *label_) + { + scope_data = scope_data_; + label = label_; + }; + // Read in the data from the sink and transform it for the use by the scope + void ReadData(float time, int ofdm_symbol_size, int num_symbols, int first_carrier_offset, int num_rb) + { + auto num_sc = num_rb * NR_NB_SC_PER_RB; + if (!frozen || next) { + if (scope_data->is_data_ready) { + iq_procedure_timer.Add(scope_data->time_taken_in_ns); + uint16_t first_sc = first_carrier_offset; + uint16_t last_sc = first_sc + num_rb * NR_NB_SC_PER_RB; + bool wrapped = false; + uint16_t wrapped_first_sc = 0; + uint16_t wrapped_last_sc = 0; + if (last_sc >= ofdm_symbol_size) { + last_sc = ofdm_symbol_size - 1; + wrapped = true; + auto num_sc_left = num_sc - (last_sc - first_sc + 1); + wrapped_last_sc = wrapped_first_sc + num_sc_left - 1; + } + timestamp = time; + scopeGraphData_t *iq_header = scope_data->scope_graph_data; + len = iq_header->lineSz; + c16_t *source = (c16_t *)(iq_header + 1); + + power.reserve(num_sc * num_symbols); + for (auto symbol = 0; symbol < num_symbols; symbol++) { + int subcarrier = 0; + for (auto sc = first_sc; sc <= last_sc; sc++) { + auto source_index = sc + symbol * ofdm_symbol_size; + power[subcarrier * num_symbols + symbol] = std::pow(source[source_index].r, 2) + std::pow(source[source_index].i, 2); + subcarrier++; + } + if (wrapped) { + for (auto sc = wrapped_first_sc; sc <= wrapped_last_sc; sc++) { + auto source_index = sc + symbol * ofdm_symbol_size; + power[subcarrier * num_symbols + symbol] = std::pow(source[source_index].r, 2) + std::pow(source[source_index].i, 2); + subcarrier++; + } + } + } + max = *std::max_element(power.begin(), power.end()); + if (frozen && max > stop_at_min) { + next = false; + } + scope_data->is_data_ready = false; + } + } + } + void Draw(float time, int ofdm_symbol_size, int num_symbols, int first_carrier_offset, int num_rb) + { + ReadData(time, ofdm_symbol_size, num_symbols, first_carrier_offset, num_rb); + ImGui::BeginGroup(); + if (ImGui::Button(frozen ? "Unfreeze" : "Freeze")) { + frozen = !frozen; + next = false; + } + if (frozen) { + ImGui::SameLine(); + ImGui::BeginDisabled(next); + if (ImGui::Button("Load next data")) { + next = true; + } + ImGui::EndDisabled(); + ImGui::Text("Filter parameters:"); + ImGui::InputFloat("Max Power minimum", &stop_at_min, 10, 100); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Data with maximum power below that value will be discarded."); + } + } + static std::vector symbol_boundaries = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}; + if (ImPlot::BeginPlot(label.c_str(), {(float)ImGui::GetWindowWidth() * 0.9f, 0})) { + auto num_sc = num_rb * NR_NB_SC_PER_RB; + ImPlot::SetupAxes("symbols", "subcarriers"); + ImPlot::SetupAxisLimits(ImAxis_X1, num_symbols, 0); + ImPlot::SetupAxisLimits(ImAxis_Y1, num_sc, 0); + ImPlot::PlotHeatmap(label.c_str(), + power.data(), + num_sc, + num_symbols, + 0, + max, + nullptr, + {0, 0}, + {(double)num_symbols, (double)num_sc}); + ImPlot::PlotInfLines("Symbol boundary", symbol_boundaries.data(), symbol_boundaries.size()); + ImPlot::PlotInfLines("RB boundary", rb_boundaries.data(), num_rb, ImPlotInfLinesFlags_Horizontal); + ImPlot::EndPlot(); + } + ImGui::SameLine(); + ImPlot::ColormapScale("##HeatScale", 0, max); + ImGui::Text("Data is %.2f seconds old", time - timestamp); + ImGui::EndGroup(); + } +}; + +// utility structure for realtime plot +struct ScrollingBuffer { + int MaxSize; + int Offset; + ImVector Data; + ScrollingBuffer(int max_size = 2000) + { + MaxSize = max_size; + Offset = 0; + Data.reserve(MaxSize); + } + void AddPoint(float x, float y) + { + if (Data.size() < MaxSize) + Data.push_back(ImVec2(x, y)); + else { + Data[Offset] = ImVec2(x, y); + Offset = (Offset + 1) % MaxSize; + } + } + void Erase() + { + if (Data.size() > 0) { + Data.shrink(0); + Offset = 0; + } + } +}; + +void ShowUeScope(PHY_VARS_NR_UE *ue, float t) +{ + if (ImPlot::BeginPlot("##Scrolling", ImVec2(-1, 150))) { + static float history = 10.0f; + ImGui::SliderFloat("History", &history, 1, 30, "%.1f s"); + static ScrollingBuffer rbs_buffer; + static ScrollingBuffer bler; + static ScrollingBuffer mcs; + rbs_buffer.AddPoint(t, getKPIUE()->nofRBs); + bler.AddPoint(t, (float)getKPIUE()->nb_nack / (float)getKPIUE()->nb_total); + mcs.AddPoint(t, (float)getKPIUE()->dl_mcs); + ImPlot::SetupAxes("time", "noOfRbs"); + ImPlot::SetupAxisLimits(ImAxis_X1, t - history, t, ImGuiCond_Always); + ImPlot::SetupAxisLimits(ImAxis_Y1, 0, NR_MAX_RB); + ImPlot::SetupAxis(ImAxis_Y2, "bler [%]", ImPlotAxisFlags_AuxDefault); + ImPlot::SetupAxis(ImAxis_Y3, "MCS", ImPlotAxisFlags_AuxDefault); + ImPlot::SetAxes(ImAxis_X1, ImAxis_Y1); + ImPlot::PlotLine("noOfRbs", &rbs_buffer.Data[0].x, &rbs_buffer.Data[0].y, rbs_buffer.Data.size(), 0, 0, 2 * sizeof(float)); + ImPlot::SetAxes(ImAxis_X1, ImAxis_Y2); + ImPlot::PlotLine("bler", &bler.Data[0].x, &bler.Data[0].y, bler.Data.size(), 0, 0, 2 * sizeof(float)); + ImPlot::SetAxes(ImAxis_X1, ImAxis_Y3); + ImPlot::PlotLine("mcs", &mcs.Data[0].x, &mcs.Data[0].y, mcs.Data.size(), 0, 0, 2 * sizeof(float)); + ImPlot::EndPlot(); + } + if (ImGui::TreeNode("PDSCH IQ")) { + static auto iq_data = new IQData(); + static auto pdsch_iq_hist = new IQHist("PDSCH IQ"); + bool new_data = false; + if (pdsch_iq_hist->ShouldReadData()) { + new_data = iq_data->TryCollect(&scope_array[pdschRxdataF_comp], t, pdsch_iq_hist->GetEpsilon()); + } + pdsch_iq_hist->Draw(iq_data, t, new_data); + ImGui::TreePop(); + } + if (ImGui::TreeNode("Broadcast channel")) { + ImGui::Text("RSRP %d", ue->measurements.ssb_rsrp_dBm[ue->frame_parms.ssb_index]); + if (ImGui::TreeNode("IQ")) { + static auto iq_data = new IQData(); + static auto broadcast_iq_hist = new IQHist("Broadcast IQ"); + bool new_data = false; + if (broadcast_iq_hist->ShouldReadData()) { + new_data = iq_data->TryCollect(&scope_array[ue->sl_mode ? psbchRxdataF_comp : pbchRxdataF_comp], + t, + broadcast_iq_hist->GetEpsilon()); + } + broadcast_iq_hist->Draw(iq_data, t, new_data); + ImGui::TreePop(); + } + if (ImGui::TreeNode("CHest")) { + static auto chest_iq_data = new IQData(); + static auto broadcast_iq_chest = new IQHist("Broadcast Chest"); + bool new_data = false; + if (broadcast_iq_chest->ShouldReadData()) { + new_data = chest_iq_data->TryCollect(&scope_array[ue->sl_mode ? psbchDlChEstimateTime : pbchDlChEstimateTime], + t, + broadcast_iq_chest->GetEpsilon()); + } + broadcast_iq_chest->Draw(chest_iq_data, t, new_data); + ImGui::TreePop(); + } + + if (ImGui::TreeNode("LLR")) { + static auto llr_plot = new LLRPlot(); + llr_plot->Draw(t, ue->sl_mode ? psbchLlr : pbchLlr, "Broadcast LLR"); + ImGui::TreePop(); + } + ImGui::TreePop(); + } + if (ImGui::TreeNode("RX IQ")) { + static auto common_rx_iq_heatmap = new IQSlotHeatmap(&scope_array[commonRxdataF], "common RX IQ"); + common_rx_iq_heatmap->Draw(t, + ue->frame_parms.ofdm_symbol_size, + ue->frame_parms.symbols_per_slot, + ue->frame_parms.first_carrier_offset, + ue->frame_parms.N_RB_DL); + ImGui::TreePop(); + } +} + +void ShowGnbScope(PHY_VARS_gNB *gNB, float t) +{ + if (ImGui::TreeNode("RX IQ")) { + static auto gnb_heatmap = new IQSlotHeatmap(&scope_array[gNBRxdataF], "common RX IQ"); + + gnb_heatmap->Draw(t, + gNB->frame_parms.ofdm_symbol_size, + gNB->frame_parms.symbols_per_slot, + gNB->frame_parms.first_carrier_offset, + gNB->frame_parms.N_RB_UL); + ImGui::TreePop(); + } + if (ImGui::TreeNode("PUSCH SLOT IQ")) { + static auto pusch_iq = new IQData(); + static auto pusch_iq_display = new IQHist("PUSCH compensated IQ"); + bool new_data = false; + if (pusch_iq_display->ShouldReadData()) { + new_data = pusch_iq->TryCollect(&scope_array[gNBPuschRxIq], t, pusch_iq_display->GetEpsilon()); + } + pusch_iq_display->Draw(pusch_iq, t, new_data); + ImGui::TreePop(); + } + if (ImGui::TreeNode("PUSCH LLRs")) { + static auto pusch_llr_plot = new LLRPlot(); + pusch_llr_plot->Draw(t, gNBPuschLlr, "PUSCH LLR"); + ImGui::TreePop(); + } +} + +void *imscope_thread(void *data_void_ptr) +{ + glfwSetErrorCallback(glfw_error_callback); + if (!glfwInit()) + return nullptr; + + // Decide GL+GLSL versions +#if defined(IMGUI_IMPL_OPENGL_ES2) + // GL ES 2.0 + GLSL 100 + const char *glsl_version = "#version 100"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 2); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + glfwWindowHint(GLFW_CLIENT_API, GLFW_OPENGL_ES_API); +#elif defined(__APPLE__) + // GL 3.2 + GLSL 150 + const char *glsl_version = "#version 150"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2); + glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // Required on Mac +#else + // GL 3.0 + GLSL 130 + const char *glsl_version = "#version 130"; + glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3); + glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 0); + // glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE); // 3.2+ only + // glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE); // 3.0+ only +#endif + + // Create window with graphics context + GLFWwindow *window = glfwCreateWindow(1280, 720, "imscope", nullptr, nullptr); + if (window == nullptr) + return nullptr; + glfwMakeContextCurrent(window); + glfwSwapInterval(1); // For frame capping + + // Setup Dear ImGui context + IMGUI_CHECKVERSION(); + ImGui::CreateContext(); + ImPlot::CreateContext(); + ImGuiIO &io = ImGui::GetIO(); + (void)io; + io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls + + // Setup Dear ImGui style + ImGui::StyleColorsDark(); + // ImGui::StyleColorsLight(); + + // Setup Platform/Renderer backends + ImGui_ImplGlfw_InitForOpenGL(window, true); +#ifdef __EMSCRIPTEN__ + ImGui_ImplGlfw_InstallEmscriptenCallbacks(window, "#canvas"); +#endif + ImGui_ImplOpenGL3_Init(glsl_version); + + // Our state + ImVec4 clear_color = ImVec4(0.45f, 0.55f, 0.60f, 1.00f); + + for (auto i = 0U; i < NR_MAX_RB; i++) { + rb_boundaries.push_back(i * NR_NB_SC_PER_RB); + } + + static double last_frame_time = glfwGetTime(); + static int target_fps = 24; + + bool is_ue = (get_softmodem_optmask() & SOFTMODEM_5GUE_BIT) > 0; + while (!glfwWindowShouldClose(window)) { + // Poll and handle events (inputs, window resize, etc.) + // You can read the io.WantCaptureMouse, io.WantCaptureKeyboard flags to tell if dear imgui wants to use your inputs. + // - When io.WantCaptureMouse is true, do not dispatch mouse input data to your main application, or clear/overwrite your copy + // of the mouse data. + // - When io.WantCaptureKeyboard is true, do not dispatch keyboard input data to your main application, or clear/overwrite your + // copy of the keyboard data. Generally you may always pass all inputs to dear imgui, and hide them from your application based + // on those two flags. + glfwPollEvents(); + + // Start the Dear ImGui frame + ImGui_ImplOpenGL3_NewFrame(); + ImGui_ImplGlfw_NewFrame(); + ImGui::NewFrame(); + + int display_w, display_h; + glfwGetFramebufferSize(window, &display_w, &display_h); + + static float t = 0; + + t += ImGui::GetIO().DeltaTime; + ImGui::SetNextWindowPos({0, 0}); + ImGui::SetNextWindowSize({(float)display_w, (float)display_h}); + ImGui::Begin("NR KPI", nullptr, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove); + if (ImGui::TreeNode("Global settings")) { + ImGui::ShowFontSelector("Font"); + ImGui::ShowStyleSelector("ImGui Style"); + ImPlot::ShowStyleSelector("ImPlot Style"); + ImPlot::ShowColormapSelector("ImPlot Colormap"); + ImGui::SliderInt("FPS target", &target_fps, 12, 60); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Reduces scope flickering in unfrozen mode. Can reduce impact on perfromance of the modem"); + } + ImGui::TreePop(); + } + iq_procedure_timer.UpdateAverage(t); + ImGui::Text("Total time used by IQ capture procedures per milisecond: %.2f [us]/[ms]", iq_procedure_timer.average / 1000); + if (ImGui::IsItemHovered()) { + ImGui::SetTooltip("Total time used in PHY threads for copying out IQ data for the scope, in uS, averaged over 1 ms"); + } + + if (is_ue) { + PHY_VARS_NR_UE *ue = (PHY_VARS_NR_UE *)data_void_ptr; + ShowUeScope(ue, t); + } else { + scopeParms_t *scope_params = (scopeParms_t *)data_void_ptr; + PHY_VARS_gNB *gNB = scope_params->gNB; + ShowGnbScope(gNB, t); + } + ImGui::End(); + + // For reference + ImPlot::ShowDemoWindow(); + ImGui::ShowDemoWindow(); + + // Rendering + ImGui::Render(); + glViewport(0, 0, display_w, display_h); + glClearColor(clear_color.x * clear_color.w, clear_color.y * clear_color.w, clear_color.z * clear_color.w, clear_color.w); + glClear(GL_COLOR_BUFFER_BIT); + ImGui_ImplOpenGL3_RenderDrawData(ImGui::GetDrawData()); + + double target_frame_time = 1.0 / target_fps; + double delta = glfwGetTime() - last_frame_time; + if (delta < target_frame_time) { + std::this_thread::sleep_for(std::chrono::duration(target_frame_time - delta)); + } + + glfwSwapBuffers(window); + last_frame_time = glfwGetTime(); + } + + // Cleanup + ImGui_ImplOpenGL3_Shutdown(); + ImGui_ImplGlfw_Shutdown(); + ImGui::DestroyContext(); + + glfwDestroyWindow(window); + glfwTerminate(); + + return nullptr; +} + +extern "C" void imscope_autoinit(void *dataptr) +{ + AssertFatal((get_softmodem_optmask() & SOFTMODEM_5GUE_BIT) || (get_softmodem_optmask() & SOFTMODEM_GNB_BIT), + "Scope cannot find NRUE or GNB context"); + + for (auto i = 0U; i < EXTRA_SCOPE_TYPES; i++) { + scope_array[i].is_data_ready = false; + scope_array[i].scope_graph_data = nullptr; + scope_array[i].meta = {-1, -1}; + } + + if (SOFTMODEM_GNB_BIT & get_softmodem_optmask()) { + scopeParms_t *scope_params = (scopeParms_t *)dataptr; + scopeData_t *scope = (scopeData_t *)calloc(1, sizeof(scopeData_t)); + scope->copyData = copyDataThreadSafe; + scope->tryLockScopeData = tryLockScopeData; + scope->copyDataUnsafeWithOffset = copyDataUnsafeWithOffset; + scope->unlockScopeData = unlockScopeData; + scope_params->gNB->scopeData = scope; + } else { + PHY_VARS_NR_UE *ue = (PHY_VARS_NR_UE *)dataptr; + scopeData_t *scope = (scopeData_t *)calloc(1, sizeof(scopeData_t)); + scope->copyData = copyDataThreadSafe; + ue->scopeData = scope; + } + pthread_t thread; + threadCreate(&thread, imscope_thread, dataptr, (char *)"imscope", -1, sched_get_priority_min(SCHED_RR)); +} + +void copyDataThreadSafe(void *scopeData, + enum scopeDataType type, + void *dataIn, + int elementSz, + int colSz, + int lineSz, + int offset, + metadata *meta) +{ + ImScopeData &scope_data = scope_array[type]; + + if (scope_data.is_data_ready) { + // data is ready, wasn't consumed yet by scope + return; + } + + if (scope_data.write_mutex.try_lock()) { + auto start = std::chrono::high_resolution_clock::now(); + scopeGraphData_t *data = scope_data.scope_graph_data; + int oldDataSz = data ? data->dataSize : 0; + int newSz = elementSz * colSz * lineSz; + if (data == NULL || oldDataSz < newSz) { + free(data); + scopeGraphData_t *ptr = (scopeGraphData_t *)malloc(sizeof(scopeGraphData_t) + newSz); + if (!ptr) { + LOG_E(PHY, "can't realloc\n"); + return; + } else { + data = ptr; + } + } + + data->elementSz = elementSz; + data->colSz = colSz; + data->lineSz = lineSz; + data->dataSize = newSz; + memcpy(((void *)(data + 1)), dataIn, newSz); + scope_data.scope_graph_data = data; + scope_data.meta = *meta; + scope_data.time_taken_in_ns = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); + scope_data.is_data_ready = true; + scope_data.write_mutex.unlock(); + } +} + +bool tryLockScopeData(enum scopeDataType type, int elementSz, int colSz, int lineSz, metadata *meta) +{ + ImScopeData &scope_data = scope_array[type]; + + if (scope_data.is_data_ready) { + // data is ready, wasn't consumed yet by scope + return false; + } + + if (scope_data.write_mutex.try_lock()) { + auto start = std::chrono::high_resolution_clock::now(); + scopeGraphData_t *data = scope_data.scope_graph_data; + int oldDataSz = data ? data->dataSize : 0; + int newSz = elementSz * colSz * lineSz; + if (data == NULL || oldDataSz < newSz) { + free(data); + scopeGraphData_t *ptr = (scopeGraphData_t *)malloc(sizeof(scopeGraphData_t) + newSz); + if (!ptr) { + LOG_E(PHY, "can't realloc\n"); + return false; + } else { + data = ptr; + } + } + + data->elementSz = elementSz; + data->colSz = colSz; + data->lineSz = lineSz; + data->dataSize = newSz; + scope_data.scope_graph_data = data; + scope_data.meta = *meta; + scope_data.time_taken_in_ns = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); + memset(scope_data.time_taken_in_ns_per_offset, 0, sizeof(scope_data.time_taken_in_ns_per_offset)); + memset(scope_data.data_copied_per_offset, 0, sizeof(scope_data.data_copied_per_offset)); + return true; + } + return false; +} + +void copyDataUnsafeWithOffset(enum scopeDataType type, void *dataIn, size_t size, size_t offset, int copy_index) +{ + AssertFatal(copy_index < MAX_OFFSETS, "Unexpected number of copies per sink. copy_index = %d\n", copy_index); + ImScopeData &scope_data = scope_array[type]; + auto start = std::chrono::high_resolution_clock::now(); + scopeGraphData_t *data = scope_data.scope_graph_data; + uint8_t *outptr = (uint8_t *)(data + 1); + memcpy(&outptr[offset], dataIn, size); + scope_data.time_taken_in_ns_per_offset[copy_index] = + std::chrono::duration_cast(std::chrono::high_resolution_clock::now() - start).count(); + scope_data.data_copied_per_offset[copy_index] = size; +} + +void unlockScopeData(enum scopeDataType type) +{ + ImScopeData &scope_data = scope_array[type]; + size_t total_size = 0; + for (auto i = 0; i < MAX_OFFSETS; i++) { + scope_data.time_taken_in_ns += scope_data.time_taken_in_ns_per_offset[i]; + total_size += scope_data.data_copied_per_offset[i]; + } + if (total_size != (uint64_t)scope_data.scope_graph_data->dataSize) { + LOG_E(PHY, "Scope is missing data - not all data that was expected was copied - possibly missed copyDataUnsafeWithOffset call\n"); + } + scope_data.is_data_ready = true; + scope_data.write_mutex.unlock(); +} diff --git a/openair1/PHY/TOOLS/imscope/imscope_screenshot.png b/openair1/PHY/TOOLS/imscope/imscope_screenshot.png new file mode 100644 index 0000000000..e33b294827 Binary files /dev/null and b/openair1/PHY/TOOLS/imscope/imscope_screenshot.png differ diff --git a/openair1/PHY/TOOLS/phy_scope_interface.c b/openair1/PHY/TOOLS/phy_scope_interface.c index 14f361362d..70d3889b64 100644 --- a/openair1/PHY/TOOLS/phy_scope_interface.c +++ b/openair1/PHY/TOOLS/phy_scope_interface.c @@ -58,8 +58,11 @@ int end_forms(void) { return -1; } -void copyData(void *scopeData, enum scopeDataType type, void *dataIn, int elementSz, int colSz, int lineSz, int offset) +void copyData(void *scopeData, enum scopeDataType type, void *dataIn, int elementSz, int colSz, int lineSz, int offset, metadata *meta) { + if (type >= MAX_SCOPE_TYPES) { + return; + } scopeData_t *tmp = (scopeData_t *)scopeData; if (tmp) { diff --git a/openair1/PHY/TOOLS/phy_scope_interface.h b/openair1/PHY/TOOLS/phy_scope_interface.h index 7c5e33b379..2f8378e7b7 100644 --- a/openair1/PHY/TOOLS/phy_scope_interface.h +++ b/openair1/PHY/TOOLS/phy_scope_interface.h @@ -70,7 +70,10 @@ enum scopeDataType { psbchDlChEstimateTime, psbchLlr, psbchRxdataF_comp, - MAX_SCOPE_TYPES + MAX_SCOPE_TYPES, + gNBPuschRxIq = MAX_SCOPE_TYPES, + gNBPuschLlr, + EXTRA_SCOPE_TYPES }; enum PlotTypeGnbIf { @@ -87,34 +90,89 @@ typedef struct { int lineSz; } scopeGraphData_t; +typedef struct { + int slot; + int frame; +} metadata; + typedef struct scopeData_s { int *argc; char **argv; RU_t *ru; PHY_VARS_gNB *gNB; scopeGraphData_t *liveData[MAX_SCOPE_TYPES]; - void (*copyData)(void *, enum scopeDataType, void *data, int elementSz, int colSz, int lineSz, int offset); + void (*copyData)(void *, enum scopeDataType, void *data, int elementSz, int colSz, int lineSz, int offset, metadata *meta); pthread_mutex_t copyDataMutex; scopeGraphData_t *copyDataBufs[MAX_SCOPE_TYPES][COPIES_MEM]; int copyDataBufsIdx[MAX_SCOPE_TYPES]; void (*scopeUpdater)(enum PlotTypeGnbIf plotType, int numElements); + bool (*tryLockScopeData)(enum scopeDataType type, int elementSz, int colSz, int lineSz, metadata *meta); + void (*copyDataUnsafeWithOffset)(enum scopeDataType type, void *dataIn, size_t size, size_t offset, int copy_index); + void (*unlockScopeData)(enum scopeDataType type); } scopeData_t; int load_softscope(char *exectype, void *initarg); int end_forms(void) ; int copyDataMutexInit(scopeData_t *); -void copyData(void *, enum scopeDataType type, void *dataIn, int elementSz, int colSz, int lineSz, int offset); +void copyData(void *, enum scopeDataType type, void *dataIn, int elementSz, int colSz, int lineSz, int offset, metadata *meta); +#define UEscopeCopyWithMetadata(ue, type, ...) \ + if (ue->scopeData) { \ + ((scopeData_t *)ue->scopeData)->copyData((scopeData_t *)ue->scopeData, type, ##__VA_ARGS__); \ + } #define UEscopeCopy(ue, type, ...) \ - if (ue->scopeData) \ - ((scopeData_t *)ue->scopeData)->copyData((scopeData_t *)ue->scopeData, type, ##__VA_ARGS__); + if (ue->scopeData) { \ + metadata mt = {.slot = -1, .frame = -1}; \ + ((scopeData_t *)ue->scopeData)->copyData((scopeData_t *)ue->scopeData, type, ##__VA_ARGS__, &mt); \ + } +#define gNBscopeCopyWithMetadata(gnb, type, ...) \ + if (gnb->scopeData) { \ + ((scopeData_t *)gnb->scopeData)->copyData((scopeData_t *)gNB->scopeData, type, ##__VA_ARGS__); \ + } #define gNBscopeCopy(gnb, type, ...) \ - if (gnb->scopeData) \ - ((scopeData_t *)gnb->scopeData)->copyData((scopeData_t *)gNB->scopeData, type, ##__VA_ARGS__); + if (gnb->scopeData) { \ + metadata mt = {.slot = -1, .frame = -1}; \ + ((scopeData_t *)gnb->scopeData)->copyData((scopeData_t *)gNB->scopeData, type, ##__VA_ARGS__, &mt); \ + } #define GnbScopeUpdate(gnb, type, numElt) \ if (gnb->scopeData) \ ((scopeData_t *)gnb->scopeData)->scopeUpdater(type, numElt); +#define gNBTryLockScopeData(gnb, type, ...) \ + gnb->scopeData && ((scopeData_t *)gnb->scopeData)->tryLockScopeData \ + && ((scopeData_t *)gnb->scopeData)->tryLockScopeData(type, ##__VA_ARGS__) + +#define gNBscopeCopyUnsafe(gnb, type, ...) \ + scopeData_t *scope_data = (scopeData_t *)gnb->scopeData; \ + if (scope_data && scope_data->copyDataUnsafeWithOffset) { \ + scope_data->copyDataUnsafeWithOffset(type, ##__VA_ARGS__); \ + } + +#define gNBunlockScopeData(gnb, type) \ + scopeData_t *scope_data = (scopeData_t *)gnb->scopeData; \ + if (scope_data && scope_data->unlockScopeData) { \ + scope_data->unlockScopeData(type); \ + } + +#define UEScopeHasTryLock(ue) \ + (ue->scopeData && ((scopeData_t *)ue->scopeData)->tryLockScopeData) + +#define UETryLockScopeData(ue, type, ...) \ + ue->scopeData && ((scopeData_t *)ue->scopeData)->tryLockScopeData \ + && ((scopeData_t *)ue->scopeData)->tryLockScopeData(type, ##__VA_ARGS__) + +#define UEscopeCopyUnsafe(ue, type, ...) \ + scopeData_t *scope_data = (scopeData_t *)ue->scopeData; \ + if (scope_data && scope_data->copyDataUnsafeWithOffset) { \ + scope_data->copyDataUnsafeWithOffset(type, ##__VA_ARGS__); \ + } + +#define UEunlockScopeData(ue, type) \ + scopeData_t *scope_data = (scopeData_t *)ue->scopeData; \ + if (scope_data && scope_data->unlockScopeData) { \ + scope_data->unlockScopeData(type); \ + } + extended_kpi_ue* getKPIUE(); #endif diff --git a/openair1/PHY/TOOLS/readme.md b/openair1/PHY/TOOLS/readme.md index 9a0e2947f9..9d1f4d465b 100644 --- a/openair1/PHY/TOOLS/readme.md +++ b/openair1/PHY/TOOLS/readme.md @@ -31,3 +31,41 @@ or ``` phy_scope_gNB(0, phy_vars_gnb, phy_vars_ru, UE_id) ``` + +# ImScope + +ImScope is a scope based on imgui & implot. This scope uses a different concurrency model than xforms scope, with thread +safety being priority. The goal is to never show incorrect data on the screen and be able to use the scope with real radios. +If correctness cannot be achieved e.g. due to performance issues when using thread safe implementation user should be warned +clearly on the screen. + +![image](./imscope/imscope_screenshot.png) + +## Prerequisites + +ImScope uses imgui, implot, glfw3 and opengl. imgui and implot should be downloaded automatically when configuring the project +with `-DENABLE_IMSCOPE=ON`, using [CPM](https://github.com/cpm-cmake/CPM.cmake). CPM is used because imgui and implot do not have +an official binary release. glfw3 and opengl should be installed with your system, on ubuntu these are contained in packages +libglfw3-dev and libopengl-dev respectivly. + +## Building + +Add `-DENABLE_IMSCOPE=ON` to your `cmake` command. Build target `imscope` + +## Running + +Run with `--imscope` flag + +## Usage notes + + - It's experimental and might contain bugs + - It uses tree nodes to hide/show scopes (layout is subject to change). If a scope is not visible or is frozen it costs nearly + nothing in the PHY threads. If its unfrozen every time the data is displayed the PHY thread would have to perform the copy. By + default this can happen up to 24 times per second. User is informed on the estimated impact on PHY threads at the top of the + window. You can use FPS target to limit the impact on PHY threads and maintain scope functionality while minimizing the effect + on realtime operation. + +## Reporting bugs and feature requests + +Report bugs and feature requests on [gitlab](https://gitlab.eurecom.fr/oai/openairinterface5g/-/issues). There is two demo windows +enabled in the scope that showcase imgui/implot, if you find something of interest it can be implemented in the scope. diff --git a/openair1/PHY/nr_phy_common/CMakeLists.txt b/openair1/PHY/nr_phy_common/CMakeLists.txt index ebbc3fcbc7..ee5fb820a8 100644 --- a/openair1/PHY/nr_phy_common/CMakeLists.txt +++ b/openair1/PHY/nr_phy_common/CMakeLists.txt @@ -1,5 +1,5 @@ add_library(nr_phy_common src/nr_phy_common.c) -target_link_libraries(nr_phy_common PRIVATE UTIL) +target_link_libraries(nr_phy_common PRIVATE UTIL PHY_COMMON) target_include_directories(nr_phy_common PUBLIC inc/) add_library(nr_ue_phy_meas src/nr_ue_phy_meas.c) diff --git a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h index 8e8f7399f0..579102b677 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -24,6 +24,10 @@ #include "PHY/impl_defs_top.h" #include "PHY/TOOLS/tools_defs.h" +void nr_qpsk_llr(int32_t *rxdataF_comp, int16_t *llr, uint32_t nb_re); +void nr_16qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag_in, int16_t *llr, uint32_t nb_re); +void nr_64qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int16_t *llr, uint32_t nb_re); +void nr_256qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int32_t *ch_mag3, int16_t *llr, uint32_t nb_re); void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal); void nr_est_delay(int ofdm_symbol_size, const c16_t *ls_est, c16_t *ch_estimates_time, delay_t *delay); unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, int nb_rb); diff --git a/openair1/PHY/nr_phy_common/src/nr_phy_common.c b/openair1/PHY/nr_phy_common/src/nr_phy_common.c index b67a94d8f6..8c45fe4e72 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -21,6 +21,339 @@ #include "nr_phy_common.h" +#ifdef __aarch64__ +#define USE_128BIT +#endif + +int16_t saturating_sub(int16_t a, int16_t b) +{ + int32_t result = (int32_t)a - (int32_t)b; + + if (result < INT16_MIN) { + return INT16_MIN; + } else if (result > INT16_MAX) { + return INT16_MAX; + } else { + return (int16_t)result; + } +} + +//---------------------------------------------------------------------------------------------- +// QPSK +//---------------------------------------------------------------------------------------------- +void nr_qpsk_llr(int32_t *rxdataF_comp, int16_t *llr, uint32_t nb_re) +{ + c16_t *rxF = (c16_t *)rxdataF_comp; + c16_t *llr32 = (c16_t *)llr; + for (int i = 0; i < nb_re; i++) { + llr32[i].r = rxF[i].r >> 3; + llr32[i].i = rxF[i].i >> 3; + } +} + +//---------------------------------------------------------------------------------------------- +// 16-QAM +//---------------------------------------------------------------------------------------------- + +void nr_16qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag_in, int16_t *llr, uint32_t nb_re) +{ + simde__m256i *rxF_256 = (simde__m256i *)rxdataF_comp; + simde__m256i *ch_mag = (simde__m256i *)ch_mag_in; + int64_t *llr_64 = (int64_t *)llr; + +#ifndef USE_128BIT + simde__m256i xmm0, xmm1, xmm2; + + for (int i = 0; i < (nb_re >> 3); i++) { + // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| + xmm0 = simde_mm256_abs_epi16(*rxF_256); + // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 + xmm0 = simde_mm256_subs_epi16(*ch_mag, xmm0); + + xmm1 = simde_mm256_unpacklo_epi32(*rxF_256, xmm0); + xmm2 = simde_mm256_unpackhi_epi32(*rxF_256, xmm0); + + // xmm1 |1st 2ed 3rd 4th 9th 10th 13rd 14th| + // xmm2 |5th 6th 7th 8th 11st 12ed 15th 16th| + + *llr_64++ = simde_mm256_extract_epi64(xmm1, 0); + *llr_64++ = simde_mm256_extract_epi64(xmm1, 1); + *llr_64++ = simde_mm256_extract_epi64(xmm2, 0); + *llr_64++ = simde_mm256_extract_epi64(xmm2, 1); + *llr_64++ = simde_mm256_extract_epi64(xmm1, 2); + *llr_64++ = simde_mm256_extract_epi64(xmm1, 3); + *llr_64++ = simde_mm256_extract_epi64(xmm2, 2); + *llr_64++ = simde_mm256_extract_epi64(xmm2, 3); + rxF_256++; + ch_mag++; + } + + nb_re &= 0x7; +#endif + + simde__m128i *rxF_128 = (simde__m128i *)rxF_256; + simde__m128i *ch_mag_128 = (simde__m128i *)ch_mag; + simde__m128i *llr_128 = (simde__m128i *)llr_64; + + // Each iteration does 4 RE (gives 16 16bit-llrs) + for (int i = 0; i < (nb_re >> 2); i++) { + // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| + simde__m128i xmm0 = simde_mm_abs_epi16(*rxF_128); + // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 + xmm0 = simde_mm_subs_epi16(*ch_mag_128, xmm0); + + llr_128[0] = simde_mm_unpacklo_epi32(*rxF_128, xmm0); // llr128[0] contains the llrs of the 1st,2nd,5th and 6th REs + llr_128[1] = simde_mm_unpackhi_epi32(*rxF_128, xmm0); // llr128[1] contains the llrs of the 3rd, 4th, 7th and 8th REs + llr_128 += 2; + rxF_128++; + ch_mag_128++; + } + + simde_mm_empty(); + + nb_re &= 0x3; + int16_t *rxDataF_i16 = (int16_t *)rxF_128; + int16_t *ch_mag_i16 = (int16_t *)ch_mag_128; + int16_t *llr_i16 = (int16_t *)llr_128; + for (uint i = 0U; i < nb_re; i++) { + int16_t real = rxDataF_i16[2 * i]; + int16_t imag = rxDataF_i16[2 * i + 1]; + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr_i16[4 * i] = real; + llr_i16[4 * i + 1] = imag; + llr_i16[4 * i + 2] = saturating_sub(mag_real, abs(real)); + llr_i16[4 * i + 3] = saturating_sub(mag_imag, abs(imag)); + } +} + +//---------------------------------------------------------------------------------------------- +// 64-QAM +//---------------------------------------------------------------------------------------------- + +void nr_64qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int16_t *llr, uint32_t nb_re) +{ + simde__m256i *rxF = (simde__m256i *)rxdataF_comp; + + simde__m256i *ch_maga = (simde__m256i *)ch_mag; + simde__m256i *ch_magb = (simde__m256i *)ch_mag2; + + int32_t *llr_32 = (int32_t *)llr; + +#ifndef USE_128BIT + simde__m256i xmm0, xmm1, xmm2; + for (int i = 0; i < (nb_re >> 3); i++) { + xmm0 = *rxF; + // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| + xmm1 = simde_mm256_abs_epi16(xmm0); + // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 + xmm1 = simde_mm256_subs_epi16(*ch_maga, xmm1); + xmm2 = simde_mm256_abs_epi16(xmm1); + xmm2 = simde_mm256_subs_epi16(*ch_magb, xmm2); + // xmm0 |1st 4th 7th 10th 13th 16th 19th 22ed| + // xmm1 |2ed 5th 8th 11th 14th 17th 20th 23rd| + // xmm2 |3rd 6th 9th 12th 15th 18th 21st 24th| + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 0); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 0); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 0); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 1); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 1); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 1); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 2); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 2); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 2); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 3); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 3); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 3); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 4); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 4); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 4); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 5); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 5); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 5); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 6); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 6); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 6); + + *llr_32++ = simde_mm256_extract_epi32(xmm0, 7); + *llr_32++ = simde_mm256_extract_epi32(xmm1, 7); + *llr_32++ = simde_mm256_extract_epi32(xmm2, 7); + rxF++; + ch_maga++; + ch_magb++; + } + + nb_re &= 0x7; +#endif + + simde__m128i *rxF_128 = (simde__m128i *)rxF; + simde__m128i *ch_mag_128 = (simde__m128i *)ch_maga; + simde__m128i *ch_magb_128 = (simde__m128i *)ch_magb; + + simde__m64 *llr64 = (simde__m64 *)llr_32; + + // Each iteration does 4 RE (gives 24 16bit-llrs) + for (int i = 0; i < (nb_re >> 2); i++) { + simde__m128i xmm0, xmm1, xmm2; + xmm0 = *rxF_128; + xmm1 = simde_mm_abs_epi16(xmm0); + xmm1 = simde_mm_subs_epi16(*ch_mag_128, xmm1); + xmm2 = simde_mm_abs_epi16(xmm1); + xmm2 = simde_mm_subs_epi16(*ch_magb_128, xmm2); + + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm1, 0), simde_mm_extract_epi32(xmm0, 0)); + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm0, 1), simde_mm_extract_epi32(xmm2, 0)); + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm2, 1), simde_mm_extract_epi32(xmm1, 1)); + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm1, 2), simde_mm_extract_epi32(xmm0, 2)); + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm0, 3), simde_mm_extract_epi32(xmm2, 2)); + *llr64++ = simde_mm_set_pi32(simde_mm_extract_epi32(xmm2, 3), simde_mm_extract_epi32(xmm1, 3)); + rxF_128++; + ch_mag_128++; + ch_magb_128++; + } + + nb_re &= 0x3; + + int16_t *rxDataF_i16 = (int16_t *)rxF_128; + int16_t *ch_mag_i16 = (int16_t *)ch_mag_128; + int16_t *ch_magb_i16 = (int16_t *)ch_magb_128; + int16_t *llr_i16 = (int16_t *)llr64; + for (int i = 0; i < nb_re; i++) { + int16_t real = rxDataF_i16[2 * i]; + int16_t imag = rxDataF_i16[2 * i + 1]; + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr_i16[6 * i] = real; + llr_i16[6 * i + 1] = imag; + llr_i16[6 * i + 2] = saturating_sub(mag_real, abs(real)); + llr_i16[6 * i + 3] = saturating_sub(mag_imag, abs(imag)); + int16_t mag_realb = ch_magb_i16[2 * i]; + int16_t mag_imagb = ch_magb_i16[2 * i + 1]; + llr_i16[6 * i + 4] = saturating_sub(mag_realb, abs(llr_i16[6 * i + 2])); + llr_i16[6 * i + 5] = saturating_sub(mag_imagb, abs(llr_i16[6 * i + 3])); + } + simde_mm_empty(); +} + +void nr_256qam_llr(int32_t *rxdataF_comp, int32_t *ch_mag, int32_t *ch_mag2, int32_t *ch_mag3, int16_t *llr, uint32_t nb_re) +{ + simde__m256i *rxF_256 = (simde__m256i *)rxdataF_comp; + simde__m256i *llr256 = (simde__m256i *)llr; + + simde__m256i *ch_maga = (simde__m256i *)ch_mag; + simde__m256i *ch_magb = (simde__m256i *)ch_mag2; + simde__m256i *ch_magc = (simde__m256i *)ch_mag3; +#ifndef USE_128BIT + simde__m256i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; + + for (int i = 0; i < (nb_re >> 3); i++) { + // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| + xmm0 = simde_mm256_abs_epi16(*rxF_256); + // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 + xmm0 = simde_mm256_subs_epi16(*ch_maga, xmm0); + // xmmtmpD2 contains 16 LLRs + xmm1 = simde_mm256_abs_epi16(xmm0); + xmm1 = simde_mm256_subs_epi16(*ch_magb, xmm1); // contains 16 LLRs + xmm2 = simde_mm256_abs_epi16(xmm1); + xmm2 = simde_mm256_subs_epi16(*ch_magc, xmm2); // contains 16 LLRs + // rxF[i] A0 A1 A2 A3 A4 A5 A6 A7 bits 7,6 + // xmm0 B0 B1 B2 B3 B4 B5 B6 B7 bits 5,4 + // xmm1 C0 C1 C2 C3 C4 C5 C6 C7 bits 3,2 + // xmm2 D0 D1 D2 D3 D4 D5 D6 D7 bits 1,0 + xmm3 = simde_mm256_unpacklo_epi32(*rxF_256, xmm0); // A0 B0 A1 B1 A4 B4 A5 B5 + xmm4 = simde_mm256_unpackhi_epi32(*rxF_256, xmm0); // A2 B2 A3 B3 A6 B6 A7 B7 + xmm5 = simde_mm256_unpacklo_epi32(xmm1, xmm2); // C0 D0 C1 D1 C4 D4 C5 D5 + xmm6 = simde_mm256_unpackhi_epi32(xmm1, xmm2); // C2 D2 C3 D3 C6 D6 C7 D7 + + xmm0 = simde_mm256_unpacklo_epi64(xmm3, xmm5); // A0 B0 C0 D0 A4 B4 C4 D4 + xmm1 = simde_mm256_unpackhi_epi64(xmm3, xmm5); // A1 B1 C1 D1 A5 B5 C5 D5 + xmm2 = simde_mm256_unpacklo_epi64(xmm4, xmm6); // A2 B2 C2 D2 A6 B6 C6 D6 + xmm3 = simde_mm256_unpackhi_epi64(xmm4, xmm6); // A3 B3 C3 D3 A7 B7 C7 D7 + *llr256++ = simde_mm256_permute2x128_si256(xmm0, xmm1, 0x20); // A0 B0 C0 D0 A1 B1 C1 D1 + *llr256++ = simde_mm256_permute2x128_si256(xmm2, xmm3, 0x20); // A2 B2 C2 D2 A3 B3 C3 D3 + *llr256++ = simde_mm256_permute2x128_si256(xmm0, xmm1, 0x31); // A4 B4 C4 D4 A5 B5 C5 D5 + *llr256++ = simde_mm256_permute2x128_si256(xmm2, xmm3, 0x31); // A6 B6 C6 D6 A7 B7 C7 D7 + ch_magc++; + ch_magb++; + ch_maga++; + rxF_256++; + } + + nb_re &= 0x7; +#endif + + simde__m128i *rxF_128 = (simde__m128i *)rxF_256; + simde__m128i *llr_128 = (simde__m128i *)llr256; + + simde__m128i *ch_maga_128 = (simde__m128i *)ch_maga; + simde__m128i *ch_magb_128 = (simde__m128i *)ch_magb; + simde__m128i *ch_magc_128 = (simde__m128i *)ch_magc; + + for (int i = 0; i < (nb_re >> 2); i++) { + simde__m128i xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6; + // registers of even index in xmm0-> |y_R|, registers of odd index in xmm0-> |y_I| + xmm0 = simde_mm_abs_epi16(*rxF_128); + // registers of even index in xmm0-> |y_R|-|h|^2, registers of odd index in xmm0-> |y_I|-|h|^2 + xmm0 = simde_mm_subs_epi16(*ch_maga_128, xmm0); + xmm1 = simde_mm_abs_epi16(xmm0); + xmm1 = simde_mm_subs_epi16(*ch_magb_128, xmm1); // contains 8 LLRs + xmm2 = simde_mm_abs_epi16(xmm1); + xmm2 = simde_mm_subs_epi16(*ch_magc_128, xmm2); // contains 8 LLRs + // rxF[i] A0 A1 A2 A3 + // xmm0 B0 B1 B2 B3 + // xmm1 C0 C1 C2 C3 + // xmm2 D0 D1 D2 D3 + xmm3 = simde_mm_unpacklo_epi32(*rxF_128, xmm0); // A0 B0 A1 B1 + xmm4 = simde_mm_unpackhi_epi32(*rxF_128, xmm0); // A2 B2 A3 B3 + xmm5 = simde_mm_unpacklo_epi32(xmm1, xmm2); // C0 D0 C1 D1 + xmm6 = simde_mm_unpackhi_epi32(xmm1, xmm2); // C2 D2 C3 D3 + + *llr_128++ = simde_mm_unpacklo_epi64(xmm3, xmm5); // A0 B0 C0 D0 + *llr_128++ = simde_mm_unpackhi_epi64(xmm3, xmm5); // A1 B1 C1 D1 + *llr_128++ = simde_mm_unpacklo_epi64(xmm4, xmm6); // A2 B2 C2 D2 + *llr_128++ = simde_mm_unpackhi_epi64(xmm4, xmm6); // A3 B3 C3 D3 + + rxF_128++; + ch_maga_128++; + ch_magb_128++; + ch_magc_128++; + } + + if (nb_re & 3) { + for (int i = 0; i < (nb_re & 0x3); i++) { + int16_t *rxDataF_i16 = (int16_t *)rxF_128; + int16_t *ch_mag_i16 = (int16_t *)ch_maga_128; + int16_t *ch_magb_i16 = (int16_t *)ch_magb_128; + int16_t *ch_magc_i16 = (int16_t *)ch_magc_128; + int16_t *llr_i16 = (int16_t *)llr_128; + int16_t real = rxDataF_i16[2 * i + 0]; + int16_t imag = rxDataF_i16[2 * i + 1]; + int16_t mag_real = ch_mag_i16[2 * i]; + int16_t mag_imag = ch_mag_i16[2 * i + 1]; + llr_i16[8 * i] = real; + llr_i16[8 * i + 1] = imag; + llr_i16[8 * i + 2] = saturating_sub(mag_real, abs(real)); + llr_i16[8 * i + 3] = saturating_sub(mag_imag, abs(imag)); + int16_t magb_real = ch_magb_i16[2 * i]; + int16_t magb_imag = ch_magb_i16[2 * i + 1]; + llr_i16[8 * i + 4] = saturating_sub(magb_real, abs(llr_i16[8 * i + 2])); + llr_i16[8 * i + 5] = saturating_sub(magb_imag, abs(llr_i16[8 * i + 3])); + int16_t magc_real = ch_magc_i16[2 * i]; + int16_t magc_imag = ch_magc_i16[2 * i + 1]; + llr_i16[8 * i + 6] = saturating_sub(magc_real, abs(llr_i16[8 * i + 4])); + llr_i16[8 * i + 7] = saturating_sub(magc_imag, abs(llr_i16[8 * i + 5])); + } + } + simde_mm_empty(); +} + void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_signal) { const idft_size_idx_t idft_size = get_idft(ofdm_symbol_size); diff --git a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c index ce2c6c3153..2e69ff7c71 100644 --- a/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c +++ b/openair1/SCHED_NR_UE/phy_procedures_nr_ue.c @@ -509,7 +509,7 @@ static int nr_ue_pdsch_procedures(PHY_VARS_NR_UE *ue, int32_t ptrs_re_per_slot[ue->frame_parms.nb_antennas_rx][NR_SYMBOLS_PER_SLOT]; memset(ptrs_re_per_slot, 0, sizeof(ptrs_re_per_slot)); - const uint32_t rx_size_symbol = dlsch[0].dlsch_config.number_rbs * NR_NB_SC_PER_RB; + const uint32_t rx_size_symbol = (dlsch[0].dlsch_config.number_rbs * NR_NB_SC_PER_RB + 15) & ~15; __attribute__((aligned(32))) int32_t rxdataF_comp[dlsch[0].Nl][ue->frame_parms.nb_antennas_rx][rx_size_symbol * NR_SYMBOLS_PER_SLOT]; memset(rxdataF_comp, 0, sizeof(rxdataF_comp)); diff --git a/openair2/COMMON/ngap_messages_types.h b/openair2/COMMON/ngap_messages_types.h index faeb11d377..861f5e6d2c 100644 --- a/openair2/COMMON/ngap_messages_types.h +++ b/openair2/COMMON/ngap_messages_types.h @@ -278,8 +278,6 @@ typedef struct pdusession_s { transport_layer_addr_t upf_addr; /* Outgoing (UL) NG-U Tunnel Endpoint Identifier (S-GW/UPF) */ uint32_t gtp_teid; - /* Stores the DRB ID of the DRBs used by this PDU Session */ - uint8_t used_drbs[MAX_DRBS_PER_UE]; /* Incoming (DL) NG-U Tunnel Endpoint Identifier (S-GW/UPF) */ uint32_t gNB_teid_N3; transport_layer_addr_t gNB_addr_N3; diff --git a/openair2/F1AP/f1ap_cu_task.c b/openair2/F1AP/f1ap_cu_task.c index a320b92935..72e3d86ea2 100644 --- a/openair2/F1AP/f1ap_cu_task.c +++ b/openair2/F1AP/f1ap_cu_task.c @@ -64,27 +64,17 @@ static void cu_task_handle_sctp_association_ind(instance_t instance, static void cu_task_handle_sctp_association_resp(instance_t instance, sctp_new_association_resp_t *sctp_new_association_resp) { DevAssert(sctp_new_association_resp != NULL); - if (sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN) { + enum sctp_state_e state = sctp_new_association_resp->sctp_state; + if (state != SCTP_STATE_ESTABLISHED) { f1ap_cudu_inst_t *f1ap_cu_data = getCxt(instance); AssertFatal(f1ap_cu_data != NULL, "illegal state: SCTP shutdown for non-existing F1AP endpoint\n"); - LOG_I(F1AP, "Received SCTP shutdown for assoc_id %d, removing endpoint\n", sctp_new_association_resp->assoc_id); + LOG_I(F1AP, "Received SCTP state %d for assoc_id %d, removing endpoint\n", state, sctp_new_association_resp->assoc_id); /* inform RRC that the DU is gone */ MessageDef *message_p = itti_alloc_new_message(TASK_CU_F1, 0, F1AP_LOST_CONNECTION); message_p->ittiMsgHeader.originInstance = sctp_new_association_resp->assoc_id; itti_send_msg_to_task(TASK_RRC_GNB, instance, message_p); return; } - - if (sctp_new_association_resp->sctp_state != SCTP_STATE_ESTABLISHED) { - LOG_W(F1AP, "Received unsuccessful result for SCTP association (%u), instance %ld, cnx_id %u\n", - sctp_new_association_resp->sctp_state, - instance, - sctp_new_association_resp->ulp_cnx_id); - //if (sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN) - //proto_agent_stop(instance); - //f1ap_handle_setup_message(instance, sctp_new_association_resp->sctp_state == SCTP_STATE_SHUTDOWN); - return; // exit -1 for debugging - } } static void cu_task_handle_sctp_data_ind(instance_t instance, sctp_data_ind_t *sctp_data_ind) { diff --git a/openair2/F1AP/f1ap_du_ue_context_management.c b/openair2/F1AP/f1ap_du_ue_context_management.c index 8c32e7bea6..6d9771f1f4 100644 --- a/openair2/F1AP/f1ap_du_ue_context_management.c +++ b/openair2/F1AP/f1ap_du_ue_context_management.c @@ -281,12 +281,9 @@ int DU_handle_UE_CONTEXT_SETUP_REQUEST(instance_t instance, sctp_assoc_t assoc_i memcpy(f1ap_ue_context_setup_req->rrc_container, ieRRC->value.choice.RRCContainer.buf, ieRRC->value.choice.RRCContainer.size); f1ap_ue_context_setup_req->rrc_container_length = ieRRC->value.choice.RRCContainer.size; - // AssertFatal(0, "check configuration, send to appropriate handler\n"); } else { LOG_E(F1AP, " RRCContainer in UEContextSetupRequestIEs size id 0\n"); } - } else { - LOG_W(F1AP, "can't find RRCContainer in UEContextSetupRequestIEs by id %ld \n", F1AP_ProtocolIE_ID_id_RRCContainer); } ue_context_setup_request(f1ap_ue_context_setup_req); @@ -596,6 +593,7 @@ int DU_send_UE_CONTEXT_SETUP_RESPONSE(sctp_assoc_t assoc_id, f1ap_ue_context_set F1AP_SRBs_Setup_Item_t *srbs_setup_item=&srbs_setup_item_ies->value.choice.SRBs_Setup_Item; /* sRBID */ srbs_setup_item->sRBID = resp->srbs_to_be_setup[i].srb_id; + srbs_setup_item->lCID = resp->srbs_to_be_setup[i].lcid; } } diff --git a/openair2/F1AP/f1ap_encoder.c b/openair2/F1AP/f1ap_encoder.c index 870dfd73fc..5de4ba43db 100644 --- a/openair2/F1AP/f1ap_encoder.c +++ b/openair2/F1AP/f1ap_encoder.c @@ -40,13 +40,13 @@ int f1ap_encode_pdu(F1AP_F1AP_PDU_t *pdu, uint8_t **buffer, uint32_t *length) { LOG_E(F1AP, "----------------- ASN1 ENCODER PRINT END----------------- \n"); } - char errbuf[128]; /* Buffer for error message */ + char errbuf[4096]; /* Buffer for error message */ size_t errlen = sizeof(errbuf); /* Size of the buffer */ int ret = asn_check_constraints(&asn_DEF_F1AP_F1AP_PDU, pdu, errbuf, &errlen); - /* assert(errlen < sizeof(errbuf)); // Guaranteed: you may rely on that */ if(ret) { - fprintf(stderr, "Constraint validation failed: %s\n", errbuf); + xer_fprint(stdout, &asn_DEF_F1AP_F1AP_PDU, pdu); + LOG_E(F1AP, "Constraint validation failed: %s\n", errbuf); } encoded = aper_encode_to_new_buffer(&asn_DEF_F1AP_F1AP_PDU, 0, pdu, (void **)buffer); diff --git a/openair2/GNB_APP/gnb_config.c b/openair2/GNB_APP/gnb_config.c index 0dfe99e3cb..0cbe75bbeb 100644 --- a/openair2/GNB_APP/gnb_config.c +++ b/openair2/GNB_APP/gnb_config.c @@ -1598,7 +1598,7 @@ static void fill_neighbour_cell_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc paramdef_t NeighbourCellParams[] = GNBNEIGHBOURCELLPARAMS_DESC; paramlist_def_t NeighbourCellParamList = {GNB_CONFIG_STRING_NEIGHBOUR_CELL_LIST, NULL, 0}; config_getlist(config_get_if(), &NeighbourCellParamList, NeighbourCellParams, sizeofArray(NeighbourCellParams), neighbourpath); - LOG_D(GNB_APP, "HO LOG: For the Cell: %d Neighbour Cell ELM NUM: %d\n", cell->nr_cell_id, NeighbourCellParamList.numelt); + LOG_D(GNB_APP, "HO LOG: For the Cell: %ld Neighbour Cell ELM NUM: %d\n", cell->nr_cell_id, NeighbourCellParamList.numelt); if (NeighbourCellParamList.numelt < 1) continue; @@ -1696,12 +1696,17 @@ static void fill_measurement_configuration(uint8_t gnb_idx, gNB_RRC_INST *rrc) for (int i = 0; i < A3_EventList.numelt; i++) { nr_a3_event_t *a3_event = (nr_a3_event_t *)calloc(1, sizeof(nr_a3_event_t)); AssertFatal(a3_event != NULL, "out of memory\n"); - a3_event->cell_id = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_CELL_ID_IDX].i64ptr; + a3_event->pci = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_PCI_ID_IDX].i64ptr; + AssertFatal(a3_event->pci >= -1 && a3_event->pci < 1024, + "entry %s.%s must be -1<=PCI<1024, but is %d\n", + measurement_path, + MEASUREMENT_EVENTS_PCI_ID, + a3_event->pci); a3_event->timeToTrigger = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_TIMETOTRIGGER_IDX].i64ptr; a3_event->a3_offset = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_OFFSET_IDX].i64ptr; a3_event->hysteresis = *A3_EventList.paramarray[i][MEASUREMENT_EVENTS_HYSTERESIS_IDX].i64ptr; - if (a3_event->cell_id == -1) + if (a3_event->pci == -1) measurementConfig->is_default_a3_configuration_exists = true; seq_arr_push_back(measurementConfig->a3_event_list, a3_event, sizeof(nr_a3_event_t)); diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index a5d8cc5b42..253d94b44a 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -324,12 +324,12 @@ typedef enum { #define MEASUREMENT_EVENTS_THRESHOLD "threshold" #define MEASUREMENT_EVENTS_PERIODICAL_BEAM_MEASUREMENT "includeBeamMeasurements" #define MEASUREMENT_EVENTS_PERIODICAL_NR_OF_RS_INDEXES "maxNrofRS_IndexesToReport" -#define MEASUREMENT_EVENTS_CELL_ID "cell_id" +#define MEASUREMENT_EVENTS_PCI_ID "physCellId" #define MEASUREMENT_EVENT_ENABLE "enable" // clang-format off #define MEASUREMENT_A3_GLOBALPARAMS_DESC \ { \ - {MEASUREMENT_EVENTS_CELL_ID, "neighbour cellId for A3Report", 0, .i64ptr = NULL, .defint64val = -1, TYPE_INT64, 0}, \ + {MEASUREMENT_EVENTS_PCI_ID, "neighbour PCI for A3Report", 0, .i64ptr = NULL, .defint64val = -1, TYPE_INT64, 0}, \ {MEASUREMENT_EVENTS_TIME_TO_TRIGGER, "a3 time to trigger", 0, .i64ptr = NULL, .defint64val = 1, TYPE_INT64, 0}, \ {MEASUREMENT_EVENTS_OFFSET, "a3 offset", 0, .i64ptr = NULL, .defint64val = 60, TYPE_INT64, 0}, \ {MEASUREMENT_EVENTS_HYSTERESIS, "a3 hysteresis", 0, .i64ptr = NULL, .defint64val = 0, TYPE_INT64, 0}, \ @@ -350,7 +350,7 @@ typedef enum { } // clang-format on -#define MEASUREMENT_EVENTS_CELL_ID_IDX 0 +#define MEASUREMENT_EVENTS_PCI_ID_IDX 0 #define MEASUREMENT_EVENTS_ENABLE_IDX 0 #define MEASUREMENT_EVENTS_TIMETOTRIGGER_IDX 1 #define MEASUREMENT_EVENTS_A2_THRESHOLD_IDX 2 diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c index c26e8a5629..c52a67c5a2 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ra_procedures.c @@ -790,7 +790,7 @@ void nr_ue_get_rach(NR_UE_MAC_INST_t *mac, int CC_id, frame_t frame, uint8_t gNB } } - if (is_nr_timer_active(ra->contention_resolution_timer)) { + if (nr_timer_is_active(&ra->contention_resolution_timer)) { nr_ue_contention_resolution(mac, CC_id, frame, nr_slot_tx, prach_resources); } } @@ -851,7 +851,7 @@ void nr_ue_contention_resolution(NR_UE_MAC_INST_t *mac, int cc_id, frame_t frame { RA_config_t *ra = &mac->ra; - if (nr_timer_expired(ra->contention_resolution_timer)) { + if (nr_timer_expired(&ra->contention_resolution_timer)) { ra->t_crnti = 0; nr_timer_stop(&ra->contention_resolution_timer); // Signal PHY to quit RA procedure diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c index ac743c0f60..ccfa861587 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_procedures.c @@ -2543,7 +2543,7 @@ int8_t nr_ue_get_SR(NR_UE_MAC_INST_t *mac, frame_t frame, slot_t slot, NR_Schedu sr_id); // todo // TODO check if the PUCCH resource for the SR transmission occasion does not overlap with a UL-SCH resource - if (!sr_info->pending || is_nr_timer_active(sr_info->prohibitTimer)) + if (!sr_info->pending || nr_timer_is_active(&sr_info->prohibitTimer)) return 0; if (sr_info->counter < sr_info->maxTransmissions) { diff --git a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c index 557a393ce2..e9a2fb58de 100644 --- a/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c +++ b/openair2/LAYER2/NR_MAC_UE/nr_ue_scheduler.c @@ -944,6 +944,7 @@ int nr_config_pusch_pdu(NR_UE_MAC_INST_t *mac, delta_pusch = table_38_213_7_1_1_1[0][dci->tpc]; } } + delta_pusch = 0; // set to 0 as a workaround for PHY not applying PUSCH tx power bool is_rar_tx_retx = rnti_type == TYPE_TC_RNTI_; @@ -1297,7 +1298,7 @@ static void nr_update_sr(NR_UE_MAC_INST_t *mac) // if a Regular BSR has been triggered and logicalChannelSR-DelayTimer is not running if (((sched_info->BSR_reporting_active & NR_BSR_TRIGGER_REGULAR) == 0) - || is_nr_timer_active(sched_info->sr_DelayTimer)) + || nr_timer_is_active(&sched_info->sr_DelayTimer)) return; nr_lcordered_info_t *lc_info = get_lc_info_from_lcid(mac, sched_info->regularBSR_trigger_lcid); @@ -1527,7 +1528,7 @@ void nr_ue_ul_scheduler(NR_UE_MAC_INST_t *mac, nr_uplink_indication_t *ul_info) NR_LC_SCHEDULING_INFO *sched_info = get_scheduling_info_from_lcid(mac, lcid); int32_t bj = sched_info->Bj; if (lc_info->pbr < UINT_MAX) { - uint32_t slots_elapsed = nr_timer_elapsed_time(sched_info->Bj_timer); // slots elapsed since Bj was last incremented + uint32_t slots_elapsed = nr_timer_elapsed_time(&sched_info->Bj_timer); // slots elapsed since Bj was last incremented // it is safe to divide by 1k since pbr in lc_info is computed multiplying by 1000 the RRC value to convert kB/s to B/s uint32_t pbr_ms = lc_info->pbr / 1000; bj += ((pbr_ms * slots_elapsed) >> mac->current_UL_BWP->scs); // each slot length is 1/scs ms diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c index d8f6690818..160cde63f5 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_RA.c @@ -560,7 +560,7 @@ static void start_ra_contention_resolution_timer(NR_RA_t *ra, const long ra_Cont // Value sf8 corresponds to 8 subframes, value sf16 corresponds to 16 subframes, and so on. // We add 2 * K2 because the timer runs from Msg2 transmission till Msg4 ACK reception ra->contention_resolution_timer = ((((int)ra_ContentionResolutionTimer + 1) * 8) << scs) + 2 * K2; - LOG_I(NR_MAC, + LOG_D(NR_MAC, "Starting RA Contention Resolution timer with %d ms + 2 * %d K2 (%d slots) duration\n", ((int)ra_ContentionResolutionTimer + 1) * 8, K2, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c index d0b36a4603..d091d486ff 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c @@ -860,12 +860,7 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, // re-initialize ta update variables after RA procedure completion UE->UE_sched_ctrl.ta_frame = frameP; - LOG_I(NR_MAC, - "[gNB %d][RAPROC] PUSCH with TC_RNTI 0x%04x received correctly, " - "adding UE MAC Context RNTI 0x%04x\n", - gnb_mod_idP, - current_rnti, - ra->rnti); + LOG_A(NR_MAC, "%4d.%2d PUSCH with TC_RNTI 0x%04x received correctly\n", frameP, slotP, current_rnti); NR_UE_sched_ctrl_t *UE_scheduling_control = &UE->UE_sched_ctrl; if (ul_cqi != 0xff) { @@ -884,7 +879,6 @@ static void _nr_rx_sdu(const module_id_t gnb_mod_idP, process_addmod_bearers_cellGroupConfig(&UE->UE_sched_ctrl, ra->CellGroup->rlc_BearerToAddModList); nr_clear_ra_proc(ra); } else { - LOG_A(NR_MAC, "[RAPROC] RA-Msg3 received (sdu_lenP %d)\n", sdu_lenP); LOG_D(NR_MAC, "[RAPROC] Received Msg3:\n"); for (uint32_t k = 0; k < sdu_lenP; k++) { LOG_D(NR_MAC, "(%i): 0x%x\n", k, sduP[k]); diff --git a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c index 2435ed629a..bb84a4793c 100644 --- a/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c +++ b/openair2/LAYER2/NR_MAC_gNB/mac_rrc_dl_handler.c @@ -233,7 +233,8 @@ static int handle_ue_context_srbs_setup(NR_UE_info_t *UE, nr_lc_config_t c = {.lcid = rlc_BearerConfig->logicalChannelIdentity, .priority = priority}; nr_mac_add_lcid(&UE->UE_sched_ctrl, &c); - (*resp_srbs)[i] = *srb; + (*resp_srbs)[i].srb_id = srb->srb_id; + (*resp_srbs)[i].lcid = c.lcid; int ret = ASN_SEQUENCE_ADD(&cellGroupConfig->rlc_BearerToAddModList->list, rlc_BearerConfig); DevAssert(ret == 0); @@ -669,8 +670,12 @@ void ue_context_release_command(const f1ap_ue_context_release_cmd_t *cmd) NR_SCHED_LOCK(&mac->sched_lock); NR_UE_info_t *UE = find_nr_UE(&mac->UE_info, cmd->gNB_DU_ue_id); if (UE == NULL) { - LOG_E(MAC, "ERROR: unknown UE with RNTI %04x, ignoring UE Context Release Command\n", cmd->gNB_DU_ue_id); NR_SCHED_UNLOCK(&mac->sched_lock); + f1ap_ue_context_release_complete_t complete = { + .gNB_CU_ue_id = cmd->gNB_CU_ue_id, + .gNB_DU_ue_id = cmd->gNB_DU_ue_id, + }; + mac->mac_rrc.ue_context_release_complete(&complete); return; } @@ -743,7 +748,7 @@ void dl_rrc_message_transfer(const f1ap_dl_rrc_message_t *dl_rrc) * the new UE context (with new C-RNTI), but set up everything to reuse the * old config. */ NR_UE_info_t *oldUE = find_nr_UE(&mac->UE_info, *dl_rrc->old_gNB_DU_ue_id); - DevAssert(oldUE); + AssertFatal(oldUE, "CU claims we should know UE %04x, but we don't\n", *dl_rrc->old_gNB_DU_ue_id); pthread_mutex_lock(&mac->sched_lock); /* 38.331 5.3.7.2 says that the UE releases the spCellConfig, so we drop it * from the current configuration. Also, expect the reconfiguration from diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.c b/openair2/RRC/NR/MESSAGES/asn1_msg.c index 3849e157c9..25baed610a 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.c +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.c @@ -1165,10 +1165,9 @@ static NR_ReportConfigToAddMod_t *prepare_a2_event_report(const nr_a2_event_t *a static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a3_event) { NR_ReportConfigToAddMod_t *rc_A3 = calloc(1, sizeof(*rc_A3)); - rc_A3->reportConfigId = - a3_event->cell_id == -1 - ? 3 - : a3_event->cell_id + 4; // 3 is default A3 Report Config ID. So cellId(0) specific Report Config ID starts from 4 + // 3 is default A3 Report Config ID. So cellId(0) specific Report Config ID + // starts from 4 + rc_A3->reportConfigId = a3_event->pci == -1 ? 3 : a3_event->pci + 4; rc_A3->reportConfig.present = NR_ReportConfigToAddMod__reportConfig_PR_reportConfigNR; NR_EventTriggerConfig_t *etrc_A3 = calloc(1, sizeof(*etrc_A3)); etrc_A3->eventId.present = NR_EventTriggerConfig__eventId_PR_eventA3; @@ -1194,7 +1193,7 @@ static NR_ReportConfigToAddMod_t *prepare_a3_event_report(const nr_a3_event_t *a return rc_A3; } -const nr_a3_event_t *get_a3_configuration(int nr_cellid) +const nr_a3_event_t *get_a3_configuration(int pci) { gNB_RRC_INST *rrc = RC.nrrrc[0]; nr_measurement_configuration_t *measurementConfiguration = &rrc->measurementConfiguration; @@ -1203,7 +1202,7 @@ const nr_a3_event_t *get_a3_configuration(int nr_cellid) for (uint8_t i = 0; i < measurementConfiguration->a3_event_list->size; i++) { nr_a3_event_t *a3_event = (nr_a3_event_t *)seq_arr_at(measurementConfiguration->a3_event_list, i); - if (a3_event->cell_id == nr_cellid) + if (a3_event->pci == pci) return a3_event; } @@ -1262,11 +1261,11 @@ NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, if (!neighbourCell->isIntraFrequencyNeighbour) continue; - const nr_a3_event_t *a3Event = get_a3_configuration(neighbourCell->nrcell_id); + const nr_a3_event_t *a3Event = get_a3_configuration(neighbourCell->physicalCellId); if (!a3Event || is_default_a3_added) continue; - if (a3Event->cell_id == -1) + if (a3Event->pci == -1) is_default_a3_added = true; NR_ReportConfigToAddMod_t *rc_A3 = prepare_a3_event_report(a3Event); @@ -1368,7 +1367,7 @@ int do_NR_Paging(uint8_t Mod_id, uint8_t *buffer, uint32_t tmsi) LOG_D(NR_RRC, "[gNB %d] do_Paging paging_record: PagingRecordList.count %d\n", Mod_id, c1->choice.paging->pagingRecordList->list.count); asn_enc_rval_t enc_rval = uper_encode_to_buffer( - &asn_DEF_NR_PCCH_Message, NULL, (void *)&pcch_msg, buffer, RRC_BUF_SIZE); + &asn_DEF_NR_PCCH_Message, NULL, (void *)&pcch_msg, buffer, NR_RRC_BUF_SIZE); if ( LOG_DEBUGFLAG(DEBUG_ASN1) ) { xer_fprint(stdout, &asn_DEF_NR_PCCH_Message, (void *)&pcch_msg); diff --git a/openair2/RRC/NR/MESSAGES/asn1_msg.h b/openair2/RRC/NR/MESSAGES/asn1_msg.h index 0d8b4c547f..623573d24f 100644 --- a/openair2/RRC/NR/MESSAGES/asn1_msg.h +++ b/openair2/RRC/NR/MESSAGES/asn1_msg.h @@ -143,7 +143,7 @@ int do_RRCReestablishment(rrc_gNB_ue_context_t *const ue_context_pP, int do_RRCReestablishmentComplete(uint8_t *buffer, size_t buffer_size, int64_t rrc_TransactionIdentifier); -const nr_a3_event_t *get_a3_configuration(int nr_cellid); +const nr_a3_event_t *get_a3_configuration(int pci); NR_MeasConfig_t *get_MeasConfig(const NR_MeasTiming_t *mt, int band, int scs, diff --git a/openair2/RRC/NR/nr_rrc_common.h b/openair2/RRC/NR/nr_rrc_common.h index e1ff2e703e..305013ecf4 100644 --- a/openair2/RRC/NR/nr_rrc_common.h +++ b/openair2/RRC/NR/nr_rrc_common.h @@ -26,17 +26,9 @@ #include #include -#define NR_RRC_HEADER_SIZE_MAX 64 -#define NR_RRC_BUFFER_SIZE_MAX 1024 #define NR_NUM_SRB 4 /* Number of Signalling Radio Bearers according to clause 4.2.2 of 3GPP TS 38.331 */ #define NR_K_KEY_SIZE 16 /* K keys have 128 bits length */ -typedef struct { - char Payload[NR_RRC_BUFFER_SIZE_MAX]; - char Header[NR_RRC_HEADER_SIZE_MAX]; - uint16_t payload_size; -} NR_RRC_BUFFER; - typedef enum UE_STATE_NR_e { NR_RRC_INACTIVE=0, NR_RRC_IDLE, diff --git a/openair2/RRC/NR/nr_rrc_config.c b/openair2/RRC/NR/nr_rrc_config.c index e5f0c295b5..5631da4c0e 100644 --- a/openair2/RRC/NR/nr_rrc_config.c +++ b/openair2/RRC/NR/nr_rrc_config.c @@ -2805,6 +2805,8 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid, && servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count > 0) { n_dl_bwp = servingcellconfigdedicated->downlinkBWP_ToAddModList->list.count; } + int default_dl_bwp = 0; + int first_active_dl_bwp = 0; if (n_dl_bwp > 0) { SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList = calloc(1, sizeof(*SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList)); @@ -2813,15 +2815,13 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid, config_downlinkBWP(bwp, scc, servingcellconfigdedicated, NULL, 0, false, bwp_loop, true); asn1cSeqAdd(&SpCellConfig->spCellConfigDedicated->downlinkBWP_ToAddModList->list, bwp); } - SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = - calloc(1, sizeof(*SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id = - servingcellconfigdedicated->firstActiveDownlinkBWP_Id ? *servingcellconfigdedicated->firstActiveDownlinkBWP_Id : 1; - SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = - calloc(1, sizeof(*SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id)); - *SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id = - servingcellconfigdedicated->defaultDownlinkBWP_Id ? *servingcellconfigdedicated->defaultDownlinkBWP_Id : 1; + const NR_BWP_Id_t *firstActiveDownlinkBWP_Id = servingcellconfigdedicated->firstActiveDownlinkBWP_Id; + first_active_dl_bwp = firstActiveDownlinkBWP_Id ? *firstActiveDownlinkBWP_Id : 1; + const NR_BWP_Id_t *defaultDownlinkBWP_Id = servingcellconfigdedicated->defaultDownlinkBWP_Id; + default_dl_bwp = defaultDownlinkBWP_Id ? *defaultDownlinkBWP_Id : 1; } + asn1cCallocOne(SpCellConfig->spCellConfigDedicated->firstActiveDownlinkBWP_Id, first_active_dl_bwp); + asn1cCallocOne(SpCellConfig->spCellConfigDedicated->defaultDownlinkBWP_Id, default_dl_bwp); // Uplink BWPs int n_ul_bwp = 0; @@ -2830,6 +2830,7 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid, && servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count > 0) { n_ul_bwp = servingcellconfigdedicated->uplinkConfig->uplinkBWP_ToAddModList->list.count; } + int first_active_ul_bwp = 0; if (n_ul_bwp > 0) { uplinkConfig->uplinkBWP_ToAddModList = calloc(1, sizeof(*uplinkConfig->uplinkBWP_ToAddModList)); for (int bwp_loop = 0; bwp_loop < n_ul_bwp; bwp_loop++) { @@ -2837,11 +2838,10 @@ static NR_SpCellConfig_t *get_initial_SpCellConfig(int uid, config_uplinkBWP(ubwp, bwp_loop, true, uid, configuration, servingcellconfigdedicated, scc, NULL); asn1cSeqAdd(&uplinkConfig->uplinkBWP_ToAddModList->list, ubwp); } - uplinkConfig->firstActiveUplinkBWP_Id = calloc(1, sizeof(*uplinkConfig->firstActiveUplinkBWP_Id)); - *uplinkConfig->firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id - ? *servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id - : 1; + const NR_BWP_Id_t *firstActiveUplinkBWP_Id = servingcellconfigdedicated->uplinkConfig->firstActiveUplinkBWP_Id; + first_active_ul_bwp = firstActiveUplinkBWP_Id ? *firstActiveUplinkBWP_Id : 1; } + asn1cCallocOne(uplinkConfig->firstActiveUplinkBWP_Id, first_active_ul_bwp); SpCellConfig->spCellConfigDedicated->csi_MeasConfig = calloc(1, sizeof(*SpCellConfig->spCellConfigDedicated->csi_MeasConfig)); SpCellConfig->spCellConfigDedicated->csi_MeasConfig->present = NR_SetupRelease_CSI_MeasConfig_PR_setup; diff --git a/openair2/RRC/NR/nr_rrc_defs.h b/openair2/RRC/NR/nr_rrc_defs.h index a9234ed5bd..b5b27a6f4d 100644 --- a/openair2/RRC/NR/nr_rrc_defs.h +++ b/openair2/RRC/NR/nr_rrc_defs.h @@ -88,15 +88,6 @@ #define NR_RRC_RECONFIGURATION_DELAY_MS 10 #define NR_RRC_BWP_SWITCHING_DELAY_MS 6 -// 3GPP TS 38.133 - Section 8 - Table 8.2.1.2.7-2: Parameters which cause interruption other than SCS -// This table was recently added to 3GPP. It shows that changing the parameters locationAndBandwidth, nrofSRS-Ports or -// maxMIMO-Layers-r16 causes an interruption. This parameter is not yet being used in code, but has been placed here -// for future reference. -#define NR_OF_SRS_PORTS_SWITCHING_DELAY_MS 30 - -#define NR_UE_MODULE_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! -#define NR_UE_INDEX_INVALID ((module_id_t) ~0) // FIXME attention! depends on type uint8_t!!! used to be -1 - typedef enum { NR_RRC_OK=0, NR_RRC_ConnSetup_failed, @@ -105,17 +96,11 @@ typedef enum { NR_RRC_HO_STARTED } NR_RRC_status_t; -#define RRM_FREE(p) if ( (p) != NULL) { free(p) ; p=NULL ; } -#define RRM_MALLOC(t,n) (t *) malloc16( sizeof(t) * n ) -#define RRM_CALLOC(t,n) (t *) malloc16( sizeof(t) * n) -#define RRM_CALLOC2(t,s) (t *) malloc16( s ) - #define MAX_MEAS_OBJ 7 #define MAX_MEAS_CONFIG 7 #define MAX_MEAS_ID 7 -#define PAYLOAD_SIZE_MAX 1024 -#define RRC_BUF_SIZE 1024 +#define NR_RRC_BUF_SIZE 4096 #define UNDEF_SECURITY_MODE 0xff #define NO_SECURITY_MODE 0x20 @@ -129,14 +114,6 @@ typedef struct UE_S_TMSI_NR_s { uint32_t fiveg_tmsi; } __attribute__ ((__packed__)) NR_UE_S_TMSI; - -typedef enum nr_e_rab_satus_e { - NR_E_RAB_STATUS_NEW, - NR_E_RAB_STATUS_DONE, // from the gNB perspective - NR_E_RAB_STATUS_ESTABLISHED, // get the reconfigurationcomplete form UE - NR_E_RAB_STATUS_FAILED, -} nr_e_rab_status_t; - typedef struct nr_e_rab_param_s { e_rab_t param; uint8_t status; @@ -156,12 +133,10 @@ typedef struct HANDOVER_INFO_NR_s { //AS_Config_t as_config; /* these two parameters are taken from 36.331 section 10.2.2: HandoverPreparationInformation-r8-IEs */ //AS_Context_t as_context; /* They are mandatory for HO */ - uint8_t buf[RRC_BUF_SIZE]; /* ASN.1 encoded handoverCommandMessage */ + uint8_t buf[NR_RRC_BUF_SIZE]; /* ASN.1 encoded handoverCommandMessage */ int size; /* size of above message in bytes */ } NR_HANDOVER_INFO; -#define NR_RRC_BUFFER_SIZE sizeof(RRC_BUFFER_NR) - typedef struct nr_rrc_guami_s { uint16_t mcc; uint16_t mnc; @@ -194,19 +169,15 @@ typedef struct pdu_session_param_s { * @brief F1-U tunnel configuration */ typedef struct f1u_tunnel_s { - /* Downlink F1-U Tunnel Endpoint Identifier (CU-UP/DU) */ - uint32_t cuup_teid_f1u; - /* DL F1-U Transport Layer */ - transport_layer_addr_t cuup_addr_f1u; + /* F1-U Tunnel Endpoint Identifier (on DU side) */ + uint32_t teid; + /* Downlink F1-U Transport Layer (on DU side) */ + transport_layer_addr_t addr; } f1u_tunnel_t; typedef struct drb_s { int status; - int defaultDRBid; int drb_id; - int reestablishPDCP; - int recoverPDCP; - int daps_Config_r16; struct cnAssociation_s { int present; int eps_BearerIdentity; @@ -232,8 +203,10 @@ typedef struct drb_s { int cipheringDisabled; } ext1; } pdcp_config; - // F1-U - f1u_tunnel_t f1u_tunnel_config; + // F1-U Downlink Tunnel Config (on DU side) + f1u_tunnel_t du_tunnel_config; + // F1-U Uplink Tunnel Config (on CU-UP side) + f1u_tunnel_t cuup_tunnel_config; } drb_t; typedef enum { @@ -284,7 +257,6 @@ typedef struct gNB_RRC_UE_s { NR_CipheringAlgorithm_t ciphering_algorithm; e_NR_IntegrityProtAlgorithm integrity_algorithm; - NR_UE_STATE_t StatusRrc; rnti_t rnti; uint64_t random_ue_identity; @@ -317,6 +289,8 @@ typedef struct gNB_RRC_UE_s { uint32_t ue_reestablishment_counter; uint32_t ue_reconfiguration_counter; + bool an_release; // flag if core requested UE release + /* NGUEContextSetup might come with PDU sessions, but setup needs to be * delayed after security (and capability); PDU sessions are stored here */ int n_initial_pdu; @@ -368,7 +342,7 @@ typedef struct { } nr_a2_event_t; typedef struct { - int cell_id; + int pci; long a3_offset; long hysteresis; long timeToTrigger; @@ -393,7 +367,7 @@ typedef struct { } nr_neighbour_gnb_configuration_t; typedef struct neighbour_cell_configuration_s { - int nr_cell_id; + uint64_t nr_cell_id; seq_arr_t *neighbour_cells; } neighbour_cell_configuration_t; diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index f9646468e0..f7992d9757 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -113,9 +113,9 @@ mui_t rrc_gNB_mui = 0; // the assoc_id might be 0 (if the DU goes offline). Below helper macro to // print an error and return from the function in that case -#define RETURN_IF_INVALID_ASSOC_ID(ue_data) \ +#define RETURN_IF_INVALID_ASSOC_ID(assoc_id) \ { \ - if (ue_data.du_assoc_id == 0) { \ + if (assoc_id == 0) { \ LOG_E(NR_RRC, "cannot send data: invalid assoc_id 0, DU offline\n"); \ return; \ } \ @@ -197,7 +197,7 @@ void nr_rrc_transfer_protected_rrc_message(const gNB_RRC_INST *rrc, { DevAssert(size > 0); f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, .srb_id = srb_id}; deliver_dl_rrc_message_data_t data = {.rrc = rrc, .dl_rrc = &dl_rrc, .assoc_id = ue_data.du_assoc_id}; nr_pdcp_data_req_srb(ue_p->rrc_ue_id, @@ -345,7 +345,6 @@ static NR_DRB_ToAddModList_t *createDRBlist(gNB_RRC_UE_t *ue, bool reestablish) if (ue->established_drbs[i].status != DRB_INACTIVE) { DRB_config = generateDRB_ASN1(&ue->established_drbs[i]); if (reestablish) { - ue->established_drbs[i].reestablishPDCP = NR_DRB_ToAddMod__reestablishPDCP_true; asn1cCallocOne(DRB_config->reestablishPDCP, NR_DRB_ToAddMod__reestablishPDCP_true); } asn1cSeqAdd(&DRB_configList->list, DRB_config); @@ -424,7 +423,7 @@ static void rrc_gNB_generate_RRCSetup(instance_t instance, LOG_DUMPMSG(NR_RRC, DEBUG_RRC, (char *)buf, size, "[MSG] RRC Setup\n"); freeSRBlist(SRBs); f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_dl_rrc_message_t dl_rrc = { .gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, @@ -454,7 +453,7 @@ static void rrc_gNB_generate_RRCReject(module_id_t module_id, rrc_gNB_ue_context LOG_I(NR_RRC, " [RAPROC] ue %04x Logical Channel DL-CCCH, Generating NR_RRCReject (bytes %d)\n", ue_p->rnti, size); f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_dl_rrc_message_t dl_rrc = { .gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, @@ -477,7 +476,6 @@ static void rrc_gNB_process_RRCSetupComplete(const protocol_ctxt_t *const ctxt_p LOG_A(NR_RRC, "UE %d Processing NR_RRCSetupComplete from UE\n", ue_context_pP->ue_context.rrc_ue_id); ue_context_pP->ue_context.Srb[1].Active = 1; ue_context_pP->ue_context.Srb[2].Active = 0; - ue_context_pP->ue_context.StatusRrc = NR_RRC_CONNECTED; AssertFatal(ctxt_pP->rntiMaybeUEid == ue_context_pP->ue_context.rrc_ue_id, "logic bug: inconsistent IDs, must use CU UE ID!\n"); rrc_gNB_send_NGAP_NAS_FIRST_REQ(ctxt_pP, ue_context_pP, rrcSetupComplete); @@ -549,13 +547,13 @@ void rrc_gNB_generate_dedicatedRRCReconfiguration(const protocol_ctxt_t *const c ue_p->measConfig = measconfig; - uint8_t buffer[RRC_BUF_SIZE] = {0}; + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; NR_SRB_ToAddModList_t *SRBs = createSRBlist(ue_p, false); NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false); int size = do_RRCReconfiguration(ue_p, buffer, - RRC_BUF_SIZE, + NR_RRC_BUF_SIZE, xid, SRBs, DRBs, @@ -676,10 +674,10 @@ rrc_gNB_modify_dedicatedRRCReconfiguration( } NR_DRB_ToAddModList_t *DRBs = createDRBlist(ue_p, false); - uint8_t buffer[RRC_BUF_SIZE]; + uint8_t buffer[NR_RRC_BUF_SIZE]; int size = do_RRCReconfiguration(ue_p, buffer, - RRC_BUF_SIZE, + NR_RRC_BUF_SIZE, xid, NULL, DRBs, @@ -742,10 +740,10 @@ rrc_gNB_generate_dedicatedRRCReconfiguration_release( LOG_W(NR_RRC,"dedlicated NAS list is empty\n"); } - uint8_t buffer[RRC_BUF_SIZE] = {0}; + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; int size = do_RRCReconfiguration(ue_p, buffer, - RRC_BUF_SIZE, + NR_RRC_BUF_SIZE, xid, NULL, NULL, @@ -808,7 +806,8 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) return; /* loop through active DRBs */ for (int drb_id = 1; drb_id <= MAX_DRBS_PER_UE; drb_id++) { - if (ue_p->established_drbs[drb_id - 1].status == DRB_INACTIVE) + drb_t *drb = get_drb(ue_p, drb_id); + if (drb->status == DRB_INACTIVE) continue; /* fetch an existing PDU session for this DRB */ rrc_pdu_session_param_t *pdu = find_pduSession_from_drbId(ue_p, drb_id); @@ -829,13 +828,11 @@ static void cuup_notify_reestablishment(gNB_RRC_INST *rrc, gNB_RRC_UE_t *ue_p) DRB_nGRAN_to_mod_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify]; drb_e1->id = drb_id; drb_e1->numDlUpParam = 1; - drb_t *drbs = &ue_p->established_drbs[drb_id]; - memcpy(&drb_e1->DlUpParamList[0].tlAddress, &drbs->f1u_tunnel_config.cuup_addr_f1u.buffer, sizeof(uint8_t) * 4); - drb_e1->DlUpParamList[0].teId = drbs->f1u_tunnel_config.cuup_teid_f1u; + memcpy(&drb_e1->DlUpParamList[0].tlAddress, &drb->du_tunnel_config.addr.buffer, sizeof(uint8_t) * 4); + drb_e1->DlUpParamList[0].teId = drb->du_tunnel_config.teid; /* PDCP configuration */ bearer_context_pdcp_config_t *pdcp_config = &drb_e1->pdcp_config; - drb_t *rrc_drb = get_drb(ue_p, drb_id); - set_bearer_context_pdcp_config(pdcp_config, rrc_drb, rrc->configuration.um_on_default_drb); + set_bearer_context_pdcp_config(pdcp_config, drb, rrc->configuration.um_on_default_drb); pdcp_config->pDCP_Reestablishment = true; /* increase DRB to modify counter */ pdu_e1->numDRB2Modify += 1; @@ -863,12 +860,12 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context module_id_t module_id = 0; gNB_RRC_INST *rrc = RC.nrrrc[module_id]; gNB_RRC_UE_t *ue_p = &ue_context_pP->ue_context; - uint8_t buffer[RRC_BUF_SIZE] = {0}; + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; uint8_t xid = rrc_gNB_get_next_transaction_identifier(module_id); ue_p->xids[xid] = RRC_REESTABLISH; const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; uint32_t ssb_arfcn = get_ssb_arfcn(du); - int size = do_RRCReestablishment(ue_context_pP, buffer, RRC_BUF_SIZE, xid, cell_info->nr_pci, ssb_arfcn); + int size = do_RRCReestablishment(ue_context_pP, buffer, NR_RRC_BUF_SIZE, xid, cell_info->nr_pci, ssb_arfcn); LOG_I(NR_RRC, "[RAPROC] UE %04x Logical Channel DL-DCCH, Generating NR_RRCReestablishment (bytes %d)\n", ue_p->rnti, size); @@ -902,7 +899,7 @@ static void rrc_gNB_generate_RRCReestablishment(rrc_gNB_ue_context_t *ue_context &security_parameters); /* F1AP DL RRC Message Transfer */ f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); uint32_t old_gNB_DU_ue_id = old_rnti; f1ap_dl_rrc_message_t dl_rrc = {.gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, @@ -935,7 +932,6 @@ static void rrc_gNB_process_RRCReestablishmentComplete(const protocol_ctxt_t *co int i = 0; ue_p->xids[xid] = RRC_ACTION_NONE; - ue_p->StatusRrc = NR_RRC_CONNECTED; ue_p->Srb[1].Active = 1; @@ -987,10 +983,10 @@ static void rrc_gNB_process_RRCReestablishmentComplete(const protocol_ctxt_t *co uint8_t new_xid = rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id); ue_p->xids[new_xid] = RRC_REESTABLISH_COMPLETE; - uint8_t buffer[RRC_BUF_SIZE] = {0}; + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; int size = do_RRCReconfiguration(ue_p, buffer, - RRC_BUF_SIZE, + NR_RRC_BUF_SIZE, new_xid, SRBs, DRBs, @@ -1031,8 +1027,8 @@ int nr_rrc_reconfiguration_req(rrc_gNB_ue_context_t *const ue_context_pP, *masterCellGroup->spCellConfig->spCellConfigDedicated->uplinkConfig->firstActiveUplinkBWP_Id = ul_bwp_id; } - uint8_t buffer[RRC_BUF_SIZE]; - int size = do_RRCReconfiguration(ue_p, buffer, RRC_BUF_SIZE, xid, NULL, NULL, NULL, NULL, NULL, NULL, masterCellGroup); + uint8_t buffer[NR_RRC_BUF_SIZE]; + int size = do_RRCReconfiguration(ue_p, buffer, NR_RRC_BUF_SIZE, xid, NULL, NULL, NULL, NULL, NULL, NULL, masterCellGroup); gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; nr_rrc_transfer_protected_rrc_message(rrc, ue_p, DCCH, buffer, size); @@ -1120,7 +1116,9 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, uint64_t random_value = 0; const char *scause = get_reestab_cause(req->reestablishmentCause); const long physCellId = req->ue_Identity.physCellId; - LOG_D(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause); + long ngap_cause = NGAP_CAUSE_RADIO_NETWORK_UNSPECIFIED; /* cause in case of NGAP release req */ + rrc_gNB_ue_context_t *ue_context_p = NULL; + LOG_I(NR_RRC, "UE %04x physCellId %ld NR_RRCReestablishmentRequest cause %s\n", msg->crnti, physCellId, scause); const nr_rrc_du_container_t *du = get_du_by_assoc_id(rrc, assoc_id); if (du == NULL) { @@ -1128,6 +1126,13 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, return; } + // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values + if (req->ue_Identity.c_RNTI < 0x1 || req->ue_Identity.c_RNTI > 0xffef) { + /* c_RNTI range error should not happen */ + LOG_E(NR_RRC, "NR_RRCReestablishmentRequest c_RNTI %04lx range error, fallback to RRC setup\n", req->ue_Identity.c_RNTI); + goto fallback_rrc_setup; + } + if (du->mib == NULL || du->sib1 == NULL) { /* we don't have MIB/SIB1 of the DU, and therefore cannot generate the * Reestablishment (as we would need the SSB's ARFCN, which we cannot @@ -1136,12 +1141,22 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, goto fallback_rrc_setup; } + if (du->mtc == NULL) { + // some UEs don't send MeasurementTimingConfiguration, so we don't know the + // SSB ARFCN and can't do reestablishment. handle it gracefully by doing + // RRC setup procedure instead + LOG_E(NR_RRC, "no MeasurementTimingConfiguration for this cell, cannot perform reestablishment\n"); + ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON; + goto fallback_rrc_setup; + } + rnti_t old_rnti = req->ue_Identity.c_RNTI; - rrc_gNB_ue_context_t *ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti); + ue_context_p = rrc_gNB_get_ue_context_by_rnti(rrc, assoc_id, old_rnti); if (ue_context_p == NULL) { LOG_E(NR_RRC, "NR_RRCReestablishmentRequest without UE context, fallback to RRC setup\n"); goto fallback_rrc_setup; } + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; const f1ap_served_cell_info_t *cell_info = &du->setup_req->cell[0].info; if (physCellId != cell_info->nr_pci) { @@ -1150,6 +1165,7 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, "RRC Reestablishment Request from different physCellId (%ld) than current physCellId (%d), fallback to RRC setup\n", physCellId, cell_info->nr_pci); + ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON; /* 38.401 8.7: "If the UE accessed from a gNB-DU other than the original * one, the gNB-CU should trigger the UE Context Setup procedure". Let's * assume that the old DU will trigger a release request, also freeing the @@ -1157,25 +1173,10 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, goto fallback_rrc_setup; } - gNB_RRC_UE_t *UE = &ue_context_p->ue_context; - // 3GPP TS 38.321 version 15.13.0 Section 7.1 Table 7.1-1: RNTI values - if (req->ue_Identity.c_RNTI < 0x1 || req->ue_Identity.c_RNTI > 0xffef) { - /* c_RNTI range error should not happen */ - LOG_E(NR_RRC, "NR_RRCReestablishmentRequest c_RNTI %04lx range error, fallback to RRC setup\n", req->ue_Identity.c_RNTI); - goto fallback_rrc_setup; - } - if (!UE->as_security_active) { /* no active security context, need to restart entire connection */ LOG_E(NR_RRC, "UE requested Reestablishment without activated AS security\n"); - goto fallback_rrc_setup; - } - - if (du->mtc == NULL) { - // some UEs don't send MeasurementTimingConfiguration, so we don't know the - // SSB ARFCN and can't do reestablishment. handle it gracefully by doing - // RRC setup procedure instead - LOG_E(NR_RRC, "no MeasurementTimingConfiguration for this cell, cannot perform reestablishment\n"); + ngap_cause = NGAP_CAUSE_RADIO_NETWORK_RELEASE_DUE_TO_NGRAN_GENERATED_REASON; goto fallback_rrc_setup; } @@ -1188,8 +1189,6 @@ static void rrc_handle_RRCReestablishmentRequest(gNB_RRC_INST *rrc, cu_remove_f1_ue_data(UE->rrc_ue_id); cu_add_f1_ue_data(UE->rrc_ue_id, &ue_data); - LOG_I(NR_RRC, "Accept Reestablishment Request UE physCellId %ld cause %s\n", physCellId, scause); - rrc_gNB_generate_RRCReestablishment(ue_context_p, msg->du2cu_rrc_container, old_rnti, du); return; @@ -1197,6 +1196,10 @@ fallback_rrc_setup: fill_random(&random_value, sizeof(random_value)); random_value = random_value & 0x7fffffffff; /* random value is 39 bits */ + /* request release of the "old" UE in case it exists */ + if (ue_context_p != NULL) + rrc_gNB_send_NGAP_UE_CONTEXT_RELEASE_REQ(0, ue_context_p, NGAP_CAUSE_RADIO_NETWORK, ngap_cause); + rrc_gNB_ue_context_t *new = rrc_gNB_create_ue_context(assoc_id, msg->crnti, rrc, random_value, msg->gNB_DU_ue_id); activate_srb(&new->ue_context, 1); rrc_gNB_generate_RRCSetup(0, msg->crnti, new, msg->du2cu_rrc_container, msg->du2cu_rrc_container_length); @@ -1228,11 +1231,11 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, switch (event_triggered->eventId.present) { case NR_EventTriggerConfig__eventId_PR_eventA2: - LOG_I(NR_RRC, "\nHO LOG: Event A2 (Serving becomes worse than threshold)\n"); + LOG_I(NR_RRC, "HO LOG: Event A2 (Serving becomes worse than threshold)\n"); break; case NR_EventTriggerConfig__eventId_PR_eventA3: { - LOG_I(NR_RRC, "\nHO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n"); + LOG_I(NR_RRC, "HO LOG: Event A3 Report - Neighbour Becomes Better than Serving!\n"); const NR_MeasResults_t *measResults = &measurementReport->criticalExtensions.choice.measurementReport->measResults; for (int serving_cell_idx = 0; serving_cell_idx < measResults->measResultServingMOList.list.count; serving_cell_idx++) { @@ -1267,7 +1270,7 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, } LOG_I(NR_RRC, - "HO LOG: Measurement Report has came for the neighbour: %d with RSRP: %d\n", + "HO LOG: Measurement Report for the neighbour %d with RSRP: %d\n", neighbourCellId, neighbourCellRSRP); @@ -1279,7 +1282,7 @@ static void process_Event_Based_Measurement_Report(NR_ReportConfigNR_t *report, // neighbour. if (!neighbour_cell_du_context && neighbour) { // No F1 connection but static neighbour configuration is available - const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->nrcell_id); + const nr_a3_event_t *a3_event_configuration = get_a3_configuration(neighbour->physicalCellId); // Additional check - This part can be modified according to additional cell specific Handover Margin if (a3_event_configuration && ((a3_event_configuration->a3_offset + a3_event_configuration->hysteresis) @@ -1864,15 +1867,39 @@ static void fill_e1_bearer_modif(DRB_nGRAN_to_setup_t *drb_e1, const f1ap_drb_to /** * @brief Store F1-U DL TL and TEID in RRC */ -static void f1u_gtp_update(f1u_tunnel_t *f1u, const f1ap_drb_to_be_setup_t *drb_f1) +static void f1u_dl_gtp_update(f1u_tunnel_t *f1u, const f1ap_up_tnl_t *p) { - f1u->cuup_teid_f1u = drb_f1->up_dl_tnl[0].teid; - memcpy(&f1u->cuup_addr_f1u.buffer, &drb_f1->up_dl_tnl[0].tl_address, sizeof(uint8_t) * 4); - f1u->cuup_addr_f1u.length = sizeof(in_addr_t); + f1u->teid = p->teid; + memcpy(&f1u->addr.buffer, &p->tl_address, sizeof(uint8_t) * 4); + f1u->addr.length = sizeof(in_addr_t); +} + +/** + * @brief Update DRB TEID information in RRC storage from received DRB list + */ +static void store_du_f1u_tunnel(const f1ap_drb_to_be_setup_t *drbs, int n, gNB_RRC_UE_t *ue) +{ + for (int i = 0; i < n; i++) { + const f1ap_drb_to_be_setup_t *drb_f1 = &drbs[i]; + AssertFatal(drb_f1->up_dl_tnl_length == 1, "can handle only one UP param\n"); + AssertFatal(drb_f1->drb_id < MAX_DRBS_PER_UE, "illegal DRB ID %ld\n", drb_f1->drb_id); + drb_t *drb = get_drb(ue, drb_f1->drb_id); + f1u_dl_gtp_update(&drb->du_tunnel_config, &drb_f1->up_dl_tnl[0]); + } +} + +/* + * @brief Store F1-U UL TEID and address in RRC + */ +static void f1u_ul_gtp_update(f1u_tunnel_t *f1u, const up_params_t *p) +{ + f1u->teid = p->teId; + memcpy(&f1u->addr.buffer, &p->tlAddress, sizeof(uint8_t) * 4); + f1u->addr.length = sizeof(in_addr_t); } /* \brief use list of DRBs and send the corresponding bearer update message via - * E1 to the CU of this UE */ + * E1 to the CU of this UE. Also updates TEID info internally */ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f1ap_drb_to_be_setup_t *drbs) { // we assume the same UE ID in CU-UP and CU-CP @@ -1892,7 +1919,6 @@ static void e1_send_bearer_updates(gNB_RRC_INST *rrc, gNB_RRC_UE_t *UE, int n, f DevAssert(pdu_e1 != NULL); pdu_e1->sessionId = pdu_ue->param.pdusession_id; DRB_nGRAN_to_setup_t *drb_e1 = &pdu_e1->DRBnGRanModList[pdu_e1->numDRB2Modify]; - f1u_gtp_update(&UE->established_drbs[drb_f1->drb_id].f1u_tunnel_config, drb_f1); /* Fill E1 bearer context modification */ fill_e1_bearer_modif(drb_e1, drb_f1); pdu_e1->numDRB2Modify += 1; @@ -1942,6 +1968,7 @@ static void rrc_CU_process_ue_context_setup_response(MessageDef *msg_p, instance /* Note: we would ideally check that SRB2 is acked, but at least LiteOn DU * seems buggy and does not ack, so simply check that locally we activated */ AssertFatal(UE->Srb[1].Active && UE->Srb[2].Active, "SRBs 1 and 2 must be active during DRB Establishment"); + store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE); e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup); } @@ -2003,7 +2030,13 @@ static void rrc_CU_process_ue_context_release_complete(MessageDef *msg_p) return; } - rrc_remove_ue(RC.nrrrc[0], ue_context_p); + gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + if (UE->an_release) { + /* only trigger release if it has been requested by core + * otherwise, it might be CU that requested release on a DU during normal + * operation (i.e, handover) */ + rrc_remove_ue(RC.nrrrc[0], ue_context_p); + } } static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, instance_t instance) @@ -2019,6 +2052,7 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i gNB_RRC_UE_t *UE = &ue_context_p->ue_context; if (resp->drbs_to_be_setup_length > 0) { + store_du_f1u_tunnel(resp->drbs_to_be_setup, resp->drbs_to_be_setup_length, UE); e1_send_bearer_updates(rrc, UE, resp->drbs_to_be_setup_length, resp->drbs_to_be_setup); } @@ -2118,6 +2152,67 @@ static pdusession_level_qos_parameter_t *get_qos_characteristics(const int qfi, return NULL; } +/* \bref return F1AP QoS characteristics based on Qos flow parameters */ +static f1ap_qos_characteristics_t get_qos_char_from_qos_flow_param(const pdusession_level_qos_parameter_t *qos_param) +{ + f1ap_qos_characteristics_t qos_char = {0}; + if (qos_param->fiveQI_type == dynamic) { + qos_char.qos_type = dynamic; + qos_char.dynamic.fiveqi = qos_param->fiveQI; + qos_char.dynamic.qos_priority_level = qos_param->qos_priority; + } else { + qos_char.qos_type = non_dynamic; + qos_char.non_dynamic.fiveqi = qos_param->fiveQI; + qos_char.non_dynamic.qos_priority_level = qos_param->qos_priority; + } + return qos_char; +} + +/* \brief fills a list of DRBs to be setup from a number of PDU sessions in E1 + * bearer setup response */ +static int fill_drb_to_be_setup_from_e1_resp(const gNB_RRC_INST *rrc, + gNB_RRC_UE_t *UE, + const pdu_session_setup_t *pduSession, + int numPduSession, + f1ap_drb_to_be_setup_t drbs[32]) +{ + int nb_drb = 0; + for (int p = 0; p < numPduSession; ++p) { + rrc_pdu_session_param_t *RRC_pduSession = find_pduSession(UE, pduSession[p].id, false); + DevAssert(RRC_pduSession); + for (int i = 0; i < pduSession[p].numDRBSetup; i++) { + const DRB_nGRAN_setup_t *drb_config = &pduSession[p].DRBnGRanList[i]; + f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb]; + drb->drb_id = pduSession[p].DRBnGRanList[i].id; + drb->rlc_mode = rrc->configuration.um_on_default_drb ? RLC_MODE_UM : RLC_MODE_AM; + drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tlAddress; + drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd; + drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].teId; + drb->up_ul_tnl_length = 1; + + /* pass QoS info to MAC */ + int nb_qos_flows = drb_config->numQosFlowSetup; + AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); + drb->drb_info.flows_to_be_setup_length = nb_qos_flows; + drb->drb_info.flows_mapped_to_drb = calloc(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t)); + AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n"); + for (int j = 0; j < nb_qos_flows; j++) { + drb->drb_info.flows_mapped_to_drb[j].qfi = drb_config->qosFlows[j].qfi; + pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession); + drb->drb_info.flows_mapped_to_drb[j].qos_params.qos_characteristics = get_qos_char_from_qos_flow_param(in_qos_char); + } + /* the DRB QoS parameters: we just reuse the ones from the first flow */ + drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params; + + /* pass NSSAI info to MAC */ + drb->nssai = RRC_pduSession->param.nssai; + + nb_drb++; + } + } + return nb_drb; +} + /** * @brief E1AP Bearer Context Setup Response processing on CU-CP */ @@ -2146,56 +2241,20 @@ void rrc_gNB_process_e1_bearer_context_setup_resp(e1ap_bearer_setup_resp_t *resp rrc_pdu->param.gNB_teid_N3 = e1_pdu->teId; memcpy(&rrc_pdu->param.gNB_addr_N3.buffer, &e1_pdu->tlAddress, sizeof(uint8_t) * 4); rrc_pdu->param.gNB_addr_N3.length = sizeof(in_addr_t); + + // save the tunnel address for the DRBs + for (int i = 0; i < e1_pdu->numDRBSetup; i++) { + DRB_nGRAN_setup_t *drb_config = &e1_pdu->DRBnGRanList[i]; + // numUpParam only relevant in F1, but not monolithic + AssertFatal(drb_config->numUpParam <= 1, "can only up to one UP param\n"); + drb_t *drb = get_drb(UE, drb_config->id); + f1u_ul_gtp_update(&drb->cuup_tunnel_config, &drb_config->UpParamList[0]); + } } /* Instruction towards the DU for DRB configuration and tunnel creation */ f1ap_drb_to_be_setup_t drbs[32]; // maximum DRB can be 32 - int nb_drb = 0; - for (int p = 0; p < resp->numPDUSessions; ++p) { - rrc_pdu_session_param_t *RRC_pduSession = find_pduSession(UE, resp->pduSession[p].id, false); - DevAssert(RRC_pduSession); - for (int i = 0; i < resp->pduSession[p].numDRBSetup; i++) { - DRB_nGRAN_setup_t *drb_config = &resp->pduSession[p].DRBnGRanList[i]; - f1ap_drb_to_be_setup_t *drb = &drbs[nb_drb]; - drb->drb_id = resp->pduSession[p].DRBnGRanList[i].id; - drb->rlc_mode = rrc->configuration.um_on_default_drb ? RLC_MODE_UM : RLC_MODE_AM; - drb->up_ul_tnl[0].tl_address = drb_config->UpParamList[0].tlAddress; - drb->up_ul_tnl[0].port = rrc->eth_params_s.my_portd; - drb->up_ul_tnl[0].teid = drb_config->UpParamList[0].teId; - drb->up_ul_tnl_length = 1; - - drb->nssai = RRC_pduSession->param.nssai; - - /* pass QoS info to MAC */ - int nb_qos_flows = drb_config->numQosFlowSetup; - AssertFatal(nb_qos_flows > 0, "must map at least one flow to a DRB\n"); - drb->drb_info.flows_to_be_setup_length = nb_qos_flows; - drb->drb_info.flows_mapped_to_drb = calloc(nb_qos_flows, sizeof(f1ap_flows_mapped_to_drb_t)); - AssertFatal(drb->drb_info.flows_mapped_to_drb, "could not allocate memory\n"); - for (int j = 0; j < nb_qos_flows; j++) { - drb->drb_info.flows_mapped_to_drb[j].qfi = drb_config->qosFlows[j].qfi; - - pdusession_level_qos_parameter_t *in_qos_char = get_qos_characteristics(drb_config->qosFlows[j].qfi, RRC_pduSession); - f1ap_qos_characteristics_t *qos_char = &drb->drb_info.flows_mapped_to_drb[j].qos_params.qos_characteristics; - if (in_qos_char->fiveQI_type == dynamic) { - qos_char->qos_type = dynamic; - qos_char->dynamic.fiveqi = in_qos_char->fiveQI; - qos_char->dynamic.qos_priority_level = in_qos_char->qos_priority; - } else { - qos_char->qos_type = non_dynamic; - qos_char->non_dynamic.fiveqi = in_qos_char->fiveQI; - qos_char->non_dynamic.qos_priority_level = in_qos_char->qos_priority; - } - } - /* the DRB QoS parameters: we just reuse the ones from the first flow */ - drb->drb_info.drb_qos = drb->drb_info.flows_mapped_to_drb[0].qos_params; - - /* pass NSSAI info to MAC */ - drb->nssai = RRC_pduSession->param.nssai; - - nb_drb++; - } - } + int nb_drb = fill_drb_to_be_setup_from_e1_resp(rrc, UE, resp->pduSession, resp->numPDUSessions, drbs); if (!UE->f1_ue_context_active) rrc_gNB_generate_UeContextSetupRequest(rrc, ue_context_p, nb_drb, drbs); @@ -2255,20 +2314,6 @@ static void print_rrc_meas(FILE *f, const NR_MeasResults_t *measresults) } } -static const char *get_rrc_connection_status_text(NR_UE_STATE_t state) -{ - switch (state) { - case NR_RRC_INACTIVE: return "inactive"; - case NR_RRC_IDLE: return "idle"; - case NR_RRC_SI_RECEIVED: return "SI-received"; - case NR_RRC_CONNECTED: return "connected"; - case NR_RRC_RECONFIGURED: return "reconfigured"; - case NR_RRC_HO_EXECUTION: return "HO-execution"; - default: AssertFatal(false, "illegal RRC state %d\n", state); return "illegal"; - } - return "illegal"; -} - static const char *get_pdusession_status_text(pdu_session_status_t status) { switch (status) { @@ -2316,7 +2361,6 @@ static void write_rrc_stats(const gNB_RRC_INST *rrc) time_t last_seen = now - ue_ctxt->last_seen; fprintf(f, " last RRC activity: %ld seconds ago\n", last_seen); - fprintf(f, " RRC status %s\n", get_rrc_connection_status_text(ue_ctxt->StatusRrc)); if (ue_ctxt->nb_of_pdusessions == 0) fprintf(f, " (no PDU sessions)\n"); @@ -2606,8 +2650,8 @@ rrc_gNB_generate_RRCRelease( ) //----------------------------------------------------------------------------- { - uint8_t buffer[RRC_BUF_SIZE] = {0}; - int size = do_NR_RRCRelease(buffer, RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id)); + uint8_t buffer[NR_RRC_BUF_SIZE] = {0}; + int size = do_NR_RRCRelease(buffer, NR_RRC_BUF_SIZE, rrc_gNB_get_next_transaction_identifier(ctxt_pP->module_id)); LOG_I(NR_RRC, PROTOCOL_NR_RRC_CTXT_UE_FMT" Logical Channel DL-DCCH, Generate RRCRelease (bytes %d)\n", @@ -2617,7 +2661,7 @@ rrc_gNB_generate_RRCRelease( gNB_RRC_INST *rrc = RC.nrrrc[ctxt_pP->module_id]; const gNB_RRC_UE_t *UE = &ue_context_pP->ue_context; f1_ue_data_t ue_data = cu_get_f1_ue_data(UE->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_ue_context_release_cmd_t ue_context_release_cmd = { .gNB_CU_ue_id = UE->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, @@ -2639,7 +2683,7 @@ int rrc_gNB_generate_pcch_msg(sctp_assoc_t assoc_id, const NR_SIB1_t *sib1, uint uint32_t N; /* N: min(T,nB). total count of PF in one DRX cycle */ uint32_t Ns = 0; /* Ns: max(1,nB/T) */ uint32_t T; /* DRX cycle */ - uint8_t buffer[RRC_BUF_SIZE]; + uint8_t buffer[NR_RRC_BUF_SIZE]; /* get default DRX cycle from configuration */ Tc = sib1->servingCellConfigCommon->downlinkConfigCommon.pcch_Config.defaultPagingCycle; @@ -2743,7 +2787,7 @@ void rrc_gNB_generate_UeContextSetupRequest(const gNB_RRC_INST *rrc, /* the callback will fill the UE context setup request and forward it */ f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_ue_context_setup_t ue_context_setup_req = { .gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, @@ -2787,7 +2831,7 @@ void rrc_gNB_generate_UeContextModificationRequest(const gNB_RRC_INST *rrc, } f1_ue_data_t ue_data = cu_get_f1_ue_data(ue_p->rrc_ue_id); - RETURN_IF_INVALID_ASSOC_ID(ue_data); + RETURN_IF_INVALID_ASSOC_ID(ue_data.du_assoc_id); f1ap_ue_context_modif_req_t ue_context_modif_req = { .gNB_CU_ue_id = ue_p->rrc_ue_id, .gNB_DU_ue_id = ue_data.secondary_ue, diff --git a/openair2/RRC/NR/rrc_gNB_NGAP.c b/openair2/RRC/NR/rrc_gNB_NGAP.c index a98853af28..1ef1f8c808 100644 --- a/openair2/RRC/NR/rrc_gNB_NGAP.c +++ b/openair2/RRC/NR/rrc_gNB_NGAP.c @@ -1180,6 +1180,7 @@ int rrc_gNB_process_NGAP_UE_CONTEXT_RELEASE_COMMAND(MessageDef *msg_p, instance_ } gNB_RRC_UE_t *UE = &ue_context_p->ue_context; + UE->an_release = true; #ifdef E2_AGENT signal_rrc_state_changed_to(UE, RC_SM_RRC_IDLE); #endif diff --git a/openair2/RRC/NR/rrc_gNB_du.c b/openair2/RRC/NR/rrc_gNB_du.c index b24e6f594c..e20121e8c2 100644 --- a/openair2/RRC/NR/rrc_gNB_du.c +++ b/openair2/RRC/NR/rrc_gNB_du.c @@ -40,12 +40,17 @@ int get_ssb_scs(const struct f1ap_served_cell_info_t *cell_info) return cell_info->mode == F1AP_MODE_TDD ? cell_info->tdd.tbw.scs : cell_info->fdd.dl_tbw.scs; } +static int ssb_arfcn_mtc(const NR_MeasurementTimingConfiguration_t *mtc) +{ + /* format has been verified when accepting MeasurementTimingConfiguration */ + NR_MeasTimingList_t *mtlist = mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; + return mtlist->list.array[0]->frequencyAndTiming->carrierFreq; +} + int get_ssb_arfcn(const struct nr_rrc_du_container_t *du) { DevAssert(du != NULL && du->mtc != NULL); - /* format has been verified when accepting MeasurementTimingConfiguration */ - NR_MeasTimingList_t *mtlist = du->mtc->criticalExtensions.choice.c1->choice.measTimingConf->measTiming; - return mtlist->list.array[0]->frequencyAndTiming->carrierFreq; + return ssb_arfcn_mtc(du->mtc); } static int du_compare(const nr_rrc_du_container_t *a, const nr_rrc_du_container_t *b) @@ -197,6 +202,58 @@ static void label_intra_frequency_neighbours(gNB_RRC_INST *rrc, for_each(cell_neighbour_list, (void *)&ssb_arfcn, is_intra_frequency_neighbour); } +static bool valid_du_in_neighbour_configs(const seq_arr_t *neighbour_cell_configuration, const f1ap_served_cell_info_t *cell, int ssb_arfcn) +{ + for (int c = 0; c < neighbour_cell_configuration->size; c++) { + const neighbour_cell_configuration_t *neighbour_config = seq_arr_at(neighbour_cell_configuration, c); + for (int ni = 0; ni < neighbour_config->neighbour_cells->size; ni++) { + const nr_neighbour_gnb_configuration_t *nc = seq_arr_at(neighbour_config->neighbour_cells, ni); + if (nc->nrcell_id != cell->nr_cellid) + continue; + // current cell is in the nc config, check that config matches + if (nc->physicalCellId != cell->nr_pci) { + LOG_W(NR_RRC, "Cell %ld in neighbour config: PCI mismatch (%d vs %d)\n", cell->nr_cellid, cell->nr_pci, nc->physicalCellId); + return false; + } + if (cell->tac && nc->tac != *cell->tac) { + LOG_W(NR_RRC, "Cell %ld in neighbour config: TAC mismatch (%d vs %d)\n", cell->nr_cellid, *cell->tac, nc->tac); + return false; + } + if (ssb_arfcn != nc->absoluteFrequencySSB) { + LOG_W(NR_RRC, + "Cell %ld in neighbour config: SSB ARFCN mismatch (%d vs %d)\n", + cell->nr_cellid, + ssb_arfcn, + nc->absoluteFrequencySSB); + return false; + } + if (get_ssb_scs(cell) != nc->subcarrierSpacing) { + LOG_W(NR_RRC, + "Cell %ld in neighbour config: SCS mismatch (%d vs %d)\n", + cell->nr_cellid, + get_ssb_scs(cell), + nc->subcarrierSpacing); + return false; + } + if (cell->plmn.mcc != nc->plmn.mcc || cell->plmn.mnc != nc->plmn.mnc + || cell->plmn.mnc_digit_length != nc->plmn.mnc_digit_length) { + LOG_W(NR_RRC, + "Cell %ld in neighbour config: PLMN mismatch (%03d.%0*d vs %03d.%0*d)\n", + cell->nr_cellid, + cell->plmn.mcc, + cell->plmn.mnc_digit_length, + cell->plmn.mnc, + nc->plmn.mcc, + nc->plmn.mnc_digit_length, + nc->plmn.mnc); + return false; + } + LOG_D(NR_RRC, "Cell %ld is neighbor of cell %ld\n", cell->nr_cellid, neighbour_config->nr_cell_id); + } + } + return true; +} + void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) { AssertFatal(assoc_id != 0, "illegal assoc_id == 0: should be -1 (monolithic) or >0 (split)\n"); @@ -219,10 +276,12 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) f1ap_served_cell_info_t *cell_info = &req->cell[0].info; if (!rrc_gNB_plmn_matches(rrc, cell_info)) { LOG_E(NR_RRC, - "PLMN mismatch: CU %d%d, DU %d%d\n", + "PLMN mismatch: CU %03d.%0*d, DU %03d%0*d\n", rrc->configuration.mcc[0], + rrc->configuration.mnc_digit_length[0], rrc->configuration.mnc[0], cell_info->plmn.mcc, + cell_info->plmn.mnc_digit_length, cell_info->plmn.mnc); f1ap_setup_failure_t fail = {.cause = F1AP_CauseRadioNetwork_plmn_not_served_by_the_gNB_CU}; rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); @@ -264,6 +323,15 @@ void rrc_gNB_process_f1_setup_req(f1ap_setup_req_t *req, sctp_assoc_t assoc_id) NR_MeasurementTimingConfiguration_t *mtc = extract_mtc(cell_info->measurement_timing_config, cell_info->measurement_timing_config_len); + if (rrc->neighbour_cell_configuration + && !valid_du_in_neighbour_configs(rrc->neighbour_cell_configuration, cell_info, ssb_arfcn_mtc(mtc))) { + LOG_E(NR_RRC, "problem with DU %ld in neighbor configuration, rejecting DU\n", req->gNB_DU_id); + f1ap_setup_failure_t fail = {.cause = F1AP_CauseMisc_unspecified}; + rrc->mac_rrc.f1_setup_failure(assoc_id, &fail); + ASN_STRUCT_FREE(asn_DEF_NR_MeasurementTimingConfiguration, mtc); + return; + } + const f1ap_gnb_du_system_info_t *sys_info = req->cell[0].sys_info; NR_MIB_t *mib = NULL; NR_SIB1_t *sib1 = NULL; @@ -501,7 +569,7 @@ void dump_du_info(const gNB_RRC_INST *rrc, FILE *f) fprintf(f, "assoc_id %d", du->assoc_id); } const f1ap_served_cell_info_t *info = &sr->cell[0].info; - fprintf(f, ": nrCellID %ld, PCI %d\n", info->nr_cellid, info->nr_pci); + fprintf(f, ": nrCellID %ld, PCI %d, SSB ARFCN %d\n", info->nr_cellid, info->nr_pci, get_ssb_arfcn(du)); if (info->mode == F1AP_MODE_TDD) { const f1ap_nr_frequency_info_t *fi = &info->tdd.freqinfo; diff --git a/openair2/RRC/NR/rrc_gNB_radio_bearers.c b/openair2/RRC/NR/rrc_gNB_radio_bearers.c index 205a58fc36..ab2bf13071 100644 --- a/openair2/RRC/NR/rrc_gNB_radio_bearers.c +++ b/openair2/RRC/NR/rrc_gNB_radio_bearers.c @@ -51,7 +51,7 @@ rrc_pdu_session_param_t *find_pduSession_from_drbId(gNB_RRC_UE_t *ue, int drb_id drb_t *get_drb(gNB_RRC_UE_t *ue, uint8_t drb_id) { - DevAssert(drb_id > 0 && drb_id < 32); + DevAssert(drb_id > 0 && drb_id <= 32); DevAssert(ue != NULL); return &ue->established_drbs[drb_id - 1]; @@ -96,10 +96,7 @@ drb_t *generateDRB(gNB_RRC_UE_t *ue, est_drb->status = DRB_ACTIVE; est_drb->drb_id = drb_id; - est_drb->reestablishPDCP = -1; - est_drb->recoverPDCP = -1; est_drb->cnAssociation.sdap_config.defaultDRB = true; - est_drb->defaultDRBid = drb_id; /* SDAP Configuration */ est_drb->cnAssociation.present = NR_DRB_ToAddMod__cnAssociation_PR_sdap_Config; diff --git a/openair2/RRC/NR_UE/rrc_UE.c b/openair2/RRC/NR_UE/rrc_UE.c index eb5113ccee..0b88661a74 100644 --- a/openair2/RRC/NR_UE/rrc_UE.c +++ b/openair2/RRC/NR_UE/rrc_UE.c @@ -479,7 +479,7 @@ static void nr_rrc_ue_decode_NR_BCCH_BCH_Message(NR_UE_RRC_INST_t *rrc, // Actions following cell selection while T311 is running NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants; - if (is_nr_timer_active(timers->T311)) { + if (nr_timer_is_active(&timers->T311)) { nr_timer_stop(&timers->T311); rrc->ra_trigger = RRC_CONNECTION_REESTABLISHMENT; @@ -1695,7 +1695,7 @@ static int nr_rrc_ue_decode_dcch(NR_UE_RRC_INST_t *rrc, void nr_rrc_handle_ra_indication(NR_UE_RRC_INST_t *rrc, bool ra_succeeded) { NR_UE_Timers_Constants_t *timers = &rrc->timers_and_constants; - if (ra_succeeded && is_nr_timer_active(timers->T304)) { + if (ra_succeeded && nr_timer_is_active(&timers->T304)) { // successful Random Access procedure triggered by reconfigurationWithSync nr_timer_stop(&timers->T304); // TODO handle the rest of procedures as described in 5.3.5.3 for when @@ -1823,6 +1823,7 @@ void *rrc_nrue(void *notUsed) // error: the remote gNB is hardcoded here rb_id_t srb_id = rrc->Srb[2] == RB_ESTABLISHED ? 2 : 1; nr_pdcp_data_req_srb(rrc->ue_id, srb_id, 0, length, buffer, deliver_pdu_srb_rlc, NULL); + free(req->nasMsg.data); free(buffer); break; } @@ -2130,7 +2131,7 @@ static void process_lte_nsa_msg(NR_UE_RRC_INST_t *rrc, nsa_msg_t *msg, int msg_l process_nsa_message(NR_UE_rrc_inst, nr_SecondaryCellGroupConfig_r15, nr_SecondaryCellGroup_buffer, nr_SecondaryCellGroup_size); process_nsa_message(NR_UE_rrc_inst, nr_RadioBearerConfigX_r15, nr_RadioBearer_buffer, nr_RadioBearer_size); LOG_I(NR_RRC, "Calling do_NR_RRCReconfigurationComplete. t_id %ld \n", t_id); - uint8_t buffer[RRC_BUF_SIZE]; + uint8_t buffer[NR_RRC_BUF_SIZE]; size_t size = do_NR_RRCReconfigurationComplete_for_nsa(buffer, sizeof(buffer), t_id); nsa_sendmsg_to_lte_ue(buffer, size, NR_RRC_CONFIG_COMPLETE_REQ); break; @@ -2138,7 +2139,7 @@ static void process_lte_nsa_msg(NR_UE_RRC_INST_t *rrc, nsa_msg_t *msg, int msg_l case OAI_TUN_IFACE_NSA: { LOG_I(NR_RRC, "We got an OAI_TUN_IFACE_NSA!!\n"); - char cmd_line[RRC_BUF_SIZE]; + char cmd_line[NR_RRC_BUF_SIZE]; memcpy(cmd_line, msg_buffer, sizeof(cmd_line)); LOG_D(NR_RRC, "Command line: %s\n", cmd_line); if (background_system(cmd_line) != 0) @@ -2218,14 +2219,14 @@ void nr_rrc_going_to_IDLE(NR_UE_RRC_INST_t *rrc, } } if (!waitTime) { - if (is_nr_timer_active(tac->T302)) { + if (nr_timer_is_active(&tac->T302)) { nr_timer_stop(&tac->T302); // TODO barring alleviation as in 5.3.14.4 // not implemented LOG_E(NR_RRC,"Go to IDLE. Barring alleviation not implemented\n"); } } - if (is_nr_timer_active(tac->T390)) { + if (nr_timer_is_active(&tac->T390)) { nr_timer_stop(&tac->T390); // TODO barring alleviation as in 5.3.14.4 // not implemented diff --git a/openair2/RRC/NR_UE/rrc_timers_and_constants.c b/openair2/RRC/NR_UE/rrc_timers_and_constants.c index 70a467691b..9e734a3188 100644 --- a/openair2/RRC/NR_UE/rrc_timers_and_constants.c +++ b/openair2/RRC/NR_UE/rrc_timers_and_constants.c @@ -560,7 +560,7 @@ void handle_rlf_sync(NR_UE_Timers_Constants_t *tac, { if (sync_msg == IN_SYNC) { tac->N310_cnt = 0; - if (is_nr_timer_active(tac->T310)) { + if (nr_timer_is_active(&tac->T310)) { tac->N311_cnt++; // Upon receiving N311 consecutive "in-sync" indications if (tac->N311_cnt >= tac->N311_k) { @@ -573,12 +573,12 @@ void handle_rlf_sync(NR_UE_Timers_Constants_t *tac, else { // OUT_OF_SYNC tac->N311_cnt = 0; - if(is_nr_timer_active(tac->T300) || - is_nr_timer_active(tac->T301) || - is_nr_timer_active(tac->T304) || - is_nr_timer_active(tac->T310) || - is_nr_timer_active(tac->T311) || - is_nr_timer_active(tac->T319)) + if(nr_timer_is_active(&tac->T300) || + nr_timer_is_active(&tac->T301) || + nr_timer_is_active(&tac->T304) || + nr_timer_is_active(&tac->T310) || + nr_timer_is_active(&tac->T311) || + nr_timer_is_active(&tac->T319)) return; tac->N310_cnt++; // upon receiving N310 consecutive "out-of-sync" indications diff --git a/openair3/NAS/NR_UE/nr_nas_msg_sim.c b/openair3/NAS/NR_UE/nr_nas_msg_sim.c index 0baab7b65c..c942e079cd 100644 --- a/openair3/NAS/NR_UE/nr_nas_msg_sim.c +++ b/openair3/NAS/NR_UE/nr_nas_msg_sim.c @@ -614,6 +614,8 @@ static void generateAuthenticationResp(nr_ue_nas_t *nas, as_nas_info_t *initialN initialNasMsg->data = (Byte_t *)malloc(size * sizeof(Byte_t)); initialNasMsg->length = mm_msg_encode(mm_msg, (uint8_t *)(initialNasMsg->data), size); + // Free res value after encode + free(res.value); } int nas_itti_kgnb_refresh_req(instance_t instance, const uint8_t kgnb[32]) @@ -1002,6 +1004,11 @@ static void generatePduSessionEstablishRequest(nr_ue_nas_t *nas, as_nas_info_t * security_header_len + mm_msg_encode(mm_msg, (uint8_t *)(initialNasMsg->data + security_header_len), size - security_header_len); + // Free allocated memory after encode + free(req_buffer); + free(mm_msg->uplink_nas_transport.dnn.value); + free(mm_msg->uplink_nas_transport.snssai.value); + /* ciphering */ uint8_t buf[initialNasMsg->length - 7]; stream_cipher.context = nas->security_container->ciphering_context; @@ -1323,6 +1330,8 @@ void *nas_nrue(void *args_p) capture_pdu_session_establishment_accept_msg(pdu_buffer, pdu_length); } + // Free NAS buffer memory after use (coming from RRC) + free(pdu_buffer); break; } @@ -1447,6 +1456,8 @@ void *nas_nrue(void *args_p) LOG_W(NR_RRC, "unknown message type %d\n", msg_type); break; } + // Free NAS buffer memory after use (coming from RRC) + free(pdu_buffer); if (initialNasMsg.length > 0) send_nas_uplink_data_req(nas, &initialNasMsg); diff --git a/openair3/SCTP/sctp_eNB_task.c b/openair3/SCTP/sctp_eNB_task.c index ffd4bba3f5..a6d3a11a39 100644 --- a/openair3/SCTP/sctp_eNB_task.c +++ b/openair3/SCTP/sctp_eNB_task.c @@ -787,7 +787,7 @@ sctp_eNB_read_from_socket( SCTP_ERROR("sctp_recvmsg (fd %d, len %d ): %s:%d\n", sctp_cnx->sd, n, strerror(errno), errno); sctp_itti_send_association_resp( - sctp_cnx->task_id, sctp_cnx->instance, -1, + sctp_cnx->task_id, sctp_cnx->instance, sctp_cnx->assoc_id, sctp_cnx->cnx_id, SCTP_STATE_UNREACHABLE, 0, 0); close(sctp_cnx->sd); diff --git a/openshift/README.md b/openshift/README.md index 5560621e68..e5a2b00bdd 100644 --- a/openshift/README.md +++ b/openshift/README.md @@ -53,8 +53,6 @@ In case you have difficulties in following it then you can copy the certificates from an RHEL9 host ```bash -oc create configmap rhsm-conf --from-file /etc/rhsm/rhsm.conf -oc create configmap rhsm-ca --from-file /etc/rhsm/ca/redhat-uep.pem oc create secret generic etc-pki-entitlement --from-file /etc/pki/entitlement/{NUMBER_ON_YOUR_COMPUTER}.pem --from-file /etc/pki/entitlement/{NUMBER_ON_YOUR_COMPUTER}-key.pem ``` diff --git a/openshift/oai-gnb-fhi72-bc.yaml b/openshift/oai-gnb-fhi72-bc.yaml index 2d8861e2d6..0e453ae379 100644 --- a/openshift/oai-gnb-fhi72-bc.yaml +++ b/openshift/oai-gnb-fhi72-bc.yaml @@ -32,13 +32,6 @@ spec: - secret: name: etc-pki-entitlement destinationDir: etc-pki-entitlement - configMaps: - - configMap: - name: rhsm-conf - destinationDir: rhsm-conf - - configMap: - name: rhsm-ca - destinationDir: rhsm-ca strategy: dockerStrategy: dockerfilePath: "docker/Dockerfile.gNB.fhi72.rhel9" diff --git a/openshift/ran-base-bc.yaml b/openshift/ran-base-bc.yaml index 788e980fef..5707e64abb 100644 --- a/openshift/ran-base-bc.yaml +++ b/openshift/ran-base-bc.yaml @@ -34,13 +34,6 @@ spec: - secret: name: etc-pki-entitlement destinationDir: etc-pki-entitlement - configMaps: - - configMap: - name: rhsm-conf - destinationDir: rhsm-conf - - configMap: - name: rhsm-ca - destinationDir: rhsm-ca strategy: dockerStrategy: dockerfilePath: "docker/Dockerfile.base.rhel9" diff --git a/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.yaml b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.yaml new file mode 100644 index 0000000000..a215c71304 --- /dev/null +++ b/targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.yaml @@ -0,0 +1,221 @@ +Active_gNBs: + - gNB-OAI +Asn1_verbosity: none +gNBs: + # Identification parameters: + - gNB_ID: 0xe00 + gNB_name: gNB-OAI + # Tracking area code, 0x0000 and 0xfffe are reserved values + tracking_area_code: 1 + plmn_list: + - + mcc: 001 + mnc: 01 + mnc_length: 2 + snssaiList: + - + sst: 1 + nr_cellid: 12345678 + # Physical parameters: + do_CSIRS: 1 + do_SRS: 1 + servingCellConfigCommon: + #spCellConfigCommon + - physCellId: 0 + # downlinkConfigCommon + #frequencyInfoDL + # this is 3600 MHz + 43 PRBs@30kHz SCS (same as initial BWP) + absoluteFrequencySSB: 641280 + dl_frequencyBand: 78 + # this is 3600 MHz + dl_absoluteFrequencyPointA: 640008 + #scs-SpecificCarrierList + dl_offstToCarrier: 0 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + dl_subcarrierSpacing: 1 + dl_carrierBandwidth: 106 + #initialDownlinkBWP + #genericParameters + # this is RBstart=27,L=48 (275*(L-1))+RBstart + initialDLBWPlocationAndBandwidth: 28875 # 6366 12925 12956 28875 12952 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialDLBWPsubcarrierSpacing: 1 + #pdcch-ConfigCommon + initialDLBWPcontrolResourceSetZero: 12 + initialDLBWPsearchSpaceZero: 0 + #uplinkConfigCommon + #frequencyInfoUL + ul_frequencyBand: 78 + #scs-SpecificCarrierList + ul_offstToCarrier: 0 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + ul_subcarrierSpacing: 1 + ul_carrierBandwidth: 106 + pMax: 20 + #initialUplinkBWP + #genericParameters + initialULBWPlocationAndBandwidth: 28875 + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + initialULBWPsubcarrierSpacing: 1 + #rach-ConfigCommon + #rach-ConfigGeneric + prach_ConfigurationIndex: 98 + #prach_msg1_FDM + #0 = one, 1=two, 2=four, 3=eight + prach_msg1_FDM: 0 + prach_msg1_FrequencyStart: 0 + zeroCorrelationZoneConfig: 13 + preambleReceivedTargetPower: -96 + #preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200) + preambleTransMax: 6 + #powerRampingStep + # 0=dB0,1=dB2,2=dB4,3=dB6 + powerRampingStep: 1 + #ra_ReponseWindow + #1,2,4,8,10,20,40,80 + ra_ResponseWindow: 4 + #ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR + #1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen + ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR: 4 + #oneHalf (0..15) 4,8,12,16,...60,64 + ssb_perRACH_OccasionAndCB_PreamblesPerSSB: 14 + #ra_ContentionResolutionTimer + #(0..7) 8,16,24,32,40,48,56,64 + ra_ContentionResolutionTimer: 7 + rsrp_ThresholdSSB: 19 + #prach-RootSequenceIndex_PR + #1 = 839, 2 = 139 + prach_RootSequenceIndex_PR: 2 + prach_RootSequenceIndex: 1 + # SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex + msg1_SubcarrierSpacing: 1 + # restrictedSetConfig + # 0=unrestricted, 1=restricted type A, 2=restricted type B + restrictedSetConfig: 0 + msg3_DeltaPreamble: 1 + p0_NominalWithGrant: -90 + # pucch-ConfigCommon setup : + # pucchGroupHopping + # 0 = neither, 1= group hopping, 2=sequence hopping + pucchGroupHopping: 0 + hoppingId: 40 + p0_nominal: -90 + # ssb_PositionsInBurs_BitmapPR + # 1=short, 2=medium, 3=long + ssb_PositionsInBurst_PR: 2 + ssb_PositionsInBurst_Bitmap: 1 + + # ssb_periodicityServingCell + # 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1 + ssb_periodicityServingCell: 2 + + # dmrs_TypeA_position + # 0 = pos2, 1 = pos3 + dmrs_TypeA_Position: 0 + + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + subcarrierSpacing: 1 + + + #tdd-UL-DL-ConfigurationCommon + # subcarrierSpacing + # 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120 + referenceSubcarrierSpacing: 1 + # pattern1 + # dl_UL_TransmissionPeriodicity + # 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10 + dl_UL_TransmissionPeriodicity: 6 + nrofDownlinkSlots: 7 + nrofDownlinkSymbols: 6 + nrofUplinkSlots: 2 + nrofUplinkSymbols: 4 + + ssPBCH_BlockPower: -25 + SCTP: + SCTP_INSTREAMS: 2 + SCTP_OUTSTREAMS: 2 + + + # AMF parameters: + amf_ip_address: + ipv4: 192.168.70.132 + + + NETWORK_INTERFACES: + GNB_IPV4_ADDRESS_FOR_NG_AMF: 192.168.70.129 + GNB_IPV4_ADDRESS_FOR_NGU: 192.168.70.129 + GNB_PORT_FOR_S1U: 2152 # Spec 2152 + +MACRLCs: + - + num_cc: 1 + tr_s_preference: local_L1 + tr_n_preference: local_RRC + pusch_TargetSNRx10: 150 + pucch_TargetSNRx10: 200 + +L1s: + - + num_cc: 1 + tr_n_preference: local_mac + prach_dtx_threshold: 120 + pucch0_dtx_threshold: 100 + ofdm_offset_divisor: 8 #set this to UINT_MAX for offset 0 + +RUs: + - + local_rf: yes + nb_tx: 1 + nb_rx: 1 + att_tx: 12 + att_rx: 12 + bands: [78] + max_pdschReferenceSignalPower: -27 + max_rxgain: 114 + eNB_instances: [0] + bf_weights: [0x00007fff, 0x0000, 0x0000, 0x0000] + clock_src: internal + +rfsimulator: + serveraddr: server + serverport: 4043 + options: [] #("saviq"); or/and "chanmod" + modelname: AWGN + IQfile: /tmp/rfsimulator.iqs + +security: + # preferred ciphering algorithms + # the first one of the list that an UE supports in chosen + # valid values: nea0, nea1, nea2, nea3 + ciphering_algorithms: [nea0] + + # preferred integrity algorithms + # the first one of the list that an UE supports in chosen + # valid values: nia0, nia1, nia2, nia3 + integrity_algorithms: [nia2, nia0] + + # setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter + # what 'ciphering_algorithms' configures; same thing for 'drb_integrity' + drb_ciphering: yes + drb_integrity: no + +log_config: + global_log_level: info + hw_log_level: info + phy_log_level: info + mac_log_level: info + rlc_log_level: info + pdcp_log_level: info + rrc_log_level: info + ngap_log_level: debug + f1ap_log_level: debug + +e2_agent: + near_ric_ip_addr: 127.0.0.1 + #sm_dir = /path/where/the/SMs/are/located/ + sm_dir: /usr/local/lib/flexric/