diff --git a/CMakeLists.txt b/CMakeLists.txt index 898b387237..a71b1c3ea2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -194,26 +194,26 @@ set(CMAKE_CXX_FLAGS "${C_FLAGS_PROCESSOR} ${commonOpts} -std=c++11 ${CMAKE_CXX_FLAGS}") -add_boolean_option(SANITIZE_ADDRESS False "enable the address sanitizer (ASan)" ON) +add_boolean_option(SANITIZE_ADDRESS OFF "enable the address sanitizer (ASan)" ON) if (SANITIZE_ADDRESS) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=address -fno-omit-frame-pointer -fno-common") endif () -add_boolean_option(SANITIZE_THREAD False "enable the address sanitizer (TSan)" ON) +add_boolean_option(SANITIZE_THREAD OFF "enable the address sanitizer (TSan)" ON) if (SANITIZE_THREAD) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=thread -fno-omit-frame-pointer -fno-common") set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} -fsanitize=thread") endif() -add_boolean_option(SANITIZE_UNDEFINED False "enable the undefined behavior sanitizer (UBSan)" ON) +add_boolean_option(SANITIZE_UNDEFINED OFF "enable the undefined behavior sanitizer (UBSan)" ON) if (SANITIZE_UNDEFINED) set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fsanitize=undefined -fno-sanitize-recover=all") endif () -add_boolean_option(SANITIZE_MEMORY False "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON) +add_boolean_option(SANITIZE_MEMORY OFF "enable the memory sanitizer (MSan, requires clang, incompatible with ASan/UBSan)" ON) if(SANITIZE_MEMORY) if (SANITIZE_UNDEFINED OR SANITIZE_ADDRESS) message(FATAL_ERROR "memory sanitizer cannot coexist with address sanitizer or undefined behavior sanitizer, please disable either MSan or ASan and UBSan") @@ -278,14 +278,16 @@ endif() # OAI uses this feature to re-use OAI LOG_I(ASN1, ...) # see common/utils/config.h add_boolean_option(TRACE_ASN1C_ENC_DEC OFF "Enable ASN1 encoder/decoder debug traces via OAI logging system" ON) -add_boolean_option(T_TRACER True "Activate the T tracer, a debugging/monitoring framework" ON) -add_boolean_option(ENABLE_LTTNG False "Activate the LTTNG tracer, a debugging/monitoring framework" ON) -add_boolean_option(UE_DEBUG_TRACE False "Activate UE debug trace" ON) -add_boolean_option(UE_TIMING_TRACE False "Activate UE timing trace" ON) +add_boolean_option(T_TRACER ON "Activate the T tracer, a debugging/monitoring framework" ON) +add_boolean_option(ENABLE_LTTNG OFF "Activate the LTTNG tracer, a debugging/monitoring framework" ON) +add_boolean_option(UE_DEBUG_TRACE OFF "Activate UE debug trace" ON) +add_boolean_option(UE_TIMING_TRACE OFF "Activate UE timing trace" ON) add_boolean_option(TRACY_ENABLE OFF "Enable tracy instrumentation" ON) if (TRACY_ENABLE) - CPMAddPackage("gh:wolfpld/tracy#v0.11.1") + # the tracy version here should match the tracy server version + # below is latest release as of this commit + CPMAddPackage("gh:wolfpld/tracy#0.12.2") endif() set (OCP_ITTI ${OPENAIR_DIR}/common/utils/ocp_itti) @@ -337,7 +339,6 @@ target_link_libraries(nr_rrc PUBLIC asn1_nr_rrc asn1_lte_rrc) set(S1AP_DIR ${OPENAIR3_DIR}/S1AP) include_directories ("${S1AP_DIR}") add_library(s1ap - ${S1AP_DIR}/s1ap_common.c ${S1AP_DIR}/s1ap_eNB.c ${S1AP_DIR}/s1ap_eNB_context_management_procedures.c ${S1AP_DIR}/s1ap_eNB_decoder.c @@ -509,34 +510,34 @@ include_directories ("${OPENAIR_DIR}/radio/COMMON") # ???!!! TO BE DOCUMENTED OPTIONS !!!??? ############################################################## -add_boolean_option(UE_EXPANSION False "enable UE_EXPANSION with max 256 UE" ON) -add_boolean_option(PHY_TX_THREAD False "enable UE_EXPANSION with max 256 UE" ON) -add_boolean_option(PRE_SCD_THREAD False "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(UE_EXPANSION OFF "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(PHY_TX_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON) +add_boolean_option(PRE_SCD_THREAD OFF "enable UE_EXPANSION with max 256 UE" ON) ########################## # SCHEDULING/REAL-TIME/PERF options ########################## -add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME False "Add data in vcd traces: disable it if perf issues" ON) -add_boolean_option(ENABLE_VCD False "always true now, time measurements of proc calls and var displays" ON) -add_boolean_option(ENABLE_VCD_FIFO False "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON) +add_boolean_option(ENABLE_USE_CPU_EXECUTION_TIME OFF "Add data in vcd traces: disable it if perf issues" ON) +add_boolean_option(ENABLE_VCD OFF "always true now, time measurements of proc calls and var displays" ON) +add_boolean_option(ENABLE_VCD_FIFO OFF "time measurements of proc calls and var displays sent to FIFO (one more thread)" ON) ########################## # PHY options ########################## -add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggreagtion for now)" ON) -add_boolean_option(SMBV False "Rohde&Schwarz SMBV100A vector signal generator" ON) -add_boolean_option(DEBUG_PHY False "Enable PHY layer debugging options" ON) -add_boolean_option(DEBUG_PHY_PROC False "Enable debugging of PHY layer procedures" ON) +add_integer_option(MAX_NUM_CCs 1 "Carrier component data arrays size (oai doesn't support carrier aggregation for now)" ON) +add_boolean_option(SMBV OFF "Rohde&Schwarz SMBV100A vector signal generator" ON) +add_boolean_option(DEBUG_PHY OFF "Enable PHY layer debugging options" ON) +add_boolean_option(DEBUG_PHY_PROC OFF "Enable debugging of PHY layer procedures" ON) ########################## # NAS LAYER OPTIONS ########################## -add_boolean_option(NAS_BUILT_IN_UE True "UE NAS layer present in this executable" ON) +add_boolean_option(NAS_BUILT_IN_UE ON "UE NAS layer present in this executable" ON) ########################## # RRC LAYER OPTIONS ########################## -add_boolean_option(RRC_DEFAULT_RAB_IS_AM True "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON) +add_boolean_option(RRC_DEFAULT_RAB_IS_AM ON "set the LTE RLC mode to AM for the default bearer, otherwise it is UM." ON) # add the binary tree to the search path for include files # We will find common/oai_version.h after generation in target directory @@ -603,7 +604,7 @@ include_directories("${OPENAIR_DIR}") ############### # include T directory even if the T is off because T macros are in the code -# no matter what. Note: sone files are generated, so we have both source and +# no matter what. Note: some files are generated, so we have both source and # binary directories include_directories(${CMAKE_CURRENT_SOURCE_DIR}/common/utils/T ${CMAKE_CURRENT_BINARY_DIR}/common/utils/T) @@ -1395,9 +1396,10 @@ include_directories("${OPENAIR1_DIR}/SCHED_NR_UE") add_library (GTPV1U ${RRC_DIR}/rrc_eNB_GTPV1U.c ${OPENAIR3_DIR}/ocp-gtpu/gtp_itf.cpp + ${OPENAIR3_DIR}/ocp-gtpu/gtpu_extensions.c ) target_link_libraries(GTPV1U PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs) -target_link_libraries(GTPV1U PRIVATE SIMU) +target_link_libraries(GTPV1U PRIVATE SIMU ds) include_directories(${OPENAIR3_DIR}/ocp-gtp) set (MME_APP_SRC diff --git a/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml b/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml new file mode 100644 index 0000000000..3d2efb5084 --- /dev/null +++ b/ci-scripts/xml_files/gnb_phytest_rfemulator_run_100_2x2.xml @@ -0,0 +1,70 @@ + + + gNB-PHY-Test-100-2x2-RFemu + Timing phytest 100 MHz 2x2 RFemulator + tasks + +100000 390101 000001 390109 200000 + + + + Custom_Command + Disable Sleep States + caracal + sudo cpupower idle-set -D 0 + + + Custom_Command + true + Enable Sleep States + caracal + sudo cpupower idle-set -E + + + + Initialize_eNB + Initialize gNB USRP + caracal + -O ci-scripts/conf_files/gnb.band79.273prb.usrpn300.phytest-dora.conf --phy-test -q -U 768 -T 273 -t 23 -D 127 -m 23 -M 273 -l 2 --device.name rf_emulator --rf_emulator.noise_level_dBFS -36 --log_config.global_log_options level,nocolor,time + datalog_rt_stats.100.2x2.yaml + NR + numactl --cpunodebind=netdev:enp94s0f0np0 --membind=netdev:enp94s0f0np0 + + + + IdleSleep + Sleep + 60 + + + + + Terminate_eNB + caracal + true + Terminate gNB + NR + + + diff --git a/cmake_targets/build_oai b/cmake_targets/build_oai index b1b305c596..658b0b858a 100755 --- a/cmake_targets/build_oai +++ b/cmake_targets/build_oai @@ -457,7 +457,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 params_yaml vrtsim" + TARGET_LIST="$TARGET_LIST params_libconfig coding rfsimulator dfts params_yaml vrtsim rf_emulator" fi mkdir -p $DIR/$BUILD_DIR/build diff --git a/common/config/DOC/config/rtusage.md b/common/config/DOC/config/rtusage.md index 3b51e1afd2..7c42a6edcc 100644 --- a/common/config/DOC/config/rtusage.md +++ b/common/config/DOC/config/rtusage.md @@ -55,7 +55,6 @@ To get help on supported parameters you can use specific options: --ulsch-max-errors: set the eNodeB max ULSCH erros --phy-test: test UE phy layer, mac disabled --usim-test: use XOR autentication algo in case of test usim mode - --emulate-rf: Emulated RF enabled(disable by defult) --clock: tells hardware to use a clock reference (0:internal, 1:external, 2:gpsdo) --wait-for-sync: Help string not specified -C: Set the downlink frequency for all component carriers diff --git a/common/utils/ds/CMakeLists.txt b/common/utils/ds/CMakeLists.txt index a5ae86c52f..b5f5efb47a 100644 --- a/common/utils/ds/CMakeLists.txt +++ b/common/utils/ds/CMakeLists.txt @@ -1,5 +1,6 @@ add_library(ds OBJECT byte_array.c + byte_array_producer.c seq_arr.c ) diff --git a/common/utils/ds/byte_array_producer.c b/common/utils/ds/byte_array_producer.c new file mode 100644 index 0000000000..07f46c363c --- /dev/null +++ b/common/utils/ds/byte_array_producer.c @@ -0,0 +1,64 @@ +/* + * 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 "byte_array_producer.h" + +/* create a byte array producer from an existing buffer */ +byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len) +{ + byte_array_producer_t result = { .b.len = len, .b.buf = buffer, .pos = 0 }; + return result; +} + +/* put a byte in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t byte) +{ + if (b->pos == b->b.len) + return 0; + + b->b.buf[b->pos] = byte; + b->pos++; + + return 1; +} + +/* put an u32 number as big endian in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v) +{ + return byte_array_producer_put_byte(b, (v >> 24) & 0xff) + && byte_array_producer_put_byte(b, (v >> 16) & 0xff) + && byte_array_producer_put_byte(b, (v >> 8) & 0xff) + && byte_array_producer_put_byte(b, v & 0xff); +} + +/* put an u24 number as big endian in a byte array if there is space + * returns 1 on success, 0 on error + */ +int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v) +{ + return byte_array_producer_put_byte(b, (v >> 16) & 0xff) + && byte_array_producer_put_byte(b, (v >> 8) & 0xff) + && byte_array_producer_put_byte(b, v & 0xff); +} diff --git a/common/utils/ds/byte_array_producer.h b/common/utils/ds/byte_array_producer.h new file mode 100644 index 0000000000..2087c77af1 --- /dev/null +++ b/common/utils/ds/byte_array_producer.h @@ -0,0 +1,40 @@ +/* + * 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 BYTE_ARRAY_PRODUCER_H +#define BYTE_ARRAY_PRODUCER_H + +#include + +#include "byte_array.h" + +typedef struct { + byte_array_t b; + size_t pos; +} byte_array_producer_t; + +byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len); + +int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t v); +int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v); +int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v); + +#endif diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.c b/common/utils/shm_iq_channel/shm_td_iq_channel.c index 89e09ddb1f..ecebbaf494 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.c @@ -40,7 +40,6 @@ typedef struct { int num_antennas_tx; int num_antennas_rx; uint64_t timestamp; - bool is_connected; pthread_mutex_t mutex; pthread_cond_t cond; } ShmTDIQChannelData; @@ -48,10 +47,10 @@ typedef struct { typedef struct ShmTDIQChannel_s { IQChannelType type; ShmTDIQChannelData *data; - uint64_t last_timestamp; char name[256]; sample_t *tx_iq_data; sample_t *rx_iq_data; + bool abort; } ShmTDIQChannel; ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int num_rx_ant) @@ -75,14 +74,12 @@ ShmTDIQChannel *shm_td_iq_channel_create(const char *name, int num_tx_ant, int n memset(shm_ptr, 0, total_size); shm_ptr->num_antennas_tx = num_tx_ant; shm_ptr->num_antennas_rx = num_rx_ant; - shm_ptr->is_connected = false; ShmTDIQChannel *channel = calloc_or_fail(1, sizeof(ShmTDIQChannel)); strncpy(channel->name, name, sizeof(channel->name) - 1); channel->tx_iq_data = (sample_t *)(shm_ptr + 1); channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t); channel->data = shm_ptr; channel->type = IQ_CHANNEL_TYPE_SERVER; - channel->last_timestamp = 0; pthread_mutexattr_t mutex_attr; pthread_condattr_t cond_attr; int ret = pthread_mutexattr_init(&mutex_attr); @@ -137,12 +134,10 @@ ShmTDIQChannel *shm_td_iq_channel_connect(const char *name, int timeout_in_secon size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * channel->data->num_antennas_tx; channel->rx_iq_data = channel->tx_iq_data + tx_buffer_size / sizeof(sample_t); channel->type = IQ_CHANNEL_TYPE_CLIENT; - channel->last_timestamp = 0; while (shm_ptr->magic != SHM_MAGIC_NUMBER) { printf("Waiting for server to initialize shared memory\n"); sleep(1); } - shm_ptr->is_connected = true; close(fd); return channel; } @@ -154,9 +149,6 @@ IQChannelErrorType shm_td_iq_channel_tx(ShmTDIQChannel *channel, const sample_t *tx_iq_data) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - return CHANNEL_ERROR_NOT_CONNECTED; - } // timestamp in the past uint64_t current_time = data->timestamp; if (timestamp < current_time) { @@ -193,9 +185,6 @@ IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel, sample_t *tx_iq_data) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - return CHANNEL_ERROR_NOT_CONNECTED; - } // timestamp in the future uint64_t current_time = data->timestamp; if (timestamp > current_time) { @@ -228,37 +217,54 @@ IQChannelErrorType shm_td_iq_channel_rx(ShmTDIQChannel *channel, void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, size_t num_samples) { ShmTDIQChannelData *data = channel->data; - if (channel->type != IQ_CHANNEL_TYPE_SERVER) { - return; - } - if (data->is_connected == false) { - return; - } mutexlock(data->mutex); data->timestamp += num_samples; condbroadcast(data->cond); mutexunlock(data->mutex); } -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp) +int shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS) { ShmTDIQChannelData *data = channel->data; - if (data->is_connected == false) { - abort(); - return; - } - size_t current_timestamp = data->timestamp; if (current_timestamp >= timestamp) { - return; + return 0; } - mutexlock(data->mutex); - while (current_timestamp < timestamp) { - condwait(data->cond, data->mutex); - current_timestamp = data->timestamp; + if (timeout_uS == 0) { + mutexlock(data->mutex); + while (current_timestamp < timestamp && !channel->abort) { + condwait(data->cond, data->mutex); + current_timestamp = data->timestamp; + } + mutexunlock(data->mutex); + } else { + struct timespec ts = {.tv_sec = 0, .tv_nsec = 0}; + if (clock_gettime(CLOCK_REALTIME, &ts) != 0) { + fprintf(stderr, "Error: clock_gettime failed: %s\n", strerror(errno)); + return 1; + } + + ts.tv_sec += timeout_uS / 1000000; // Convert microseconds to seconds + ts.tv_nsec += (timeout_uS % 1000000) * 1000; // Convert remaining microseconds to nanoseconds + + mutexlock(data->mutex); + while (current_timestamp < timestamp && !channel->abort) { + int ret = pthread_cond_timedwait(&data->cond, &data->mutex, &ts); + if (ret == ETIMEDOUT) { + fprintf(stderr, "Error: Timed out waiting for samples.\n"); + mutexunlock(data->mutex); + return 1; + } else if (ret != 0) { + fprintf(stderr, "Error: pthread_cond_timedwait failed: %s\n", strerror(ret)); + mutexunlock(data->mutex); + return 1; + } else { + current_timestamp = data->timestamp; + } + } + mutexunlock(data->mutex); } - mutexunlock(data->mutex); - return; + return 0; } uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel) @@ -267,9 +273,18 @@ uint64_t shm_td_iq_channel_get_current_sample(const ShmTDIQChannel *channel) return data->timestamp; } -bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel) +void shm_td_iq_channel_abort(ShmTDIQChannel *channel) { - return channel->data->is_connected; + ShmTDIQChannelData *data = channel->data; + mutexlock(data->mutex); + channel->abort = true; + condbroadcast(data->cond); + mutexunlock(data->mutex); +} + +bool shm_td_iq_channel_is_aborted(const ShmTDIQChannel *channel) +{ + return channel->abort; } void shm_td_iq_channel_destroy(ShmTDIQChannel *channel) @@ -278,9 +293,11 @@ void shm_td_iq_channel_destroy(ShmTDIQChannel *channel) size_t tx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_tx; size_t rx_buffer_size = CIRCULAR_BUFFER_SIZE * sizeof(sample_t) * data->num_antennas_rx; size_t total_size = sizeof(ShmTDIQChannelData) + tx_buffer_size + rx_buffer_size; - munmap(data, total_size); if (channel->type == IQ_CHANNEL_TYPE_SERVER) { + munmap(data, total_size); shm_unlink(channel->name); + } else { + munmap(data, total_size); } free(channel); } diff --git a/common/utils/shm_iq_channel/shm_td_iq_channel.h b/common/utils/shm_iq_channel/shm_td_iq_channel.h index 7c5a730914..b71720c1f9 100644 --- a/common/utils/shm_iq_channel/shm_td_iq_channel.h +++ b/common/utils/shm_iq_channel/shm_td_iq_channel.h @@ -118,16 +118,27 @@ void shm_td_iq_channel_produce_samples(ShmTDIQChannel *channel, uint64_t num_sam * @brief Wait until sample at the specified timestamp is available * * @param channel The ShmTDIQChannel structure. + * @param timestamp The timestamp for which to wait. + * @param timeout_uS The timeout in microseconds to wait for the sample. 0 means wait indefinitely. + * + * @return 0 if the sample is available, 1 if timed out */ -void shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp); +int shm_td_iq_channel_wait(ShmTDIQChannel *channel, uint64_t timestamp, uint64_t timeout_uS); /** - * @brief Checks if the IQ channel is connected. + * @brief Aborts the IQ channel causing the wait to return immediately * * @param channel The ShmTDIQChannel structure. - * @return True if the channel is connected, false otherwise. */ -bool shm_td_iq_channel_is_connected(const ShmTDIQChannel *channel); +void shm_td_iq_channel_abort(ShmTDIQChannel *channel); + +/** + * @brief Checks if the IQ channel is aborted. + * + * @param channel The ShmTDIQChannel structure. + * @return True if the channel is aborted, false otherwise. + */ +bool shm_td_iq_channel_is_aborted(const ShmTDIQChannel *channel); /** * @brief Destroys the shared memory IQ channel. diff --git a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c index f5170b8144..05140a0f63 100644 --- a/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c +++ b/common/utils/shm_iq_channel/tests/test_shm_td_iq_channel.c @@ -87,15 +87,6 @@ void server(void) int num_ant_tx = 1; int num_ant_rx = 1; ShmTDIQChannel *channel = shm_td_iq_channel_create(SHM_CHANNEL_NAME, num_ant_tx, num_ant_rx); - for (int i = 0; i < 10; i++) { - if (shm_td_iq_channel_is_connected(channel)) { - printf("Server connected\n"); - break; - } - printf("Waiting for client\n"); - sleep(1); - } - AssertFatal(shm_td_iq_channel_is_connected(channel), "Server failed to connect\n"); pthread_t producer_thread; int ret = pthread_create(&producer_thread, NULL, produce_symbols, channel); @@ -104,7 +95,7 @@ void server(void) uint64_t timestamp = 0; int iq_contents = 0; while (timestamp < num_samples_per_update * num_updates) { - shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update); + shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update, 0); uint64_t target_timestamp = timestamp + num_samples_per_update; timestamp += num_samples_per_update; uint32_t iq_data[num_samples_per_update]; @@ -127,21 +118,12 @@ int client(void) { int total_errors = 0; ShmTDIQChannel *channel = shm_td_iq_channel_connect(SHM_CHANNEL_NAME, 10); - for (int i = 0; i < 10; i++) { - if (shm_td_iq_channel_is_connected(channel)) { - printf("Client connected\n"); - break; - } - printf("Waiting for server\n"); - sleep(1); - } - AssertFatal(shm_td_iq_channel_is_connected(channel), "Client failed to connect\n"); uint64_t timestamp = 0; int iq_contents = 0; while (timestamp < num_samples_per_update * num_updates) { - shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update); + shm_td_iq_channel_wait(channel, timestamp + num_samples_per_update, 0); // Server starts producing from second slot if (timestamp > num_samples_per_update) { uint64_t target_timestamp = timestamp - num_samples_per_update; diff --git a/common/utils/time_meas.h b/common/utils/time_meas.h index f7c2810b60..de79a0252d 100644 --- a/common/utils/time_meas.h +++ b/common/utils/time_meas.h @@ -193,6 +193,7 @@ static inline void merge_meas(time_stats_t *dst_ts, const time_stats_t *src_ts) return; dst_ts->trials += src_ts->trials; dst_ts->diff += src_ts->diff; + dst_ts->diff_square += src_ts->diff_square; if (src_ts->max > dst_ts->max) dst_ts->max = src_ts->max; } diff --git a/doc/CMakeLists.txt b/doc/CMakeLists.txt index 2b6c1dd0bb..7f46dc1517 100644 --- a/doc/CMakeLists.txt +++ b/doc/CMakeLists.txt @@ -1,4 +1,4 @@ -add_boolean_option(GENERATE_DOXYGEN False "Generate source code doc using doxygen" OFF) +add_boolean_option(GENERATE_DOXYGEN OFF "Generate source code doc using doxygen" OFF) if(GENERATE_DOXYGEN) find_package(Doxygen REQUIRED) diff --git a/doc/Doxyfile b/doc/Doxyfile index f128e3eb88..e67078a043 100644 --- a/doc/Doxyfile +++ b/doc/Doxyfile @@ -1021,7 +1021,6 @@ INPUT = \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_ue_context.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_management_procedures.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_context_management_procedures.c \ -@CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_common.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_decoder.h \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_management_procedures.c \ @CMAKE_CURRENT_SOURCE_DIR@/../openair3/S1AP/s1ap_eNB_handlers.h \ diff --git a/doc/README.md b/doc/README.md index f2049bac04..9bd89480af 100644 --- a/doc/README.md +++ b/doc/README.md @@ -118,6 +118,7 @@ Some directories under `radio` contain READMEs: - [IQPlayer](../radio/iqplayer/DOC/iqrecordplayer_usage.md), and [general documentation](./iqrecordplayer_usage.md) - [fhi_72](../radio/fhi_72/README.md) - [vrtsim](../radio/vrtsim/README.md) +- [rf_emulator](../radio/emulator/README.md) The other SDRs (AW2S, LimeSDR, ...) have no READMEs. diff --git a/executables/lte-ru.c b/executables/lte-ru.c index 326e55b288..eed5a90eb9 100644 --- a/executables/lte-ru.c +++ b/executables/lte-ru.c @@ -531,44 +531,6 @@ volatile late_control_e late_control=STATE_BURST_NORMAL; /* add fail safe for late command end */ -static void *emulatedRF_thread(void *param) { - RU_proc_t *proc = (RU_proc_t *) param; - int microsec = 500; // length of time to sleep, in miliseconds - int numerology = 0 ; - struct timespec req = {0}; - req.tv_sec = 0; - req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2; - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(1,&cpuset); - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - int policy; - int ret; - struct sched_param sparam; - memset(&sparam, 0, sizeof(sparam)); - sparam.sched_priority = sched_get_priority_max(SCHED_FIFO); - policy = SCHED_FIFO ; - pthread_setschedparam(pthread_self(), policy, &sparam); - wait_sync("emulatedRF_thread"); - - while(!oai_exit) { - nanosleep(&req, (struct timespec *)NULL); - - if(proc->emulate_rf_busy ) { - LOG_E(PHY,"rf being delayed in emulated RF\n"); - } - - proc->emulate_rf_busy = 1; - AssertFatal((ret=pthread_mutex_lock(&proc->mutex_emulateRF))==0,"mutex_lock returns %d\n",ret); - ++proc->instance_cnt_emulateRF; - pthread_cond_signal(&proc->cond_emulateRF); - AssertFatal((ret=pthread_mutex_unlock(&proc->mutex_emulateRF))==0,"mutex_unlock returns %d\n",ret); - } - - return 0; -} - - void rx_rf(RU_t *ru, int *frame, int *subframe) { @@ -586,17 +548,7 @@ void rx_rf(RU_t *ru, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 1 ); old_ts = proc->timestamp_rx; - if(ru->emulate_rf) { - wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - rxs = fp->samples_per_tti; - } else { - rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, - &ts, - rxp, - fp->samples_per_tti, - ru->nb_rx); - } + rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, fp->samples_per_tti, ru->nb_rx); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); ru->south_in_cnt++; @@ -613,11 +565,7 @@ void rx_rf(RU_t *ru, resynch=1; } - if(get_softmodem_params()->emulate_rf) { - proc->timestamp_rx = old_ts + fp->samples_per_tti; - } else { - proc->timestamp_rx = ts-ru->ts_offset; - } + proc->timestamp_rx = ts-ru->ts_offset; // AssertFatal(rxs == fp->samples_per_tti, // "rx_rf: Asked for %d samples, got %d from SDR\n",fp->samples_per_tti,rxs); @@ -1229,7 +1177,6 @@ void wakeup_L1s(RU_t *ru) { // pthread_mutex_unlock(&proc->mutex_RU); // LOG_D(PHY,"wakeup eNB top for for subframe %d\n", ru->proc.tti_rx); // ru->eNB_top(eNB_list[0],ru->proc.frame_rx,ru->proc.tti_rx,string); - ru->proc.emulate_rf_busy = 0; } @@ -1497,7 +1444,6 @@ static void *ru_thread_tx( void *param ) { L1_rxtx_proc_t *L1_proc; cpu_set_t cpuset; CPU_ZERO(&cpuset); - char filename[256]; thread_top_init("ru_thread_tx",1,400000,500000,500000); //CPU_SET(5, &cpuset); //pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); @@ -1530,24 +1476,12 @@ static void *ru_thread_tx( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru,frame_tx,tti_tx); - if(!(ru->emulate_rf)) { //if(!emulate_rf){ - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru,frame_tx,tti_tx,timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, frame_tx, tti_tx, timestamp_tx); - if (ru->fh_north_out) ru->fh_north_out(ru); - } else { - for (int i=0; inb_tx; i++) { - if(frame_tx == 2) { - sprintf(filename,"txdataF%d_frame%d_sf%d.m",i,frame_tx,tti_tx); - LOG_M(filename,"txdataF_frame",ru->common.txdataF_BF[i],fp->symbols_per_tti*fp->ofdm_symbol_size, 1, 1); - } - - if(frame_tx == 2 && tti_tx==0) { - sprintf(filename,"txdata%d_frame%d.m",i,frame_tx); - LOG_M(filename,"txdata_frame",ru->common.txdata[i],fp->samples_per_tti*10, 1, 1); - } - } - } + if (ru->fh_north_out) + ru->fh_north_out(ru); LOG_D(PHY,"ru_thread_tx: releasing RU TX in %d.%d\n", frame_tx, tti_tx); release_thread(&proc->mutex_eNBs,&proc->instance_cnt_eNBs,"ru_thread_tx"); @@ -1612,7 +1546,6 @@ static void *ru_thread( void *param ) { int ret; cpu_set_t cpuset; CPU_ZERO(&cpuset); - char filename[256]; // set default return value #if defined(PRE_SCD_THREAD) dlsch_ue_select_tbl_in_use = 1; @@ -1625,21 +1558,7 @@ static void *ru_thread( void *param ) { LOG_I(PHY,"thread ru created id=%ld\n", syscall(__NR_gettid)); LOG_I(PHY,"Starting RU %d (%s,%s),\n", ru->idx, NB_functions[ru->function], NB_timing[ru->if_timing]); - if(get_softmodem_params()->emulate_rf) { - phy_init_RU(ru); - - if (setup_RU_buffers(ru)!=0) { - LOG_I(PHY,"Exiting, cannot initialize RU Buffers\n"); - exit(-1); - } - - LOG_I(PHY, "Signaling main thread that RU %d is ready\n",ru->idx); - AssertFatal((ret=pthread_mutex_lock(ru->ru_mutex))==0,"mutex_lock returns %d\n",ret); - *ru->ru_mask &= ~(1<idx); - pthread_cond_signal(ru->ru_cond); - AssertFatal((ret=pthread_mutex_unlock(ru->ru_mutex))==0,"mutex_unlock returns %d\n",ret); - ru->state = RU_RUN; - } else if (ru->has_ctrl_prt == 0) { + if (ru->has_ctrl_prt == 0) { // There is no control port: start everything here LOG_I(PHY, "RU %d has no OAI ctrl port\n",ru->idx); @@ -1695,18 +1614,22 @@ static void *ru_thread( void *param ) { } else wait_sync("ru_thread"); LOG_D(PHY,"RU %d: Got start from control thread\n",ru->idx); - if(!(ru->emulate_rf)) { - if (ru->is_slave == 0) AssertFatal(ru->state == RU_RUN,"ru-%d state = %s != RU_RUN\n",ru->idx,ru_states[ru->state]); - else if (ru->is_slave == 1) AssertFatal(ru->state == RU_SYNC || ru->state == RU_RUN || - ru->state == RU_CHECK_SYNC,"ru %d state = %s != RU_SYNC or RU_RUN or RU_CHECK_SYNC\n",ru->idx,ru_states[ru->state]); + if (ru->is_slave == 0) + AssertFatal(ru->state == RU_RUN, "ru-%d state = %s != RU_RUN\n", ru->idx, ru_states[ru->state]); + else if (ru->is_slave == 1) + AssertFatal(ru->state == RU_SYNC || ru->state == RU_RUN || ru->state == RU_CHECK_SYNC, + "ru %d state = %s != RU_SYNC or RU_RUN or RU_CHECK_SYNC\n", + ru->idx, + ru_states[ru->state]); - // Start RF device if any - if (ru->start_rf) { - if (ru->start_rf(ru) != 0) - AssertFatal(1==0,"Could not start the RF device\n"); - else LOG_I(PHY,"RU %d rf device ready\n",ru->idx); - } else LOG_D(PHY,"RU %d no rf device\n",ru->idx); - } + // Start RF device if any + if (ru->start_rf) { + if (ru->start_rf(ru) != 0) + AssertFatal(1 == 0, "Could not start the RF device\n"); + else + LOG_I(PHY, "RU %d rf device ready\n", ru->idx); + } else + LOG_D(PHY, "RU %d no rf device\n", ru->idx); // if an asnych_rxtx thread exists // wakeup the thread because the devices are ready at this point @@ -1864,26 +1787,13 @@ static void *ru_thread( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru, proc->frame_tx, proc->tti_tx); - if(!(ru->emulate_rf)) { //if(!emulate_rf){ - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); - if ((ru->fh_north_out) && (ru->state!=RU_CHECK_SYNC)) ru->fh_north_out(ru); - } else { - for (int i=0; inb_tx; i++) { - if(proc->frame_tx == 2) { - sprintf(filename,"txdataF%d_frame%d_sf%d.m",i,proc->frame_tx,proc->tti_tx); - LOG_M(filename,"txdataF_frame",ru->common.txdataF_BF[i],ru->frame_parms->symbols_per_tti*ru->frame_parms->ofdm_symbol_size, 1, 1); - } + if ((ru->fh_north_out) && (ru->state != RU_CHECK_SYNC)) + ru->fh_north_out(ru); - if(proc->frame_tx == 2 && proc->tti_tx==0) { - sprintf(filename,"txdata%d_frame%d.m",i,proc->frame_tx); - LOG_M(filename,"txdata_frame",ru->common.txdata[i],ru->frame_parms->samples_per_tti*10, 1, 1); - } - } - } - - proc->emulate_rf_busy = 0; } #else @@ -1903,12 +1813,11 @@ static void *ru_thread( void *param ) { LOG_I(PHY, "Exiting ru_thread \n"); - if (!(ru->emulate_rf)) { - if (ru->stop_rf != NULL) { - if (ru->stop_rf(ru) != 0) - LOG_E(HW,"Could not stop the RF device\n"); - else LOG_I(PHY,"RU %d rf device stopped\n",ru->idx); - } + if (ru->stop_rf != NULL) { + if (ru->stop_rf(ru) != 0) + LOG_E(HW, "Could not stop the RF device\n"); + else + LOG_I(PHY, "RU %d rf device stopped\n", ru->idx); } return NULL; @@ -2128,12 +2037,12 @@ static void *rf_tx( void *param ) { // do OFDM if needed if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) ru->feptx_ofdm(ru,proc->frame_tx,proc->tti_tx); - if(!ru->emulate_rf) { - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) ru->fh_south_out(ru,proc->frame_tx,proc->tti_tx,proc->timestamp_tx); + // do outgoing fronthaul (south) if needed + if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) + ru->fh_south_out(ru, proc->frame_tx, proc->tti_tx, proc->timestamp_tx); - if (ru->fh_north_out) ru->fh_north_out(ru); - } + if (ru->fh_north_out) + ru->fh_north_out(ru); } if (release_thread(&proc->mutex_rf_tx,&proc->instance_cnt_rf_tx,"rf_tx") < 0) break; @@ -2205,7 +2114,7 @@ int start_write_thread(RU_t *ru) { void init_RU_proc(RU_t *ru) { int i=0; RU_proc_t *proc; - pthread_attr_t *attr_FH=NULL, *attr_FH1=NULL, *attr_prach=NULL, *attr_asynch=NULL, *attr_synch=NULL, *attr_emulateRF=NULL, *attr_ctrl=NULL, *attr_prach_br=NULL; + pthread_attr_t *attr_FH=NULL, *attr_FH1=NULL, *attr_prach=NULL, *attr_asynch=NULL, *attr_synch=NULL, *attr_ctrl=NULL, *attr_prach_br=NULL; //pthread_attr_t *attr_fep=NULL; LOG_I(PHY,"Initializing RU proc %d (%s,%s),\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing]); proc = &ru->proc; @@ -2215,7 +2124,6 @@ void init_RU_proc(RU_t *ru) { proc->instance_cnt_synch = -1; proc->instance_cnt_FH = -1; proc->instance_cnt_FH1 = -1; - proc->instance_cnt_emulateRF = -1; proc->instance_cnt_asynch_rxtx = -1; proc->instance_cnt_ru = -1; proc->instance_cnt_eNBs = -1; @@ -2232,20 +2140,17 @@ void init_RU_proc(RU_t *ru) { pthread_mutex_init( &proc->mutex_synch,NULL); pthread_mutex_init( &proc->mutex_FH,NULL); pthread_mutex_init( &proc->mutex_FH1,NULL); - pthread_mutex_init( &proc->mutex_emulateRF,NULL); pthread_mutex_init( &proc->mutex_eNBs, NULL); pthread_mutex_init( &proc->mutex_ru,NULL); pthread_cond_init( &proc->cond_prach, NULL); pthread_cond_init( &proc->cond_FH, NULL); pthread_cond_init( &proc->cond_FH1, NULL); - pthread_cond_init( &proc->cond_emulateRF, NULL); pthread_cond_init( &proc->cond_asynch_rxtx, NULL); pthread_cond_init( &proc->cond_synch,NULL); pthread_cond_init( &proc->cond_eNBs, NULL); pthread_cond_init( &proc->cond_ru_thread,NULL); pthread_attr_init( &proc->attr_FH); pthread_attr_init( &proc->attr_FH1); - pthread_attr_init( &proc->attr_emulateRF); pthread_attr_init( &proc->attr_prach); pthread_attr_init( &proc->attr_synch); pthread_attr_init( &proc->attr_asynch_rxtx); @@ -2289,9 +2194,6 @@ void init_RU_proc(RU_t *ru) { pthread_create( &proc->pthread_rf_tx, NULL, rf_tx, (void *)ru ); #endif - if (ru->emulate_rf) - pthread_create( &proc->pthread_emulateRF, attr_emulateRF, emulatedRF_thread, (void *)proc ); - if (get_thread_parallel_conf() == PARALLEL_RU_L1_SPLIT || get_thread_parallel_conf() == PARALLEL_RU_L1_TRX_SPLIT) pthread_create( &proc->pthread_FH1, attr_FH1, ru_thread_tx, (void *)ru ); diff --git a/executables/lte-softmodem.c b/executables/lte-softmodem.c index 6a7e607586..25a3c25aa9 100644 --- a/executables/lte-softmodem.c +++ b/executables/lte-softmodem.c @@ -138,7 +138,6 @@ RU_t **RCconfig_RU(int nb_RU,int nb_L1_inst,PHY_VARS_eNB ***eNB,uint64_t *ru_mas RU_t **RCconfig_RU(int nb_RU,int nb_L1_inst,PHY_VARS_eNB ***eNB,uint64_t *ru_mask,pthread_mutex_t *ru_mutex,pthread_cond_t *ru_cond); int transmission_mode=1; -int emulate_rf = 0; int numerology = 0; THREAD_STRUCT thread_struct; @@ -204,55 +203,6 @@ void nr_slot_select() {}; void NR_UL_indication(NR_UL_IND_t *UL_INFO) {}; void gNB_dlsch_ulsch_scheduler() {}; -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) { -#ifdef DEBUG - printf("difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#else - LOG_I(HW,"difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#endif -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { diff --git a/executables/lte-softmodem.h b/executables/lte-softmodem.h index 8c55f0dc65..db6136c351 100644 --- a/executables/lte-softmodem.h +++ b/executables/lte-softmodem.h @@ -127,7 +127,6 @@ extern int sync_var; extern int transmission_mode; extern double cpuf; -extern int emulate_rf; extern int numerology; extern int usrp_tx_thread; diff --git a/executables/lte-uesoftmodem.c b/executables/lte-uesoftmodem.c index ea48edd801..b212ceb3f1 100644 --- a/executables/lte-uesoftmodem.c +++ b/executables/lte-uesoftmodem.c @@ -174,55 +174,6 @@ instance_t CUuniqInstance=0; /* forward declarations */ void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]); -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) { -#ifdef DEBUG - printf("difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#else - LOG_I(HW,"difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -#endif -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { @@ -384,7 +335,8 @@ void set_default_frame_parms(LTE_DL_FRAME_PARMS *frame_parms[MAX_NUM_CCs]) { } } -void init_openair0(LTE_DL_FRAME_PARMS *frame_parms,int rxgain) { +static void init_openair0(LTE_DL_FRAME_PARMS *frame_parms, int rxgain) +{ int card; int i; diff --git a/executables/nr-cuup.c b/executables/nr-cuup.c index 6467683cac..403f78ddb3 100644 --- a/executables/nr-cuup.c +++ b/executables/nr-cuup.c @@ -37,7 +37,6 @@ RAN_CONTEXT_t RC; THREAD_STRUCT thread_struct; uint64_t downlink_frequency[MAX_NUM_CCs][4]; int32_t uplink_frequency_offset[MAX_NUM_CCs][4]; -int asn1_xer_print; int oai_exit = 0; instance_t CUuniqInstance = 0; diff --git a/executables/nr-gnb.c b/executables/nr-gnb.c index 345b58df7e..24870544b8 100644 --- a/executables/nr-gnb.c +++ b/executables/nr-gnb.c @@ -231,6 +231,9 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size output += print_meas_log(&gNB->dlsch_resource_mapping_stats, "DLSCH resource mapping", NULL, NULL, output,end-output); output += print_meas_log(&gNB->dlsch_precoding_stats, "DLSCH precoding", NULL, NULL, output,end-output); output += print_meas_log(&gNB->phy_proc_rx, "L1 Rx processing", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_deinterleave, "UL segment deinterleaving", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_rate_unmatch, "UL segment rate recovery", NULL, NULL, output, end - output); + output += print_meas_log(&gNB->ts_ldpc_decode, "UL segments decoding", NULL, NULL, output, end - output); output += print_meas_log(&gNB->ul_indication_stats, "UL Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->slot_indication_stats, "Slot Indication", NULL, NULL, output, end - output); output += print_meas_log(&gNB->rx_pusch_stats, "PUSCH inner-receiver", NULL, NULL, output, end - output); @@ -288,6 +291,9 @@ void *nrL1_stats_thread(void *param) { reset_meas(&gNB->phy_proc_tx); reset_meas(&gNB->dlsch_encoding_stats); reset_meas(&gNB->phy_proc_rx); + reset_meas(&gNB->ts_deinterleave); + reset_meas(&gNB->ts_rate_unmatch); + reset_meas(&gNB->ts_ldpc_decode); reset_meas(&gNB->ul_indication_stats); reset_meas(&gNB->slot_indication_stats); reset_meas(&gNB->rx_pusch_stats); diff --git a/executables/nr-ru.c b/executables/nr-ru.c index c0da88e634..de3a4b7875 100644 --- a/executables/nr-ru.c +++ b/executables/nr-ru.c @@ -63,7 +63,6 @@ static int DEFRUTPCORES[] = {-1,-1,-1,-1}; #include - #include "T.h" #include "nfapi_interface.h" #include @@ -74,63 +73,69 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg); /*************************************************************/ /* Functions to attach and configure RRU */ -int attach_rru(RU_t *ru) { - ssize_t msg_len,len; +int attach_rru(RU_t *ru) +{ RRU_CONFIG_msg_t rru_config_msg; int received_capabilities=0; wait_gNBs(); // Wait for capabilities while (received_capabilities==0) { - memset((void *)&rru_config_msg,0,sizeof(rru_config_msg)); - rru_config_msg.type = RAU_tick; - rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE; + rru_config_msg = (RRU_CONFIG_msg_t){.type = RAU_tick, .len = sizeof(rru_config_msg.msg)}; LOG_D(PHY, "Sending RAU tick to RRU %d\n", ru->idx); AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1), "RU %d cannot access remote radio\n",ru->idx); - msg_len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - + ssize_t msg_len = rru_config_msg.len + sizeof(RRU_capabilities_t); // wait for answer with timeout - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + ssize_t len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, msg_len); + if (len < 0) { LOG_D(PHY, "Waiting for RRU %d\n", ru->idx); } else if (rru_config_msg.type == RRU_capabilities) { - AssertFatal(rru_config_msg.len==msg_len,"Received capabilities with incorrect length (%d!=%d)\n",(int)rru_config_msg.len,(int)msg_len); - LOG_I(PHY,"Received capabilities from RRU %d (len %d/%d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n",ru->idx, - (int)rru_config_msg.len,(int)msg_len, - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_pdschReferenceSignalPower[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->max_rxgain[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_tx[0], - ((RRU_capabilities_t *)&rru_config_msg.msg[0])->nb_rx[0]); + AssertFatal(rru_config_msg.len == msg_len, + "Received capabilities with incorrect length (%ld!=%ld)\n", + rru_config_msg.len, + msg_len); + RRU_capabilities_t *cap = (RRU_capabilities_t *)rru_config_msg.msg; + LOG_I(PHY, + "Received capabilities from RRU %d (len %ld/%ld, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx " + "%d, nb_rx %d)\n", + ru->idx, + rru_config_msg.len, + msg_len, + cap->num_bands, + cap->max_pdschReferenceSignalPower[0], + cap->max_rxgain[0], + cap->nb_tx[0], + cap->nb_rx[0]); received_capabilities=1; } else { LOG_E(PHY,"Received incorrect message %d from RRU %d\n",rru_config_msg.type,ru->idx); } } - configure_ru(ru, (RRU_capabilities_t *)&rru_config_msg.msg[0]); + configure_ru(ru, (RRU_capabilities_t *)rru_config_msg.msg); rru_config_msg.type = RRU_config; rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t); - LOG_I(PHY,"Sending Configuration to RRU %d (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n",ru->idx, - ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]); + RRU_config_t *conf = (RRU_config_t *)rru_config_msg.msg; + LOG_I(PHY, + "Sending Configuration to RRU %d (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL " + "%d,3/4FS %d, prach_FO %d, prach_CI %d)\n", + ru->idx, + conf->num_bands, + conf->band_list[0], + conf->tx_freq[0], + conf->rx_freq[0], + conf->att_tx[0], + conf->att_rx[0], + conf->N_RB_DL[0], + conf->N_RB_UL[0], + conf->threequarter_fs[0], + conf->prach_FreqOffset[0], + conf->prach_ConfigIndex[0]); AssertFatal((ru->ifdevice.trx_ctlsend_func(&ru->ifdevice,&rru_config_msg,rru_config_msg.len)!=-1), "RU %d failed send configuration to remote radio\n",ru->idx); - - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, sizeof(rru_config_msg.msg)); + if (len < 0) { LOG_I(PHY,"Waiting for RRU %d\n",ru->idx); } else if (rru_config_msg.type == RRU_config_ok) { LOG_I(PHY, "RRU_config_ok received\n"); @@ -142,58 +147,57 @@ int attach_rru(RU_t *ru) { } int connect_rau(RU_t *ru) { - RRU_CONFIG_msg_t rru_config_msg; - ssize_t msg_len; - int tick_received = 0; - int configuration_received = 0; - RRU_capabilities_t *cap; - int i; - int len; + RRU_CONFIG_msg_t rru_config_msg; // wait for RAU_tick + int tick_received = 0; while (tick_received == 0) { - msg_len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE; - - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - msg_len))<0) { + ssize_t msg_len = sizeof(rru_config_msg.msg); + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, msg_len); + if (len < 0) { LOG_I(PHY,"Waiting for RAU\n"); } else { if (rru_config_msg.type == RAU_tick) { LOG_I(PHY,"Tick received from RAU\n"); tick_received = 1; - } else LOG_E(PHY,"Received erroneous message (%d)from RAU, expected RAU_tick\n",rru_config_msg.type); + } else + LOG_E(PHY, "Received erroneous message (%d)from RAU, expected RAU_tick\n", rru_config_msg.type); } } // send capabilities rru_config_msg.type = RRU_capabilities; rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_capabilities_t); - cap = (RRU_capabilities_t *)&rru_config_msg.msg[0]; - LOG_I(PHY,"Sending Capabilities (len %d, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n", - (int)rru_config_msg.len,ru->num_bands,ru->max_pdschReferenceSignalPower,ru->max_rxgain,ru->nb_tx,ru->nb_rx); + RRU_capabilities_t *cap = (RRU_capabilities_t *)rru_config_msg.msg; + LOG_I(PHY, + "Sending Capabilities (len %ld, num_bands %d,max_pdschReferenceSignalPower %d, max_rxgain %d, nb_tx %d, nb_rx %d)\n", + rru_config_msg.len, + ru->num_bands, + ru->max_pdschReferenceSignalPower, + ru->max_rxgain, + ru->nb_tx, + ru->nb_rx); switch (ru->function) { case NGFI_RRU_IF4p5: - cap->FH_fmt = OAI_IF4p5_only; + cap->FH_fmt = OAI_IF4p5_only; break; case NGFI_RRU_IF5: - cap->FH_fmt = OAI_IF5_only; + cap->FH_fmt = OAI_IF5_only; break; case MBP_RRU_IF5: - cap->FH_fmt = MBP_IF5; + cap->FH_fmt = MBP_IF5; break; default: - AssertFatal(1==0,"RU_function is unknown %d\n",RC.ru[0]->function); + AssertFatal(false, "RU_function is unknown %d\n", RC.ru[0]->function); break; } - cap->num_bands = ru->num_bands; - - for (i=0; inum_bands; i++) { + cap->num_bands = ru->num_bands; + for (int i = 0; i < ru->num_bands; i++) { LOG_I(PHY,"Band %d: nb_rx %d nb_tx %d pdschReferenceSignalPower %d rxgain %d\n", ru->band[i],ru->nb_rx,ru->nb_tx,ru->max_pdschReferenceSignalPower,ru->max_rxgain); cap->band_list[i] = ru->band[i]; @@ -208,25 +212,28 @@ int connect_rau(RU_t *ru) { // wait for configuration rru_config_msg.len = sizeof(RRU_CONFIG_msg_t)-MAX_RRU_CONFIG_SIZE+sizeof(RRU_config_t); + int configuration_received = 0; while (configuration_received == 0) { - if ((len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, - &rru_config_msg, - rru_config_msg.len))<0) { + int len = ru->ifdevice.trx_ctlrecv_func(&ru->ifdevice, &rru_config_msg, rru_config_msg.len); + if (len < 0) { LOG_I(PHY,"Waiting for configuration from RAU\n"); } else { - LOG_I(PHY,"Configuration received from RAU (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL %d,3/4FS %d, prach_FO %d, prach_CI %d)\n", - ((RRU_config_t *)&rru_config_msg.msg[0])->num_bands, - ((RRU_config_t *)&rru_config_msg.msg[0])->band_list[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->tx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->rx_freq[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_tx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->att_rx[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_DL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->N_RB_UL[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->threequarter_fs[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_FreqOffset[0], - ((RRU_config_t *)&rru_config_msg.msg[0])->prach_ConfigIndex[0]); - configure_rru(ru, (void *)&rru_config_msg.msg[0]); + RRU_config_t *conf = (RRU_config_t *)rru_config_msg.msg; + LOG_I(PHY, + "Configuration received from RAU (num_bands %d,band0 %d,txfreq %u,rxfreq %u,att_tx %d,att_rx %d,N_RB_DL %d,N_RB_UL " + "%d,3/4FS %d, prach_FO %d, prach_CI %d)\n", + conf->num_bands, + conf->band_list[0], + conf->tx_freq[0], + conf->rx_freq[0], + conf->att_tx[0], + conf->att_rx[0], + conf->N_RB_DL[0], + conf->N_RB_UL[0], + conf->threequarter_fs[0], + conf->prach_FreqOffset[0], + conf->prach_ConfigIndex[0]); + configure_rru(ru, (void *)rru_config_msg.msg); configuration_received = 1; } } @@ -238,15 +245,23 @@ int connect_rau(RU_t *ru) { // southbound IF5 fronthaul for 16-bit OAI format void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); + if (ru == RC.ru[0]) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); int offset = ru->nr_frame_parms->get_samples_slot_timestamp(slot,ru->nr_frame_parms,0); void *buffs[ru->nb_tx]; for (int aid = 0; aid < ru->nb_tx; aid++) buffs[aid] = (void*)&ru->common.txdata[aid][offset]; struct timespec txmeas; clock_gettime(CLOCK_MONOTONIC, &txmeas); - LOG_D(NR_PHY,"IF5 TX %d.%d, TS %llu, buffs[0] %p, buffs[1] %p ener0 %f dB, tx start %d\n",frame,slot,(unsigned long long)timestamp,buffs[0],buffs[1], - 10*log10((double)signal_energy(buffs[0],ru->nr_frame_parms->get_samples_per_slot(slot,ru->nr_frame_parms))),(int)txmeas.tv_nsec); + LOG_D(NR_PHY, + "IF5 TX %d.%d, TS %lu, buffs[0] %p, buffs[1] %p ener0 %f dB, tx start %d\n", + frame, + slot, + timestamp, + buffs[0], + buffs[1], + 10 * log10((double)signal_energy(buffs[0], ru->nr_frame_parms->get_samples_per_slot(slot, ru->nr_frame_parms))), + (int)txmeas.tv_nsec); ru->ifdevice.trx_write_func2(&ru->ifdevice, timestamp, buffs, @@ -258,13 +273,12 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { // southbound IF4p5 fronthaul void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp) { - nfapi_nr_config_request_scf_t *cfg = &ru->config; - - if (ru == RC.ru[0]) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx&0xffffffff ); + if (ru == RC.ru[0]) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_TST, ru->proc.timestamp_tx & 0xffffffff); LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx); - if ((nr_slot_select(cfg,ru->proc.frame_tx,ru->proc.tti_tx)&NR_DOWNLINK_SLOT) > 0) + if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0) send_IF4p5(ru,frame, slot, IF4p5_PDLFFT); } @@ -281,11 +295,11 @@ void fh_if5_south_in(RU_t *ru, VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 1 ); start_meas(&ru->rx_fhaul); - ru->ifdevice.trx_read_func2(&ru->ifdevice,&proc->timestamp_rx,NULL,fp->get_samples_per_slot(*tti,fp)); - if (proc->first_rx == 1) ru->ts_offset = proc->timestamp_rx; - proc->frame_rx = ((proc->timestamp_rx-ru->ts_offset) / (fp->samples_per_subframe*10))&1023; - proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx-ru->ts_offset,fp); - + ru->ifdevice.trx_read_func2(&ru->ifdevice, &proc->timestamp_rx, NULL, fp->get_samples_per_slot(*tti, fp)); + if (proc->first_rx == 1) + ru->ts_offset = proc->timestamp_rx; + proc->frame_rx = ((proc->timestamp_rx - ru->ts_offset) / (fp->samples_per_subframe * 10)) & 1023; + proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx - ru->ts_offset, fp); if (proc->first_rx == 0) { if (proc->tti_rx != *tti) { @@ -311,8 +325,26 @@ void fh_if5_south_in(RU_t *ru, struct timespec rxmeas; clock_gettime(CLOCK_MONOTONIC, &rxmeas); double fhtime = ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0); - if (fhtime > 800) LOG_W(PHY,"IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %d\n",*frame,*tti,proc->frame_rx,proc->tti_rx,proc->first_rx,ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0),(int)rxmeas.tv_nsec); - else LOG_D(PHY,"IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %d\n",*frame,*tti,proc->frame_rx,proc->tti_rx,proc->first_rx,ru->rx_fhaul.p_time/(cpu_freq_GHz*1000.0),(int)rxmeas.tv_nsec); + if (fhtime > 800) + LOG_W(PHY, + "IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %ld\n", + *frame, + *tti, + proc->frame_rx, + proc->tti_rx, + proc->first_rx, + ru->rx_fhaul.p_time / (cpu_freq_GHz * 1000.0), + rxmeas.tv_nsec); + else + LOG_D(PHY, + "IF5 %d.%d => RX %d.%d first_rx %d: time %f, rxstart %ld\n", + *frame, + *tti, + proc->frame_rx, + proc->tti_rx, + proc->first_rx, + ru->rx_fhaul.p_time / (cpu_freq_GHz * 1000.0), + rxmeas.tv_nsec); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, proc->timestamp_rx&0xffffffff ); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_RECV_IF5, 0 ); @@ -329,21 +361,16 @@ void fh_if4p5_south_in(RU_t *ru, uint32_t symbol_number=0; uint32_t symbol_mask_full=0; - /* - if ((fp->frame_type == TDD) && (subframe_select(fp,*slot)==SF_S)) - symbol_mask_full = (1<ul_symbols_in_S_subframe)-1; - else - symbol_mask_full = (1<symbols_per_slot)-1; - - AssertFatal(proc->symbol_mask[*slot]==0,"rx_fh_if4p5: proc->symbol_mask[%d] = %x\n",*slot,proc->symbol_mask[*slot]);*/ do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!! recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number); if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<first_rx==0) && (f!=*frame)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received frame %d != expected %d\n",f,*frame); + if ((proc->first_rx == 0) && (f != *frame)) + LOG_E(PHY, "rx_fh_if4p5: PULTICK received frame %d != expected %d\n", f, *frame); - if ((proc->first_rx==0) && (sl!=*slot)) LOG_E(PHY,"rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n",sl,*slot,proc->first_rx); + if ((proc->first_rx == 0) && (sl != *slot)) + LOG_E(PHY, "rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n", sl, *slot, proc->first_rx); break; } else if (packet_type == IF4p5_PRACH) { @@ -359,7 +386,7 @@ void fh_if4p5_south_in(RU_t *ru, proc->timestamp_rx = (proc->frame_rx * fp->samples_per_subframe * 10) + fp->get_samples_slot_timestamp(proc->tti_rx, fp, 0); // proc->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_subframe); proc->tti_tx = (sl+ru->sl_ahead)%fp->slots_per_frame; - proc->frame_tx = (sl>(fp->slots_per_frame-1-(ru->sl_ahead))) ? (f+1)&1023 : f; + proc->frame_tx = (sl > (fp->slots_per_frame - 1 - (ru->sl_ahead))) ? (f + 1) & 1023 : f; if (proc->first_rx == 0) { if (proc->tti_rx != *slot) { @@ -394,20 +421,12 @@ void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) { NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; RU_proc_t *proc = &ru->proc; uint16_t packet_type; - uint32_t symbol_number,symbol_mask,prach_rx; - // uint32_t got_prach_info=0; - symbol_number = 0; - symbol_mask = (1<<(fp->symbols_per_slot))-1; - prach_rx = 0; + uint32_t symbol_number = 0; + uint32_t symbol_mask = (1 << fp->symbols_per_slot) - 1; + uint32_t prach_rx = 0; do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!! recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number); - - // grab first prach information for this new subframe - /*if (got_prach_info==0) { - prach_rx = is_prach_subframe(fp, proc->frame_rx, proc->tti_rx); - got_prach_info = 1; - }*/ if (proc->first_rx != 0) { *frame = proc->frame_rx; *slot = proc->tti_rx; @@ -417,22 +436,19 @@ void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) { LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame); exit_fun("Exiting"); } - if (proc->tti_rx != *slot) { LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot); exit_fun("Exiting"); } } - if (packet_type == IF4p5_PULFFT) symbol_mask &= (~(1< 0) || (prach_rx >0)); // haven't received all PUSCH symbols and PRACH information + if (packet_type == IF4p5_PULFFT) + symbol_mask &= ~(1 << symbol_number); + else if (packet_type == IF4p5_PRACH) + prach_rx &= ~0x1; + } while (symbol_mask > 0 || prach_rx > 0); // haven't received all PUSCH symbols and PRACH information } - - - - /*************************************************************/ /* Input Fronthaul from North RRU */ @@ -459,26 +475,9 @@ void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot) { } } -void fh_if5_north_asynch_in(RU_t *ru,int *frame,int *slot) { - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - RU_proc_t *proc = &ru->proc; - int tti_tx,frame_tx; - openair0_timestamp timestamp_tx = 0; +void fh_if5_north_asynch_in(RU_t *ru, int *frame, int *slot) +{ AssertFatal(1 == 0, "Shouldn't get here\n"); - frame_tx = (timestamp_tx / (fp->samples_per_subframe*10))&1023; - uint32_t idx_sf = timestamp_tx / fp->samples_per_subframe; - tti_tx = (idx_sf * fp->slots_per_subframe + (int)round((float)(timestamp_tx % fp->samples_per_subframe) / fp->samples_per_slot0))%(fp->slots_per_frame); - - if (proc->first_tx != 0) { - *slot = tti_tx; - *frame = frame_tx; - proc->first_tx = 0; - } else { - AssertFatal(tti_tx == *slot, - "tti_tx %d is not what we expect %d\n",tti_tx,*slot); - AssertFatal(frame_tx == *frame, - "frame_tx %d is not what we expect %d\n",frame_tx,*frame); - } } void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { @@ -486,17 +485,18 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { nfapi_nr_config_request_scf_t *cfg = &ru->config; RU_proc_t *proc = &ru->proc; uint16_t packet_type; - uint32_t symbol_number,symbol_mask,symbol_mask_full=0; + uint32_t symbol_mask_full = 0; int slot_tx,frame_tx; LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru); - symbol_number = 0; - symbol_mask = 0; + uint32_t symbol_number = 0; + uint32_t symbol_mask = 0; // symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<dl_symbols_in_S_subframe) : (1<symbols_per_slot))-1; do { recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number); - if (((nr_slot_select(cfg,frame_tx,slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0)) start_meas(&ru->rx_fhaul); + if (((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0)) + start_meas(&ru->rx_fhaul); LOG_D(PHY,"slot %d (%d): frame %d, slot %d, symbol %d\n", *slot,nr_slot_select(cfg,frame_tx,*slot),frame_tx,slot_tx,symbol_number); @@ -505,7 +505,6 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { *frame = frame_tx; *slot = slot_tx; proc->first_tx = 0; - //symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<dl_symbols_in_S_subframe) : (1<symbols_per_slot))-1; } else { AssertFatal(frame_tx == *frame, "frame_tx %d is not what we expect %d\n",frame_tx,*frame); @@ -515,18 +514,22 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { if (packet_type == IF4p5_PDLFFT) { symbol_mask = symbol_mask | (1<0) stop_meas(&ru->rx_fhaul); + if ((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) + stop_meas(&ru->rx_fhaul); proc->tti_tx = slot_tx; proc->frame_tx = frame_tx; - if ((frame_tx == 0)&&(slot_tx == 0)) proc->frame_tx_unwrap += 1024; + if (frame_tx == 0 && slot_tx == 0) + proc->frame_tx_unwrap += 1024; - proc->timestamp_tx = (((uint64_t)frame_tx + (uint64_t)proc->frame_tx_unwrap) * fp->samples_per_subframe * 10) + fp->get_samples_slot_timestamp(slot_tx, fp, 0); - LOG_D(PHY,"RU %d/%d TST %llu, frame %d, subframe %d\n",ru->idx,0,(long long unsigned int)proc->timestamp_tx,frame_tx,slot_tx); + proc->timestamp_tx = + ((uint64_t)frame_tx + proc->frame_tx_unwrap) * fp->samples_per_subframe * 10 + fp->get_samples_slot_timestamp(slot_tx, fp, 0); + LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx); // dump VCD output for first RU in list if (ru == RC.ru[0]) { @@ -544,48 +547,20 @@ void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) { void fh_if5_north_out(RU_t *ru) { /// **** send_IF5 of rxdata to BBU **** /// VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 1 ); - AssertFatal(1==0,"Shouldn't get here\n"); - VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_SEND_IF5, 0 ); + AssertFatal(1 == 0, "Shouldn't get here\n"); } // RRU IF4p5 northbound interface (RX) void fh_if4p5_north_out(RU_t *ru) { RU_proc_t *proc=&ru->proc; + if (ru->idx == 0) + VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx); - //NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - //const int subframe = proc->tti_rx; - if (ru->idx==0) VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TTI_NUMBER_RX0_RU, proc->tti_rx ); - - /* - if ((fp->frame_type == TDD) && (subframe_select(fp,subframe)!=SF_UL)) { - /// **** in TDD during DL send_IF4 of ULTICK to RCC **** /// - send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULTICK); - return; - }*/ start_meas(&ru->tx_fhaul); send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT); stop_meas(&ru->tx_fhaul); } -void *emulatedRF_thread(void *param) { - RU_proc_t *proc = (RU_proc_t *) param; - int microsec = 500; // length of time to sleep, in miliseconds - struct timespec req = {0}; - req.tv_sec = 0; - req.tv_nsec = (numerology>0)? ((microsec * 1000L)/numerology):(microsec * 1000L)*2; - wait_sync("emulatedRF_thread"); - - while(!oai_exit) { - nanosleep(&req, (struct timespec *)NULL); - pthread_mutex_lock(&proc->mutex_emulateRF); - ++proc->instance_cnt_emulateRF; - pthread_mutex_unlock(&proc->mutex_emulateRF); - pthread_cond_signal(&proc->cond_emulateRF); - } - - return 0; -} - static void rx_rf(RU_t *ru, int *frame, int *slot) { RU_proc_t *proc = &ru->proc; @@ -606,20 +581,11 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) openair0_timestamp ts; unsigned int rxs; - if(emulate_rf) { - wait_on_condition(&proc->mutex_emulateRF,&proc->cond_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - release_thread(&proc->mutex_emulateRF,&proc->instance_cnt_emulateRF,"emulatedRF_thread"); - rxs = samples_per_slot; - ts = old_ts + rxs; - } else { - rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); - } + rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME( VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_READ, 0 ); proc->timestamp_rx = ts-ru->ts_offset; - //AssertFatal(rxs == fp->samples_per_subframe, - //"rx_rf: Asked for %d samples, got %d from USRP\n",fp->samples_per_subframe,rxs); if (rxs != samples_per_slot) LOG_E(PHY, "rx_rf: Asked for %d samples, got %d from USRP\n", samples_per_slot, rxs); @@ -636,23 +602,26 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) } } - //compute system frame number (SFN) according to O-RAN-WG4-CUS.0-v02.00 (using alpha=beta=0) - // this assumes that the USRP has been synchronized to the GPS time - // OAI uses timestamps in sample time stored in int64_t, but it will fit in double precision for many years to come. - double gps_sec = ((double) ts)/cfg->sample_rate; - //proc->frame_rx = ((int64_t) (gps_sec/0.01)) & 1023; + // compute system frame number (SFN) according to O-RAN-WG4-CUS.0-v02.00 (using alpha=beta=0) + // this assumes that the USRP has been synchronized to the GPS time + // OAI uses timestamps in sample time stored in int64_t, but it will fit in double precision for many years to come. + double gps_sec = ((double)ts) / cfg->sample_rate; // in fact the following line is the same as long as the timestamp_rx is synchronized to GPS. proc->frame_rx = (proc->timestamp_rx / (fp->samples_per_subframe*10))&1023; proc->tti_rx = fp->get_slot_from_timestamp(proc->timestamp_rx,fp); // synchronize first reception to frame 0 subframe 0 - LOG_D(PHY,"RU %d/%d TS %ld, GPS %f, SR %f, frame %d, slot %d.%d / %d\n", + LOG_D(PHY, + "RU %d/%d TS %ld, GPS %f, SR %f, frame %d, slot %d.%d / %d\n", ru->idx, 0, - ts, //(unsigned long long int)(proc->timestamp_rx+ru->ts_offset), - gps_sec, - cfg->sample_rate, - proc->frame_rx,proc->tti_rx,proc->tti_tx,fp->slots_per_frame); + ts, + gps_sec, + cfg->sample_rate, + proc->frame_rx, + proc->tti_rx, + proc->tti_tx, + fp->slots_per_frame); // dump VCD output for first RU in list if (ru == RC.ru[0]) { @@ -662,15 +631,19 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) if (proc->first_rx == 0) { if (proc->tti_rx != *slot) { - LOG_E(PHY,"Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->tti_rx %d, slot %d)\n",(long long unsigned int)proc->timestamp_rx,proc->tti_rx,*slot); + LOG_E(PHY, + "Received Timestamp (%lu) doesn't correspond to the time we think it is (proc->tti_rx %d, slot %d)\n", + proc->timestamp_rx, + proc->tti_rx, + *slot); exit_fun("Exiting"); } if (proc->frame_rx != *frame) { LOG_E(PHY, - "Received Timestamp (%llu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d, proc->tti_rx %d, " + "Received Timestamp (%lu) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d, proc->tti_rx %d, " "slot %d)\n", - (long long unsigned int)proc->timestamp_rx, + proc->timestamp_rx, proc->frame_rx, *frame, proc->tti_rx, @@ -683,10 +656,8 @@ static void rx_rf(RU_t *ru, int *frame, int *slot) *slot = proc->tti_rx; } - if (!emulate_rf) { - metadata mt = {.slot = *slot, .frame = *frame}; - gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt); - } + metadata mt = {.slot = *slot, .frame = *frame}; + gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME( VCD_SIGNAL_DUMPER_VARIABLES_TRX_TS, (proc->timestamp_rx+ru->ts_offset)&0xffffffff ); @@ -720,6 +691,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot) } } break; + case RU_GPIO_CONTROL_INTERDIGITAL: { // the beam index is written in bits 8-10 of the flags // bit 11 enables the gpio programming @@ -805,9 +777,7 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) flags_gpio = get_gpio_flags(ru, slot); const int flags = flags_burst | (flags_gpio << 4); - - if (proc->first_tx == 1) - proc->first_tx = 0; + proc->first_tx = 0; VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_TRX_WRITE_FLAGS, flags); VCD_SIGNAL_DUMPER_DUMP_VARIABLE_BY_NAME(VCD_SIGNAL_DUMPER_VARIABLES_FRAME_NUMBER_TX0_RU, frame); @@ -828,10 +798,10 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) nt, flags); LOG_D(PHY, - "[TXPATH] RU %d tx_rf, writing to TS %llu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " + "[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, " "returned %d, E %f\n", ru->idx, - (long long unsigned int)(timestamp + ru->ts_offset - sf_extension), + timestamp + ru->ts_offset - sf_extension, frame, slot, proc->frame_tx_unwrap, @@ -841,7 +811,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp) txs, 10 * log10((double)signal_energy(txp[0], siglen + sf_extension))); VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_TRX_WRITE, 0); - // AssertFatal(txs == 0,"trx write function error %d\n", txs); } static void fill_rf_config(RU_t *ru, char *rf_config_file) @@ -880,12 +849,12 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { - cfg->tx_freq[i] = (double)fp->dl_CarrierFreq; + cfg->tx_freq[i] = fp->dl_CarrierFreq; } else if (ru->if_freq_offset) { - cfg->tx_freq[i] = (double)(ru->if_frequency); + cfg->tx_freq[i] = ru->if_frequency; LOG_I(PHY, "Setting IF TX frequency to %lu Hz with IF TX frequency offset %d Hz\n", ru->if_frequency, ru->if_freq_offset); } else { - cfg->tx_freq[i] = (double)ru->if_frequency; + cfg->tx_freq[i] = ru->if_frequency; } cfg->tx_gain[i] = ru->att_tx; @@ -895,12 +864,12 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file) for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { if (ru->if_frequency == 0) { - cfg->rx_freq[i] = (double)fp->ul_CarrierFreq; + cfg->rx_freq[i] = fp->ul_CarrierFreq; } else if (ru->if_freq_offset) { - cfg->rx_freq[i] = (double)(ru->if_frequency + ru->if_freq_offset); + cfg->rx_freq[i] = ru->if_frequency + ru->if_freq_offset; LOG_I(PHY, "Setting IF RX frequency to %lu Hz with IF RX frequency offset %d Hz\n", ru->if_frequency, ru->if_freq_offset); } else { - cfg->rx_freq[i] = (double)(ru->if_frequency+fp->ul_CarrierFreq-fp->dl_CarrierFreq); + cfg->rx_freq[i] = ru->if_frequency + fp->ul_CarrierFreq - fp->dl_CarrierFreq; } cfg->rx_gain[i] = ru->max_rxgain-ru->att_rx; @@ -941,17 +910,11 @@ static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_ antennas are mapped to successive RF chains on the same card. */ int setup_RU_buffers(RU_t *ru) { - int card,ant; - //uint16_t N_TA_offset = 0; - NR_DL_FRAME_PARMS *fp; + if (!ru) + return (-1); + + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; nfapi_nr_config_request_scf_t *config = &ru->config; - - if (ru) { - fp = ru->nr_frame_parms; - } else { - return(-1); - } - int mu = config->ssb_config.scs_common.value; int N_RB = config->carrier_config.dl_grid_size[config->ssb_config.scs_common.value].value; @@ -963,8 +926,8 @@ int setup_RU_buffers(RU_t *ru) if (ru->openair0_cfg.mmapped_dma == 1) { // replace RX signal buffers with mmaped HW versions for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) { - card = i / 4; - ant = i % 4; + int card = i / 4; + int ant = i % 4; LOG_D(PHY, "Mapping RU id %u, rx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.rxdata[i]); ru->common.rxdata[i] = ru->openair0_cfg.rxbase[ru->rf_map.chain + ant]; @@ -975,8 +938,8 @@ int setup_RU_buffers(RU_t *ru) } for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { - card = i / 4; - ant = i % 4; + int card = i / 4; + int ant = i % 4; LOG_D(PHY, "Mapping RU id %u, tx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant); free(ru->common.txdata[i]); ru->common.txdata[i] = ru->openair0_cfg.txbase[ru->rf_map.chain + ant]; @@ -985,8 +948,9 @@ int setup_RU_buffers(RU_t *ru) ru->common.txdata[i][j] = 16 - j; } } - } else { // not memory-mapped DMA - //nothing to do, everything already allocated in lte_init + } else { + // not memory-mapped DMA + // nothing to do, everything already allocated in lte_init } return(0); @@ -996,58 +960,21 @@ void ru_tx_func(void *param) { processingData_RU_t *info = (processingData_RU_t *) param; RU_t *ru = info->ru; - NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; int frame_tx = info->frame_tx; int slot_tx = info->slot_tx; - int print_frame = 8; - char filename[40]; // do TX front-end processing if needed (precoding and/or IDFTs) if (ru->feptx_prec) ru->feptx_prec(ru,frame_tx,slot_tx); // do OFDM with/without TX front-end processing if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->feptx_ofdm)) + if (ru->fh_north_asynch_in == NULL && ru->feptx_ofdm) ru->feptx_ofdm(ru, frame_tx, slot_tx); - if(!emulate_rf) { - // do outgoing fronthaul (south) if needed - if ((ru->fh_north_asynch_in == NULL) && (ru->fh_south_out)) - ru->fh_south_out(ru, frame_tx, slot_tx, info->timestamp_tx); - - if (ru->fh_north_out) - ru->fh_north_out(ru); - } else { - if(frame_tx == print_frame) { - for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) { - if(slot_tx == 0) { - sprintf(filename,"gNBdataF_frame%d_sl%d.m", print_frame, slot_tx); - LOG_M(filename,"txdataF_frame",&ru->gNB_list[0]->common_vars.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); - sprintf(filename,"tx%ddataF_frame%d_sl%d.m", i, print_frame, slot_tx); - LOG_M(filename,"txdataF_frame",&ru->common.txdataF[i][0],fp->samples_per_frame_wCP, 1, 1); - sprintf(filename,"tx%ddataF_BF_frame%d_sl%d.m", i, print_frame, slot_tx); - LOG_M(filename,"txdataF_BF_frame",&ru->common.txdataF_BF[i][0],fp->samples_per_subframe_wCP, 1, 1); - } - - if(slot_tx == 9) { - sprintf(filename,"tx%ddata_frame%d.m", i, print_frame); - LOG_M(filename,"txdata_frame",&ru->common.txdata[i][0],fp->samples_per_frame, 1, 1); - sprintf(filename,"tx%ddata_frame%d.dat", i, print_frame); - FILE *output_fd = fopen(filename,"w"); - - if (output_fd) { - fwrite(&ru->common.txdata[i][0], - sizeof(int32_t), - fp->samples_per_frame, - output_fd); - fclose(output_fd); - } else { - LOG_E(PHY,"Cannot write to file %s\n",filename); - } - }//if(slot_tx == 9) - }//for (i=0; inb_tx; i++) - }//if(frame_tx == print_frame) - }//else emulate_rf + if (ru->fh_north_asynch_in == NULL && ru->fh_south_out) + ru->fh_south_out(ru, frame_tx, slot_tx, info->timestamp_tx); + if (ru->fh_north_out) + ru->fh_north_out(ru); } /* @brief wait for the next RX TTI to be free @@ -1111,7 +1038,7 @@ void *ru_thread(void *param) // set default return value sprintf(threadname,"ru_thread %u",ru->idx); LOG_I(PHY,"Starting RU %d (%s,%s) on cpu %d\n",ru->idx,NB_functions[ru->function],NB_timing[ru->if_timing],sched_getcpu()); - memcpy((void *)&ru->config,(void *)&RC.gNB[0]->gNB_config,sizeof(ru->config)); + ru->config = RC.gNB[0]->gNB_config; nr_init_frame_parms(&ru->config, fp); nr_dump_frame_parms(fp); @@ -1119,54 +1046,52 @@ void *ru_thread(void *param) fill_rf_config(ru, ru->rf_config_file); fill_split7_2_config(&ru->openair0_cfg.split7, &ru->config, fp->slots_per_frame, fp->ofdm_symbol_size); - if(!emulate_rf) { - // Start IF device if any - if (ru->nr_start_if) { - LOG_I(PHY, "starting transport\n"); - ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params); - AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret); - - if (ru->ifdevice.get_internal_parameter != NULL) { - /* it seems the device can "overwrite" (request?) to set the callbacks - * for fh_south_in()/fh_south_out() differently */ - void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in"); - if (t != NULL) - ru->fh_south_in = t; - t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out"); - if (t != NULL) - ru->fh_south_out = t; - } else { - - malloc_IF4p5_buffer(ru); - } - - int cpu = sched_getcpu(); - if (ru->ru_thread_core > -1 && cpu != ru->ru_thread_core) { - /* we start the ru_thread using threadCreate(), which already sets CPU - * affinity; let's force it here again as per feature request #732 */ - cpu_set_t cpuset; - CPU_ZERO(&cpuset); - CPU_SET(ru->ru_thread_core, &cpuset); - int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); - AssertFatal(ret == 0, "Error in pthread_getaffinity_np(): ret: %d, errno: %d", ret, errno); - LOG_I(PHY, "RU %d: manually set CPU affinity to CPU %d\n", ru->idx, ru->ru_thread_core); - } - - LOG_I(PHY,"Starting IF interface for RU %d, nb_rx %d\n",ru->idx,ru->nb_rx); - AssertFatal(ru->nr_start_if(ru,NULL) == 0, "Could not start the IF device\n"); - - if (ru->has_ctrl_prt > 0) { - if (ru->if_south == LOCAL_RF) ret = connect_rau(ru); - else ret = attach_rru(ru); - - AssertFatal(ret==0,"Cannot connect to remote radio\n"); - } + // Start IF device if any + if (ru->nr_start_if) { + LOG_I(PHY, "starting transport\n"); + ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params); + AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret); + if (ru->ifdevice.get_internal_parameter != NULL) { + /* it seems the device can "overwrite" (request?) to set the callbacks + * for fh_south_in()/fh_south_out() differently */ + void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in"); + if (t != NULL) + ru->fh_south_in = t; + t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out"); + if (t != NULL) + ru->fh_south_out = t; + } else { + malloc_IF4p5_buffer(ru); } - else if (ru->if_south == LOCAL_RF) { // configure RF parameters only - ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); - AssertFatal(ret==0,"Cannot connect to local radio\n"); + + int cpu = sched_getcpu(); + if (ru->ru_thread_core > -1 && cpu != ru->ru_thread_core) { + /* we start the ru_thread using threadCreate(), which already sets CPU + * affinity; let's force it here again as per feature request #732 */ + cpu_set_t cpuset; + CPU_ZERO(&cpuset); + CPU_SET(ru->ru_thread_core, &cpuset); + int ret = pthread_setaffinity_np(pthread_self(), sizeof(cpu_set_t), &cpuset); + AssertFatal(ret == 0, "Error in pthread_getaffinity_np(): ret: %d, errno: %d", ret, errno); + LOG_I(PHY, "RU %d: manually set CPU affinity to CPU %d\n", ru->idx, ru->ru_thread_core); } + + LOG_I(PHY, "Starting IF interface for RU %d, nb_rx %d\n", ru->idx, ru->nb_rx); + AssertFatal(ru->nr_start_if(ru, NULL) == 0, "Could not start the IF device\n"); + + if (ru->has_ctrl_prt > 0) { + if (ru->if_south == LOCAL_RF) + ret = connect_rau(ru); + else + ret = attach_rru(ru); + + AssertFatal(ret == 0, "Cannot connect to remote radio\n"); + } + + } else if (ru->if_south == LOCAL_RF) { // configure RF parameters only + ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); + AssertFatal(ret==0,"Cannot connect to local radio\n"); } if (setup_RU_buffers(ru)!=0) { @@ -1181,31 +1106,31 @@ void *ru_thread(void *param) pthread_mutex_unlock(&RC.ru_mutex); wait_sync("ru_thread"); - if(!emulate_rf) { - // Start RF device if any - if (ru->start_rf) { - if (ru->start_rf(ru) != 0) - LOG_E(HW,"Could not start the RF device\n"); - else LOG_I(PHY,"RU %d rf device ready\n",ru->idx); - } else LOG_I(PHY,"RU %d no rf device\n",ru->idx); + // Start RF device if any + if (ru->start_rf) { + if (ru->start_rf(ru) != 0) + LOG_E(HW, "Could not start the RF device\n"); + else + LOG_I(PHY, "RU %d rf device ready\n", ru->idx); + } else + LOG_I(PHY, "RU %d no rf device\n", ru->idx); - LOG_I(PHY, "RU %d RF started cpu_meas_enabled %d\n", ru->idx, cpu_meas_enabled); - // start trx write thread - if(usrp_tx_thread == 1) { - if (ru->start_write_thread) { - if(ru->start_write_thread(ru) != 0) { - LOG_E(HW,"Could not start tx write thread\n"); - } else { - LOG_I(PHY,"tx write thread ready\n"); - } + LOG_I(PHY, "RU %d RF started cpu_meas_enabled %d\n", ru->idx, cpu_meas_enabled); + // start trx write thread + if (usrp_tx_thread == 1) { + if (ru->start_write_thread) { + if (ru->start_write_thread(ru) != 0) { + LOG_E(HW, "Could not start tx write thread\n"); + } else { + LOG_I(PHY, "tx write thread ready\n"); } } } // This is a forever while loop, it loops over subframes which are scheduled by incoming samples from HW devices struct timespec slot_start; - clock_gettime(CLOCK_MONOTONIC, &slot_start); - + clock_gettime(CLOCK_MONOTONIC, &slot_start); + while (!oai_exit) { if (slot==(fp->slots_per_frame-1)) { slot=0; @@ -1222,8 +1147,8 @@ void *ru_thread(void *param) // synchronization on input FH interface, acquire signals/data and block LOG_D(PHY,"[RU_thread] read data: frame_rx = %d, tti_rx = %d\n", frame, slot); - if (ru->fh_south_in) ru->fh_south_in(ru,&frame,&slot); - else AssertFatal(1==0, "No fronthaul interface at south port"); + AssertFatal(ru->fh_south_in, "No fronthaul interface at south port"); + ru->fh_south_in(ru, &frame, &slot); if (initial_wait == 1 && proc->frame_rx < 300) { if (proc->frame_rx > 0 && ((proc->frame_rx % 100) == 0) && proc->tti_rx == 0) { @@ -1308,14 +1233,14 @@ void *ru_thread(void *param) } // end if (slot_type == NR_UPLINK_SLOT || slot_type == NR_MIXED_SLOT) { notifiedFIFO_elt_t *resTx = newNotifiedFIFO_elt(sizeof(processingData_L1tx_t), 0, &gNB->L1_tx_out, NULL); - processingData_L1tx_t *syncMsgTx = NotifiedFifoData(resTx); - syncMsgTx->gNB = gNB; - syncMsgTx->frame = proc->frame_tx; - syncMsgTx->slot = proc->tti_tx; - syncMsgTx->frame_rx = proc->frame_rx; - syncMsgTx->slot_rx = proc->tti_rx; - syncMsgTx->timestamp_tx = proc->timestamp_tx; resTx->key = proc->tti_tx; + processingData_L1tx_t *syncMsgTx = NotifiedFifoData(resTx); + *syncMsgTx = (processingData_L1tx_t){.gNB = gNB, + .frame = proc->frame_tx, + .slot = proc->tti_tx, + .frame_rx = proc->frame_rx, + .slot_rx = proc->tti_rx, + .timestamp_tx = proc->timestamp_tx}; pushNotifiedFIFO(&gNB->L1_tx_out, resTx); } @@ -1325,15 +1250,16 @@ void *ru_thread(void *param) int start_streaming(RU_t *ru) { LOG_I(PHY,"Starting streaming on third-party RRU\n"); - return(ru->ifdevice.thirdparty_startstreaming(&ru->ifdevice)); + return ru->ifdevice.thirdparty_startstreaming(&ru->ifdevice); } int nr_start_if(struct RU_t_s *ru, struct PHY_VARS_gNB_s *gNB) { if (ru->if_south <= REMOTE_IF5) - for (int i=0;inb_rx;i++) ru->openair0_cfg.rxbase[i] = ru->common.rxdata[i]; + for (int i = 0; i < ru->nb_rx; i++) + ru->openair0_cfg.rxbase[i] = ru->common.rxdata[i]; ru->openair0_cfg.rxsize = ru->nr_frame_parms->samples_per_subframe*10; reset_meas(&ru->ifdevice.tx_fhaul); - return(ru->ifdevice.trx_start_func(&ru->ifdevice)); + return ru->ifdevice.trx_start_func(&ru->ifdevice); } int start_rf(RU_t *ru) { @@ -1349,29 +1275,12 @@ int stop_rf(RU_t *ru) { } int start_write_thread(RU_t *ru) { - return(ru->rfdevice.trx_write_init(&ru->rfdevice)); + return ru->rfdevice.trx_write_init(&ru->rfdevice); } -void init_RU_proc(RU_t *ru) { - int i=0; - RU_proc_t *proc; - proc = &ru->proc; - memset((void *)proc,0,sizeof(RU_proc_t)); - proc->ru = ru; - proc->instance_cnt_emulateRF = -1; - proc->first_rx = 1; - proc->first_tx = 1; - proc->frame_offset = 0; - proc->num_slaves = 0; - proc->frame_tx_unwrap = 0; - - for (i=0; i<10; i++) proc->symbol_mask[i]=0; - - pthread_mutex_init( &proc->mutex_emulateRF,NULL); - pthread_cond_init( &proc->cond_emulateRF, NULL); - - if(emulate_rf) - threadCreate( &proc->pthread_emulateRF, emulatedRF_thread, (void *)proc, "emulateRF", -1, OAI_PRIORITY_RT ); +void init_RU_proc(RU_t *ru) +{ + ru->proc = (RU_proc_t){.ru = ru, .first_rx = 1, .first_tx = 1}; LOG_I(PHY, "Initialized RU proc %d (%s,%s),\n", ru->idx, NB_functions[ru->function], NB_timing[ru->if_timing]); } @@ -1395,10 +1304,10 @@ void kill_NR_RU_proc(int inst) { * mutex_fep/cond_fep. Thus, we unlocked above for pthread_FH above and do * the same for FEP thread below again (using broadcast() to ensure both * threads get the signal). This one will also destroy the mutex and cond. */ - pthread_mutex_lock(&proc->mutex_fep[0]); + pthread_mutex_lock(proc->mutex_fep); proc->instance_cnt_fep[0] = 0; - pthread_cond_broadcast(&proc->cond_fep[0]); - pthread_mutex_unlock( &proc->mutex_fep[0] ); + pthread_cond_broadcast(proc->cond_fep); + pthread_mutex_unlock(proc->mutex_fep); pthread_join(proc->pthread_FH, NULL); // everything should be stopped now, we can safely stop the RF device @@ -1417,19 +1326,15 @@ void kill_NR_RU_proc(int inst) { int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { FH_fmt_options_t fmt = cap->FH_fmt; int i; - int found_band=0; LOG_I(PHY,"RRU %d, num_bands %d, looking for band %d\n",ru->idx,cap->num_bands,ru->nr_frame_parms->nr_band); for (i=0; inum_bands; i++) { LOG_I(PHY,"band %d on RRU %d\n",cap->band_list[i],ru->idx); - - if (ru->nr_frame_parms->nr_band == cap->band_list[i]) { - found_band=1; + if (ru->nr_frame_parms->nr_band == cap->band_list[i]) break; - } } - if (found_band == 0) { + if (i == cap->num_bands) { LOG_I(PHY,"Couldn't find target NR band %d on RRU %d\n",ru->nr_frame_parms->nr_band,ru->idx); return(-1); } @@ -1441,18 +1346,18 @@ int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { break; case REMOTE_IF5: - if (fmt == OAI_IF5_only || fmt == OAI_IF5_and_IF4p5) return(0); - + if (fmt == OAI_IF5_only || fmt == OAI_IF5_and_IF4p5) + return (0); break; case REMOTE_IF4p5: - if (fmt == OAI_IF4p5_only || fmt == OAI_IF5_and_IF4p5) return(0); - + if (fmt == OAI_IF4p5_only || fmt == OAI_IF5_and_IF4p5) + return (0); break; case REMOTE_MBP_IF5: - if (fmt == MBP_IF5) return(0); - + if (fmt == MBP_IF5) + return (0); break; default: @@ -1464,46 +1369,40 @@ int check_capabilities(RU_t *ru,RRU_capabilities_t *cap) { } const char rru_format_options[4][20] = {"OAI_IF5_only", "OAI_IF4p5_only", "OAI_IF5_and_IF4p5", "MBP_IF5"}; - const char rru_formats[3][20] = {"OAI_IF5", "MBP_IF5", "OAI_IF4p5"}; const char ru_if_formats[4][20] = {"LOCAL_RF", "REMOTE_OAI_IF5", "REMOTE_MBP_IF5", "REMOTE_OAI_IF4p5"}; void configure_ru(void *ruu, void *arg) { RU_t *ru = (RU_t *)ruu; - RRU_config_t *config = (RRU_config_t *)arg; - RRU_capabilities_t *capabilities = (RRU_capabilities_t *)arg; nfapi_nr_config_request_scf_t *cfg = &ru->config; int ret; LOG_I(PHY, "Received capabilities from RRU %d\n", ru->idx); - if (capabilities->FH_fmt < MAX_FH_FMTs) LOG_I(PHY, "RU FH options %s\n",rru_format_options[capabilities->FH_fmt]); + RRU_capabilities_t *capabilities = (RRU_capabilities_t *)arg; + if (capabilities->FH_fmt < MAX_FH_FMTs) + LOG_I(PHY, "RU FH options %s\n", rru_format_options[capabilities->FH_fmt]); ret = check_capabilities(ru,capabilities); AssertFatal(ret == 0, "Cannot configure RRU %d, check_capabilities returned %d\n", ru->idx, ret); // take antenna capabilities of RRU - ru->nb_tx = capabilities->nb_tx[0]; - ru->nb_rx = capabilities->nb_rx[0]; + ru->nb_tx = capabilities->nb_tx[0]; + ru->nb_rx = capabilities->nb_rx[0]; // Pass configuration to RRU LOG_I(PHY, "Using %s fronthaul (%d), band %d \n",ru_if_formats[ru->if_south],ru->if_south,ru->nr_frame_parms->nr_band); + // wait for configuration - config->FH_fmt = ru->if_south; - config->num_bands = 1; - config->band_list[0] = ru->nr_frame_parms->nr_band; - config->tx_freq[0] = ru->nr_frame_parms->dl_CarrierFreq; - config->rx_freq[0] = ru->nr_frame_parms->ul_CarrierFreq; - //config->tdd_config[0] = ru->nr_frame_parms->tdd_config; - //config->tdd_config_S[0] = ru->nr_frame_parms->tdd_config_S; - config->att_tx[0] = ru->att_tx; - config->att_rx[0] = ru->att_rx; - config->N_RB_DL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value; - config->N_RB_UL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value; - config->threequarter_fs[0] = ru->nr_frame_parms->threequarter_fs; - /* if (ru->if_south==REMOTE_IF4p5) { - config->prach_FreqOffset[0] = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset; - config->prach_ConfigIndex[0] = ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex; - LOG_I(PHY,"REMOTE_IF4p5: prach_FrequOffset %d, prach_ConfigIndex %d\n", - config->prach_FreqOffset[0],config->prach_ConfigIndex[0]);*/ + RRU_config_t *config = (RRU_config_t *)arg; + *config = (RRU_config_t){.FH_fmt = ru->if_south, + .num_bands = 1, + .band_list[0] = ru->nr_frame_parms->nr_band, + .tx_freq[0] = ru->nr_frame_parms->dl_CarrierFreq, + .rx_freq[0] = ru->nr_frame_parms->ul_CarrierFreq, + .att_tx[0] = ru->att_tx, + .att_rx[0] = ru->att_rx, + .N_RB_DL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value, + .N_RB_UL[0] = cfg->carrier_config.dl_grid_size[cfg->ssb_config.scs_common.value].value, + .threequarter_fs[0] = ru->nr_frame_parms->threequarter_fs}; nr_init_frame_parms(&ru->config, ru->nr_frame_parms); nr_phy_init_RU(ru); } @@ -1513,37 +1412,31 @@ void configure_rru(void *ruu, void *arg) RRU_config_t *config = (RRU_config_t *)arg; RU_t *ru = (RU_t *)ruu; nfapi_nr_config_request_scf_t *cfg = &ru->config; - ru->nr_frame_parms->nr_band = config->band_list[0]; - ru->nr_frame_parms->dl_CarrierFreq = config->tx_freq[0]; - ru->nr_frame_parms->ul_CarrierFreq = config->rx_freq[0]; + NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms; - if (ru->nr_frame_parms->dl_CarrierFreq == ru->nr_frame_parms->ul_CarrierFreq) { - cfg->cell_config.frame_duplex_type.value = TDD; - //ru->nr_frame_parms->tdd_config = config->tdd_config[0]; - //ru->nr_frame_parms->tdd_config_S = config->tdd_config_S[0]; + fp->nr_band = config->band_list[0]; + fp->dl_CarrierFreq = config->tx_freq[0]; + fp->ul_CarrierFreq = config->rx_freq[0]; + + if (fp->dl_CarrierFreq == fp->ul_CarrierFreq) { + cfg->cell_config.frame_duplex_type.value = TDD; } else - cfg->cell_config.frame_duplex_type.value = FDD; + cfg->cell_config.frame_duplex_type.value = FDD; - ru->att_tx = config->att_tx[0]; - ru->att_rx = config->att_rx[0]; + ru->att_tx = config->att_tx[0]; + ru->att_rx = config->att_rx[0]; int mu = cfg->ssb_config.scs_common.value; - cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_DL[0]; - cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_UL[0]; - ru->nr_frame_parms->threequarter_fs = config->threequarter_fs[0]; + cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_DL[0]; + cfg->carrier_config.dl_grid_size[mu].value = config->N_RB_UL[0]; + fp->threequarter_fs = config->threequarter_fs[0]; - //ru->nr_frame_parms->pdsch_config_common.referenceSignalPower = ru->max_pdschReferenceSignalPower-config->att_tx[0]; if (ru->function==NGFI_RRU_IF4p5) { - ru->nr_frame_parms->att_rx = ru->att_rx; - ru->nr_frame_parms->att_tx = ru->att_tx; - /* - LOG_I(PHY,"Setting ru->function to NGFI_RRU_IF4p5, prach_FrequOffset %d, prach_ConfigIndex %d, att (%d,%d)\n", - config->prach_FreqOffset[0],config->prach_ConfigIndex[0],ru->att_tx,ru->att_rx); - ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_FreqOffset = config->prach_FreqOffset[0]; - ru->nr_frame_parms->prach_config_common.prach_ConfigInfo.prach_ConfigIndex = config->prach_ConfigIndex[0]; */ + fp->att_rx = ru->att_rx; + fp->att_tx = ru->att_tx; } fill_rf_config(ru,ru->rf_config_file); - nr_init_frame_parms(&ru->config, ru->nr_frame_parms); + nr_init_frame_parms(&ru->config, fp); nr_phy_init_RU(ru); } @@ -1600,17 +1493,6 @@ void set_function_spec_param(RU_t *ru) ru->stop_rf = stop_rf; ru->start_write_thread = start_write_thread; // starting RF TX in different thread } - - /* - fill_rf_config(ru,rf_config_file); - init_frame_parms(&ru->frame_parms,1); - nr_phy_init_RU(ru); - - ret = openair0_device_load(&ru->rfdevice,&ru->openair0_cfg); - if (setup_RU_buffers(ru)!=0) { - exit(-1); - } - */ break; case REMOTE_IF5: // the remote unit is IF5 RRU @@ -1659,12 +1541,6 @@ void set_function_spec_param(RU_t *ru) void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) { - int ru_id; - RU_t *ru; - PHY_VARS_gNB *gNB_RC; - PHY_VARS_gNB *gNB0= (PHY_VARS_gNB *)NULL; - NR_DL_FRAME_PARMS *fp = (NR_DL_FRAME_PARMS *)NULL; - int i; // create status mask RC.ru_mask = 0; pthread_mutex_init(&RC.ru_mutex,NULL); @@ -1674,9 +1550,9 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) LOG_I(PHY,"number of L1 instances %d, number of RU %d, number of CPU cores %d\n",RC.nb_nr_L1_inst,RC.nb_RU,get_nprocs()); LOG_D(PHY,"Process RUs RC.nb_RU:%d\n",RC.nb_RU); - for (ru_id=0; ru_idrf_config_file = rf_config_file; ru->idx = ru_id; ru->ts_offset = 0; @@ -1693,23 +1569,18 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) } } - gNB_RC = RC.gNB[0]; - gNB0 = ru->gNB_list[0]; - fp = ru->nr_frame_parms; + PHY_VARS_gNB *gNB_RC = RC.gNB[0]; + PHY_VARS_gNB *gNB0 = ru->gNB_list[0]; LOG_D(PHY, "RU FUnction:%d ru->if_south:%d\n", ru->function, ru->if_south); if (gNB0) { - if ((ru->function != NGFI_RRU_IF5) && (ru->function != NGFI_RRU_IF4p5)) - AssertFatal(gNB0, "gNB0 is null!\n"); - - if (gNB0 && gNB_RC) { + if (gNB_RC) { LOG_D(PHY, "Copying frame parms from gNB in RC to gNB %d in ru %d and frame_parms in ru\n", gNB0->Mod_id, ru->idx); - memcpy((void *)fp, &gNB_RC->frame_parms, sizeof(NR_DL_FRAME_PARMS)); - memcpy((void *)&gNB0->frame_parms, (void *)&gNB_RC->frame_parms, sizeof(NR_DL_FRAME_PARMS)); + *ru->nr_frame_parms = gNB_RC->frame_parms; + gNB0->frame_parms = gNB_RC->frame_parms; // attach all RU to all gNBs in its list/ LOG_D(PHY,"ru->num_gNB:%d gNB0->num_RU:%d\n", ru->num_gNB, gNB0->num_RU); - - for (i=0; inum_gNB; i++) { + for (int i = 0; i < ru->num_gNB; i++) { gNB0 = ru->gNB_list[i]; gNB0->RU_list[gNB0->num_RU++] = ru; } @@ -1723,22 +1594,21 @@ void init_NR_RU(configmodule_interface_t *cfg, char *rf_config_file) LOG_E(PHY, "Number of threads for gNB should be more than 1. Allocated only %d\n", threadCnt); char pool[80]; int s_offset = sprintf(pool,"%d",ru->tpcores[0]); - for (int icpu=1; icputpcores[icpu]); + for (int icpu = 1; icpu < threadCnt; icpu++) { + s_offset += sprintf(pool + s_offset, ",%d", ru->tpcores[icpu]); } LOG_I(PHY, "RU thread-pool core string %s (size %d)\n", pool, threadCnt); - ru->threadPool = (tpool_t*)malloc(sizeof(tpool_t)); + ru->threadPool = malloc(sizeof(tpool_t)); initTpool(pool, ru->threadPool, cpumeas(CPUMEAS_GETSTATE)); // FEP RX result FIFO - ru->respfeprx = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + ru->respfeprx = malloc(sizeof(notifiedFIFO_t)); initNotifiedFIFO(ru->respfeprx); // FEP TX result FIFO - ru->respfeptx = (notifiedFIFO_t*) malloc(sizeof(notifiedFIFO_t)); + ru->respfeptx = malloc(sizeof(notifiedFIFO_t)); initNotifiedFIFO(ru->respfeptx); } } // for ru_id - // sleep(1); LOG_D(HW,"[nr-softmodem.c] RU threads created\n"); } @@ -1755,7 +1625,6 @@ void stop_RU(int nb_ru) { } } - /* --------------------------------------------------------*/ /* from here function to use configuration module */ static void NRRCconfig_RU(configmodule_interface_t *cfg) @@ -1764,199 +1633,199 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg) paramlist_def_t RUParamList = {CONFIG_STRING_RU_LIST, NULL, 0}; config_getlist(cfg, &RUParamList, RUParams, sizeofArray(RUParams), NULL); - if (RUParamList.numelt > 0) { - RC.ru = (RU_t **)malloc(RC.nb_RU*sizeof(RU_t *)); - RC.ru_mask = (1 << RC.nb_RU) - 1; + if (RUParamList.numelt <= 0) + return; - for (int j = 0; j < RC.nb_RU; j++) { - RC.ru[j] = calloc(1, sizeof(*RC.ru[j])); - RC.ru[j]->idx = j; - RC.ru[j]->nr_frame_parms = calloc(1, sizeof(*RC.ru[j]->nr_frame_parms)); - RC.ru[j]->frame_parms = calloc(1, sizeof(*RC.ru[j]->frame_parms)); - RC.ru[j]->if_timing = synch_to_ext_device; + RC.ru = (RU_t **)malloc(RC.nb_RU * sizeof(RU_t *)); + RC.ru_mask = (1 << RC.nb_RU) - 1; - if (RC.nb_nr_L1_inst > 0) - RC.ru[j]->num_gNB = RUParamList.paramarray[j][RU_ENB_LIST_IDX].numelt; - else - RC.ru[j]->num_gNB = 0; + for (int j = 0; j < RC.nb_RU; j++) { + RU_t *ru = RC.ru[j] = calloc(1, sizeof(*RC.ru[j])); + ru->idx = j; + ru->nr_frame_parms = calloc(1, sizeof(*ru->nr_frame_parms)); + ru->frame_parms = calloc(1, sizeof(*ru->frame_parms)); + ru->if_timing = synch_to_ext_device; + paramdef_t *param = RUParamList.paramarray[j]; + if (RC.nb_nr_L1_inst > 0) + ru->num_gNB = param[RU_ENB_LIST_IDX].numelt; + else + ru->num_gNB = 0; - for (int i = 0; i < RC.ru[j]->num_gNB; i++) - RC.ru[j]->gNB_list[i] = RC.gNB[RUParamList.paramarray[j][RU_ENB_LIST_IDX].iptr[i]]; + for (int i = 0; i < ru->num_gNB; i++) + ru->gNB_list[i] = RC.gNB[param[RU_ENB_LIST_IDX].iptr[i]]; - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_ADDRS)) { - RC.ru[j]->openair0_cfg.sdr_addrs = strdup(*(RUParamList.paramarray[j][RU_SDR_ADDRS].strptr)); - } + if (config_isparamset(param, RU_SDR_ADDRS)) { + ru->openair0_cfg.sdr_addrs = strdup(*param[RU_SDR_ADDRS].strptr); + } - if (config_isparamset(RUParamList.paramarray[j], RU_GPIO_CONTROL)) { - if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "generic") == 0) { - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; - LOG_I(PHY, "RU GPIO control set as 'generic'\n"); - } else if (strcmp(*RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr, "interdigital") == 0) { - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_INTERDIGITAL; - LOG_I(PHY, "RU GPIO control set as 'interdigital'\n"); - } else { - AssertFatal(false, - "bad GPIO controller in configuration file: '%s'\n", - *(RUParamList.paramarray[j][RU_GPIO_CONTROL].strptr)); - } - } else - RC.ru[j]->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_NONE; - - if (config_isparamset(RUParamList.paramarray[j], RU_TX_SUBDEV)) { - RC.ru[j]->openair0_cfg.tx_subdev = strdup(*(RUParamList.paramarray[j][RU_TX_SUBDEV].strptr)); - LOG_I(PHY, "RU USRP tx subdev == %s\n", RC.ru[j]->openair0_cfg.tx_subdev); - } - - if (config_isparamset(RUParamList.paramarray[j], RU_RX_SUBDEV)) { - RC.ru[j]->openair0_cfg.rx_subdev = strdup(*(RUParamList.paramarray[j][RU_RX_SUBDEV].strptr)); - LOG_I(PHY, "RU USRP rx subdev == %s\n", RC.ru[j]->openair0_cfg.rx_subdev); - } - - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_CLK_SRC)) { - if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "internal") == 0) { - RC.ru[j]->openair0_cfg.clock_source = internal; - LOG_I(PHY, "RU clock source set as internal\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "external") == 0) { - RC.ru[j]->openair0_cfg.clock_source = external; - LOG_I(PHY, "RU clock source set as external\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr), "gpsdo") == 0) { - RC.ru[j]->openair0_cfg.clock_source = gpsdo; - LOG_I(PHY, "RU clock source set as gpsdo\n"); - } else { - LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); - } + if (config_isparamset(param, RU_GPIO_CONTROL)) { + char *str = *param[RU_GPIO_CONTROL].strptr; + if (strcmp(str, "generic") == 0) { + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_GENERIC; + LOG_I(PHY, "RU GPIO control set as 'generic'\n"); + } else if (strcmp(str, "interdigital") == 0) { + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_INTERDIGITAL; + LOG_I(PHY, "RU GPIO control set as 'interdigital'\n"); } else { - LOG_D(PHY, "Setting clock source to internal\n"); - RC.ru[j]->openair0_cfg.clock_source = internal; + AssertFatal(false, "bad GPIO controller in configuration file: '%s'\n", str); } + } else + ru->openair0_cfg.gpio_controller = RU_GPIO_CONTROL_NONE; - if (config_isparamset(RUParamList.paramarray[j], RU_SDR_TME_SRC)) { - if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "internal") == 0) { - RC.ru[j]->openair0_cfg.time_source = internal; - LOG_I(PHY, "RU time source set as internal\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "external") == 0) { - RC.ru[j]->openair0_cfg.time_source = external; - LOG_I(PHY, "RU time source set as external\n"); - } else if (strcmp(*(RUParamList.paramarray[j][RU_SDR_TME_SRC].strptr), "gpsdo") == 0) { - RC.ru[j]->openair0_cfg.time_source = gpsdo; - LOG_I(PHY, "RU time source set as gpsdo\n"); - } else { - LOG_E(PHY, "Erroneous RU time source in the provided configuration file: '%s'\n", *(RUParamList.paramarray[j][RU_SDR_CLK_SRC].strptr)); - } + if (config_isparamset(param, RU_TX_SUBDEV)) { + ru->openair0_cfg.tx_subdev = strdup(*param[RU_TX_SUBDEV].strptr); + LOG_I(PHY, "RU USRP tx subdev == %s\n", ru->openair0_cfg.tx_subdev); + } + + if (config_isparamset(param, RU_RX_SUBDEV)) { + ru->openair0_cfg.rx_subdev = strdup(*param[RU_RX_SUBDEV].strptr); + LOG_I(PHY, "RU USRP rx subdev == %s\n", ru->openair0_cfg.rx_subdev); + } + + if (config_isparamset(param, RU_SDR_CLK_SRC)) { + char *str = *param[RU_SDR_CLK_SRC].strptr; + if (strcmp(str, "internal") == 0) { + ru->openair0_cfg.clock_source = internal; + LOG_I(PHY, "RU clock source set as internal\n"); + } else if (strcmp(str, "external") == 0) { + ru->openair0_cfg.clock_source = external; + LOG_I(PHY, "RU clock source set as external\n"); + } else if (strcmp(str, "gpsdo") == 0) { + ru->openair0_cfg.clock_source = gpsdo; + LOG_I(PHY, "RU clock source set as gpsdo\n"); } else { - LOG_D(PHY, "Setting time source to internal\n"); - RC.ru[j]->openair0_cfg.time_source = internal; + LOG_E(PHY, "Erroneous RU clock source in the provided configuration file: '%s'\n", str); + } + } else { + LOG_D(PHY, "Setting clock source to internal\n"); + ru->openair0_cfg.clock_source = internal; + } + + if (config_isparamset(param, RU_SDR_TME_SRC)) { + char *str = *param[RU_SDR_TME_SRC].strptr; + if (strcmp(str, "internal") == 0) { + ru->openair0_cfg.time_source = internal; + LOG_I(PHY, "RU time source set as internal\n"); + } else if (strcmp(str, "external") == 0) { + ru->openair0_cfg.time_source = external; + LOG_I(PHY, "RU time source set as external\n"); + } else if (strcmp(str, "gpsdo") == 0) { + ru->openair0_cfg.time_source = gpsdo; + LOG_I(PHY, "RU time source set as gpsdo\n"); + } else { + LOG_E(PHY, "Erroneous RU time source in the provided configuration file: '%s'\n", str); + } + } else { + LOG_D(PHY, "Setting time source to internal\n"); + ru->openair0_cfg.time_source = internal; + } + + ru->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset; + + if (strcmp(*param[RU_LOCAL_RF_IDX].strptr, "yes") == 0) { + if (!config_isparamset(param, RU_LOCAL_IF_NAME_IDX)) { + ru->if_south = LOCAL_RF; + ru->function = gNodeB_3GPP; + LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j); + } else { + ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr); + ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr); + ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr); + ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr; + ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr; + ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr; + ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr; + char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr; + if (strcmp(str, "udp") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF5; + ru->eth_params.transp_preference = ETH_UDP_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j); + } else if (strcmp(str, "raw") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF5; + ru->eth_params.transp_preference = ETH_RAW_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j); + } else if (strcmp(str, "udp_if4p5") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF4p5; + ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j); + } else if (strcmp(str, "raw_if4p5") == 0) { + ru->if_south = LOCAL_RF; + ru->function = NGFI_RRU_IF4p5; + ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; + LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j); + } } - RC.ru[j]->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset; + ru->max_pdschReferenceSignalPower = *param[RU_MAX_RS_EPRE_IDX].uptr; + ru->max_rxgain = *param[RU_MAX_RXGAIN_IDX].uptr; + ru->sf_extension = *param[RU_SF_EXTENSION_IDX].uptr; + } // strcmp(local_rf, "yes") == 0 + else { + char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr; + LOG_D(PHY, "RU %d: Transport %s\n", j, str); + ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr); + ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr); + ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr); + ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr; + ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr; + ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr; + ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr; - if (strcmp(*(RUParamList.paramarray[j][RU_LOCAL_RF_IDX].strptr), "yes") == 0) { - if (!(config_isparamset(RUParamList.paramarray[j],RU_LOCAL_IF_NAME_IDX))) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = gNodeB_3GPP; - LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j); - } else { - RC.ru[j]->eth_params.local_if_name = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr)); - RC.ru[j]->eth_params.my_addr = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.remote_addr = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.my_portc = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr); - RC.ru[j]->eth_params.remote_portc = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr); - RC.ru[j]->eth_params.my_portd = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr); - RC.ru[j]->eth_params.remote_portd = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr); - - if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j); - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) { - RC.ru[j]->if_south = LOCAL_RF; - RC.ru[j]->function = NGFI_RRU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; - LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j); - } - } - - RC.ru[j]->max_pdschReferenceSignalPower = *(RUParamList.paramarray[j][RU_MAX_RS_EPRE_IDX].uptr);; - RC.ru[j]->max_rxgain = *(RUParamList.paramarray[j][RU_MAX_RXGAIN_IDX].uptr); - RC.ru[j]->sf_extension = *(RUParamList.paramarray[j][RU_SF_EXTENSION_IDX].uptr); - } //strcmp(local_rf, "yes") == 0 - else { - LOG_D(PHY, "RU %d: Transport %s\n", j, *(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr)); - RC.ru[j]->eth_params.local_if_name = strdup(*(RUParamList.paramarray[j][RU_LOCAL_IF_NAME_IDX].strptr)); - RC.ru[j]->eth_params.my_addr = strdup(*(RUParamList.paramarray[j][RU_LOCAL_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.remote_addr = strdup(*(RUParamList.paramarray[j][RU_REMOTE_ADDRESS_IDX].strptr)); - RC.ru[j]->eth_params.my_portc = *(RUParamList.paramarray[j][RU_LOCAL_PORTC_IDX].uptr); - RC.ru[j]->eth_params.remote_portc = *(RUParamList.paramarray[j][RU_REMOTE_PORTC_IDX].uptr); - RC.ru[j]->eth_params.my_portd = *(RUParamList.paramarray[j][RU_LOCAL_PORTD_IDX].uptr); - RC.ru[j]->eth_params.remote_portd = *(RUParamList.paramarray[j][RU_REMOTE_PORTD_IDX].uptr); - - if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_ecpri_if5") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF5_ECPRI_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw") == 0) { - RC.ru[j]->if_south = REMOTE_IF5; - RC.ru[j]->function = NGFI_RAU_IF5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "udp_if4p5") == 0) { - RC.ru[j]->if_south = REMOTE_IF4p5; - RC.ru[j]->function = NGFI_RAU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; - } else if (strcmp(*(RUParamList.paramarray[j][RU_TRANSPORT_PREFERENCE_IDX].strptr), "raw_if4p5") == 0) { - RC.ru[j]->if_south = REMOTE_IF4p5; - RC.ru[j]->function = NGFI_RAU_IF4p5; - RC.ru[j]->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; - } - } /* strcmp(local_rf, "yes") != 0 */ - - RC.ru[j]->nb_tx = *(RUParamList.paramarray[j][RU_NB_TX_IDX].uptr); - RC.ru[j]->nb_rx = *(RUParamList.paramarray[j][RU_NB_RX_IDX].uptr); - RC.ru[j]->att_tx = *(RUParamList.paramarray[j][RU_ATT_TX_IDX].uptr); - RC.ru[j]->att_rx = *(RUParamList.paramarray[j][RU_ATT_RX_IDX].uptr); - RC.ru[j]->if_frequency = *(RUParamList.paramarray[j][RU_IF_FREQUENCY].u64ptr); - RC.ru[j]->if_freq_offset = *(RUParamList.paramarray[j][RU_IF_FREQ_OFFSET].iptr); - RC.ru[j]->sl_ahead = *(RUParamList.paramarray[j][RU_SL_AHEAD].iptr); - RC.ru[j]->num_bands = RUParamList.paramarray[j][RU_BAND_LIST_IDX].numelt; - for (int i = 0; i < RC.ru[j]->num_bands; i++) - RC.ru[j]->band[i] = RUParamList.paramarray[j][RU_BAND_LIST_IDX].iptr[i]; - RC.ru[j]->openair0_cfg.nr_flag = *(RUParamList.paramarray[j][RU_NR_FLAG].iptr); - RC.ru[j]->openair0_cfg.nr_band = RC.ru[j]->band[0]; - RC.ru[j]->openair0_cfg.nr_scs_for_raster = *(RUParamList.paramarray[j][RU_NR_SCS_FOR_RASTER].iptr); - LOG_D(PHY, - "[RU %d] Setting nr_flag %d, nr_band %d, nr_scs_for_raster %d\n", - j, - RC.ru[j]->openair0_cfg.nr_flag, - RC.ru[j]->openair0_cfg.nr_band, - RC.ru[j]->openair0_cfg.nr_scs_for_raster); - RC.ru[j]->openair0_cfg.rxfh_cores[0] = *(RUParamList.paramarray[j][RU_RXFH_CORE_ID].iptr); - RC.ru[j]->openair0_cfg.txfh_cores[0] = *(RUParamList.paramarray[j][RU_TXFH_CORE_ID].iptr); - RC.ru[j]->num_tpcores = *(RUParamList.paramarray[j][RU_NUM_TP_CORES].iptr); - RC.ru[j]->half_slot_parallelization = *(RUParamList.paramarray[j][RU_HALF_SLOT_PARALLELIZATION].iptr); - RC.ru[j]->ru_thread_core = *(RUParamList.paramarray[j][RU_RU_THREAD_CORE].iptr); - LOG_D(PHY, "[RU %d] Setting half-slot parallelization to %d\n", j, RC.ru[j]->half_slot_parallelization); - AssertFatal(RC.ru[j]->num_tpcores <= RUParamList.paramarray[j][RU_TP_CORES].numelt, "Number of TP cores should be <=16\n"); - for (int i = 0; i < RC.ru[j]->num_tpcores; i++) - RC.ru[j]->tpcores[i] = RUParamList.paramarray[j][RU_TP_CORES].iptr[i]; - }// j=0..num_rus - } else { - RC.nb_RU = 0; - } // setting != NULL + if (strcmp(str, "udp") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_UDP_MODE; + } else if (strcmp(str, "udp_ecpri_if5") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_UDP_IF5_ECPRI_MODE; + } else if (strcmp(str, "raw") == 0) { + ru->if_south = REMOTE_IF5; + ru->function = NGFI_RAU_IF5; + ru->eth_params.transp_preference = ETH_RAW_MODE; + } else if (strcmp(str, "udp_if4p5") == 0) { + ru->if_south = REMOTE_IF4p5; + ru->function = NGFI_RAU_IF4p5; + ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE; + } else if (strcmp(str, "raw_if4p5") == 0) { + ru->if_south = REMOTE_IF4p5; + ru->function = NGFI_RAU_IF4p5; + ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE; + } + } /* strcmp(local_rf, "yes") != 0 */ + ru->nb_tx = *param[RU_NB_TX_IDX].uptr; + ru->nb_rx = *param[RU_NB_RX_IDX].uptr; + ru->att_tx = *param[RU_ATT_TX_IDX].uptr; + ru->att_rx = *param[RU_ATT_RX_IDX].uptr; + ru->if_frequency = *param[RU_IF_FREQUENCY].u64ptr; + ru->if_freq_offset = *param[RU_IF_FREQ_OFFSET].iptr; + ru->sl_ahead = *param[RU_SL_AHEAD].iptr; + ru->num_bands = param[RU_BAND_LIST_IDX].numelt; + for (int i = 0; i < ru->num_bands; i++) + ru->band[i] = param[RU_BAND_LIST_IDX].iptr[i]; + ru->openair0_cfg.nr_flag = *param[RU_NR_FLAG].iptr; + ru->openair0_cfg.nr_band = ru->band[0]; + ru->openair0_cfg.nr_scs_for_raster = *param[RU_NR_SCS_FOR_RASTER].iptr; + LOG_D(PHY, + "[RU %d] Setting nr_flag %d, nr_band %d, nr_scs_for_raster %d\n", + j, + ru->openair0_cfg.nr_flag, + ru->openair0_cfg.nr_band, + ru->openair0_cfg.nr_scs_for_raster); + ru->openair0_cfg.rxfh_cores[0] = *param[RU_RXFH_CORE_ID].iptr; + ru->openair0_cfg.txfh_cores[0] = *param[RU_TXFH_CORE_ID].iptr; + ru->num_tpcores = *param[RU_NUM_TP_CORES].iptr; + ru->half_slot_parallelization = *param[RU_HALF_SLOT_PARALLELIZATION].iptr; + ru->ru_thread_core = *param[RU_RU_THREAD_CORE].iptr; + LOG_D(PHY, "[RU %d] Setting half-slot parallelization to %d\n", j, ru->half_slot_parallelization); + AssertFatal(ru->num_tpcores <= param[RU_TP_CORES].numelt, "Number of TP cores should be <=16\n"); + for (int i = 0; i < ru->num_tpcores; i++) + ru->tpcores[i] = param[RU_TP_CORES].iptr[i]; + } // j=0..num_rus return; } diff --git a/executables/nr-softmodem-common.h b/executables/nr-softmodem-common.h index 3ab6d1e73d..61823d43cd 100644 --- a/executables/nr-softmodem-common.h +++ b/executables/nr-softmodem-common.h @@ -133,7 +133,6 @@ extern int sync_var; extern int transmission_mode; extern double cpuf; -extern int emulate_rf; extern int numerology; extern int usrp_tx_thread; void wait_gNBs(void); diff --git a/executables/nr-softmodem.c b/executables/nr-softmodem.c index 5cc5e3a4fc..d8df770cb8 100644 --- a/executables/nr-softmodem.c +++ b/executables/nr-softmodem.c @@ -24,7 +24,6 @@ #include "common/config/config_userapi.h" #include "common/utils/load_module_shlib.h" -#include "RRC/LTE/rrc_vars.h" #ifdef SMBV #include "PHY/TOOLS/smbv.h" unsigned short config_frames[4] = {2,9,11,13}; @@ -119,7 +118,6 @@ double rx_gain_off = 0.0; static int tx_max_power[MAX_NUM_CCs]; /* = {0,0}*/; int chain_offset = 0; -int emulate_rf = 0; int numerology = 0; double cpuf; @@ -130,52 +128,6 @@ void pdcp_run(const protocol_ctxt_t *const ctxt_pP) abort(); } -/*---------------------BMC: timespec helpers -----------------------------*/ - -struct timespec min_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; -struct timespec max_diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - -struct timespec clock_difftime(struct timespec start, struct timespec end) { - struct timespec temp; - - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - - return temp; -} - -void print_difftimes(void) -{ - LOG_I(HW, "difftimes min = %lu ns ; max = %lu ns\n", min_diff_time.tv_nsec, max_diff_time.tv_nsec); -} - -void update_difftimes(struct timespec start, struct timespec end) { - struct timespec diff_time = { .tv_sec = 0, .tv_nsec = 0 }; - int changed = 0; - diff_time = clock_difftime(start, end); - - if ((min_diff_time.tv_nsec == 0) || (diff_time.tv_nsec < min_diff_time.tv_nsec)) { - min_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - - if ((max_diff_time.tv_nsec == 0) || (diff_time.tv_nsec > max_diff_time.tv_nsec)) { - max_diff_time.tv_nsec = diff_time.tv_nsec; - changed = 1; - } - -#if 1 - - if (changed) print_difftimes(); - -#endif -} - /*------------------------------------------------------------------------*/ unsigned int build_rflocal(int txi, int txq, int rxi, int rxq) { diff --git a/executables/nr-uesoftmodem.c b/executables/nr-uesoftmodem.c index 6ed407a507..8b331e4976 100644 --- a/executables/nr-uesoftmodem.c +++ b/executables/nr-uesoftmodem.c @@ -249,7 +249,7 @@ void set_options(int CC_id, PHY_VARS_NR_UE *UE){ } -void init_openair0() +static void init_openair0() { int card; int freq_off = 0; @@ -261,7 +261,6 @@ void init_openair0() for (card=0; cardthreequarter_fs; openair0_cfg[card].sample_rate = frame_parms->samples_per_subframe * 1e3; openair0_cfg[card].samples_per_frame = frame_parms->samples_per_frame; diff --git a/executables/softmodem-common.h b/executables/softmodem-common.h index 33d4f4cf04..d122b1a1d5 100644 --- a/executables/softmodem-common.h +++ b/executables/softmodem-common.h @@ -96,7 +96,6 @@ extern "C" #define CONFIG_HLP_NUMEROLOGY "adding numerology for 5G\n" #define CONFIG_HLP_BAND "band index\n" -#define CONFIG_HLP_EMULATE_RF "Emulated RF enabled(disable by defult)\n" #define CONFIG_HLP_PARALLEL_CMD "three config for level of parallelism 'PARALLEL_SINGLE_THREAD', 'PARALLEL_RU_L1_SPLIT', or 'PARALLEL_RU_L1_TRX_SPLIT'\n" #define CONFIG_HLP_WORKER_CMD "two option for worker 'WORKER_DISABLE' or 'WORKER_ENABLE'\n" #define CONFIG_HLP_USRP_THREAD "having extra thead for usrp tx\n" @@ -128,7 +127,6 @@ extern "C" #define CHAIN_OFFSET softmodem_params.chain_offset #define NUMEROLOGY softmodem_params.numerology #define BAND softmodem_params.band -#define EMULATE_RF softmodem_params.emulate_rf #define CLOCK_SOURCE softmodem_params.clock_source #define TIMING_SOURCE softmodem_params.timing_source #define TUNE_OFFSET softmodem_params.tune_offset @@ -164,7 +162,6 @@ extern int usrp_tx_thread; {"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, .iptr=&cpu_meas_enabled, .defintval=0, TYPE_INT, 0}, \ {"numerology" , CONFIG_HLP_NUMEROLOGY, 0, .iptr=&NUMEROLOGY, .defintval=1, TYPE_INT, 0}, \ {"band" , CONFIG_HLP_BAND, 0, .iptr=&BAND, .defintval=78, TYPE_INT, 0}, \ - {"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, .iptr=&EMULATE_RF, .defintval=0, TYPE_INT, 0}, \ {"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, .strptr=¶llel_config, .defstrval=NULL, TYPE_STRING, 0}, \ {"worker-config", CONFIG_HLP_WORKER_CMD, 0, .strptr=&worker_config, .defstrval=NULL, TYPE_STRING, 0}, \ {"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, .uptr=&noS1, .defintval=0, TYPE_UINT, 0}, \ @@ -217,7 +214,6 @@ extern int usrp_tx_thread; { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ { .s5 = { NULL } }, \ - { .s5 = { NULL } }, \ { .s3a = { config_checkstr_assign_integer, \ {"MONOLITHIC", "PNF", "VNF", "AERIAL","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \ {NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF, NFAPI_MODE_AERIAL,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \ @@ -320,7 +316,6 @@ typedef struct { int phy_test; int do_ra; uint8_t sl_mode; - int emulate_rf; int chain_offset; int numerology; int band; diff --git a/nfapi/oai_integration/nfapi_pnf.c b/nfapi/oai_integration/nfapi_pnf.c index 48e31b7264..3de11d8003 100644 --- a/nfapi/oai_integration/nfapi_pnf.c +++ b/nfapi/oai_integration/nfapi_pnf.c @@ -88,7 +88,6 @@ extern int nfapi_sync_var; extern int sync_var; nfapi_tx_request_pdu_t *tx_request_pdu[1023][10][10]; // [frame][subframe][max_num_pdus] -nfapi_nr_pdu_t *tx_data_request[1023][20][10]; //[frame][slot][max_num_pdus] uint8_t tx_pdus[32][8][4096]; nfapi_ue_release_request_body_t release_rntis; diff --git a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c index 62a20ef822..815ec9ce26 100644 --- a/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c +++ b/openair1/PHY/CODING/nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c @@ -815,6 +815,10 @@ static int pmd_lcore_ldpc_dec(void *arg) AssertFatal(ret == 0, "Allocation failed for %d ops", num_segments); set_ldpc_dec_op(ops_enq, bufs->inputs, bufs->hard_outputs, bufs->harq_outputs, nrLDPC_slot_decoding_parameters); + // Start timer + // We report timing only once in (0,0) since the timers are merged at the end + start_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode); + uint16_t enq = 0, deq = 0; while (enq < num_segments) { uint16_t num_to_enq = num_segments - enq; @@ -828,6 +832,10 @@ static int pmd_lcore_ldpc_dec(void *arg) DevAssert(time_out <= TIME_OUT_POLL); } + // Stop timer + // We report timing only once in (0,0) since the timers are merged at the end + stop_meas(&nrLDPC_slot_decoding_parameters->TBs[0].segments[0].ts_ldpc_decode); + if (deq == enq) { ret = retrieve_ldpc_dec_op(ops_deq, nrLDPC_slot_decoding_parameters); AssertFatal(ret == 0, "LDPC offload decoder failed!"); diff --git a/openair1/PHY/INIT/nr_init.c b/openair1/PHY/INIT/nr_init.c index dcdc4ee433..60bc63dac4 100644 --- a/openair1/PHY/INIT/nr_init.c +++ b/openair1/PHY/INIT/nr_init.c @@ -131,7 +131,6 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB) crcTableInit(); init_byte2m128i(); - init_byte2bit16(); init_pucch2_luts(); nr_init_fde(); // Init array for frequency equalization of transform precoding of PUSCH diff --git a/openair1/PHY/LTE_TRANSPORT/prach.c b/openair1/PHY/LTE_TRANSPORT/prach.c index e220549045..90c9d63925 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach.c +++ b/openair1/PHY/LTE_TRANSPORT/prach.c @@ -69,8 +69,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, uint32_t preamble_shift2; uint16_t preamble_index0=0,n_shift_ra=0,n_shift_ra_bar; uint16_t d_start=0; - uint16_t numshift=0; - uint16_t *prach_root_sequence_map; + uint16_t numshift = 0; uint8_t not_found; int k=0; uint16_t u; @@ -221,7 +220,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, if (eNB) start_meas(&eNB->rx_prach); - prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; + const uint16_t *prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; // PDP is oversampled, e.g. 1024 sample instead of 839 // Adapt the NCS (zero-correlation zones) with oversampling factor e.g. 1024/839 NCS2 = (N_ZC==839) ? ((NCS<<10)/839) : ((NCS<<8)/139); @@ -495,7 +494,7 @@ void rx_prach0(PHY_VARS_eNB *eNB, new_dft = 0; if (br_flag == 1) { - Xu=(c16_t *)eNB->X_u_br[ce_level][preamble_offset-first_nonzero_root_idx]; + Xu = eNB->X_u_br[ce_level][preamble_offset - first_nonzero_root_idx]; prach_ifft = prach_ifftp[prach_ifft_cnt++]; if (eNB->prach_vars_br.repetition_number[ce_level]==1) memset(prach_ifft,0,((N_ZC==839)?2048:256)*sizeof(int32_t)); diff --git a/openair1/PHY/LTE_TRANSPORT/prach_common.c b/openair1/PHY/LTE_TRANSPORT/prach_common.c index 2d9ef9d712..0939398c60 100644 --- a/openair1/PHY/LTE_TRANSPORT/prach_common.c +++ b/openair1/PHY/LTE_TRANSPORT/prach_common.c @@ -35,12 +35,13 @@ #include "PHY/phy_extern_ue.h" #include "common/utils/LOG/vcd_signal_dumper.h" #include "common/utils/lte/prach_utils.h" +#include "openair1/PHY/LTE_TRANSPORT/prach_extern.h" const uint16_t NCS_unrestricted[16] = {0, 13, 15, 18, 22, 26, 32, 38, 46, 59, 76, 93, 119, 167, 279, 419}; const uint16_t NCS_restricted[15] = {15, 18, 22, 26, 32, 38, 46, 55, 68, 82, 100, 128, 158, 202, 237}; // high-speed case const uint16_t NCS_4[7] = {2, 4, 6, 8, 10, 12, 15}; -int16_t ru[2*839]; // quantized roots of unity +c16_t root_unit[839]; // quantized roots of unity static uint32_t ZC_inv[839]; // multiplicative inverse for roots u uint16_t du[838]; @@ -367,13 +368,13 @@ int is_prach_subframe(LTE_DL_FRAME_PARMS *frame_parms, uint32_t frame, uint8_t s return(prach_mask); } - void compute_prach_seq(uint16_t rootSequenceIndex, uint8_t prach_ConfigIndex, uint8_t zeroCorrelationZoneConfig, uint8_t highSpeedFlag, frame_type_t frame_type, - uint32_t X_u[64][839]) { + c16_t X_u[64][839]) +{ // Compute DFT of x_u => X_u[k] = x_u(inv(u)*k)^* X_u[k] = exp(j\pi u*inv(u)*k*(inv(u)*k+1)/N_ZC) unsigned int k,inv_u,i,NCS=0,num_preambles; int N_ZC; @@ -495,7 +496,7 @@ void compute_prach_seq(uint16_t rootSequenceIndex, for (k=0; kframe_parms.prach_config_common.prach_ConfigInfo.prach_ConfigIndex, ue->frame_parms.prach_config_common.prach_ConfigInfo.prach_FreqOffset, tdd_mapindex, Nf); - prach_root_sequence_map = (prach_fmt<4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; + const uint16_t *prach_root_sequence_map = (prach_fmt < 4) ? prach_root_sequence_map0_3 : prach_root_sequence_map4; /* // this code is not part of get_prach_prb_offset if (frame_type == TDD) { // TDD @@ -215,20 +210,6 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 #endif // nsymb = (frame_parms->Ncp==0) ? 14:12; // subframe_offset = (unsigned int)frame_parms->ofdm_symbol_size*subframe*nsymb; - k = (12*n_ra_prb) - 6*ue->frame_parms.N_RB_UL; - - if (k<0) - k+=ue->frame_parms.ofdm_symbol_size; - - k*=12; - k+=13; - Xu = (int16_t *)ue->X_u[preamble_offset-first_nonzero_root_idx]; - /* - k+=(12*ue->frame_parms.first_carrier_offset); - if (k>(12*ue->frame_parms.ofdm_symbol_size)) - k-=(12*ue->frame_parms.ofdm_symbol_size); - */ - k*=2; switch (ue->frame_parms.N_RB_UL) { case 6: @@ -259,20 +240,26 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1 break; } + { + int k = (12 * n_ra_prb) - 6 * ue->frame_parms.N_RB_UL; - for (offset=0,offset2=0; offset= N_ZC) - offset2 -= N_ZC; + if (k < 0) + k += ue->frame_parms.ofdm_symbol_size; - Xu_re = (((int32_t)Xu[offset<<1]*amp)>>15); - Xu_im = (((int32_t)Xu[1+(offset<<1)]*amp)>>15); - prachF[k++]= ((Xu_re*ru[offset2<<1]) - (Xu_im*ru[1+(offset2<<1)]))>>15; - prachF[k++]= ((Xu_im*ru[offset2<<1]) + (Xu_re*ru[1+(offset2<<1)]))>>15; + k *= 12; + k += 13; + c16_t *Xu_ue = ue->X_u[preamble_offset - first_nonzero_root_idx]; + c16_t *complex_prachF = (c16_t *)prachF; + for (int offset = 0, offset2 = 0; offset < N_ZC; offset++, offset2 += preamble_shift) { + if (offset2 >= N_ZC) + offset2 -= N_ZC; + c16_t Xu = c16mulRealShift(*Xu_ue++, amp, 15); + complex_prachF[k++] = c16mulShift(Xu, root_unit[offset2], 15); - if (k==(12*2*ue->frame_parms.ofdm_symbol_size)) - k=0; + if (k == 12 * ue->frame_parms.ofdm_symbol_size) + k = 0; + } } - switch (prach_fmt) { case 0: Ncp = 3168; diff --git a/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h b/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h index 9aab56e52a..06bf87fc74 100644 --- a/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h +++ b/openair1/PHY/LTE_UE_TRANSPORT/transport_proto_ue.h @@ -1649,8 +1649,7 @@ void compute_prach_seq(uint16_t rootSequenceIndex, uint8_t zeroCorrelationZoneConfig, uint8_t highSpeedFlag, frame_type_t frame_type, - uint32_t X_u[64][839]); - + c16_t X_u[64][839]); void init_prach_tables(int N_ZC); diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c index 667c7146b0..239b6f2f29 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_decoding.c @@ -305,6 +305,10 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB, LOG_D(PHY, "ULSCH %d in error\n", ULSCH_id); } offset += ((harq_process->K >> 3) - (harq_process->F >> 3) - ((harq_process->C > 1) ? 3 : 0)); + + merge_meas(&phy_vars_gNB->ts_deinterleave, &nrLDPC_segment_decoding_parameters.ts_deinterleave); + merge_meas(&phy_vars_gNB->ts_rate_unmatch, &nrLDPC_segment_decoding_parameters.ts_rate_unmatch); + merge_meas(&phy_vars_gNB->ts_ldpc_decode, &nrLDPC_segment_decoding_parameters.ts_ldpc_decode); } } diff --git a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c index e8d8efeb28..bd4ee92284 100644 --- a/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c +++ b/openair1/PHY/NR_TRANSPORT/nr_ulsch_demodulation.c @@ -7,6 +7,7 @@ #include "PHY/NR_REFSIG/ptrs_nr.h" #include "PHY/NR_ESTIMATION/nr_ul_estimation.h" #include "PHY/defs_nr_common.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "common/utils/nr/nr_common.h" #include #include "PHY/sse_intrin.h" @@ -210,31 +211,6 @@ static int get_nb_re_pusch (NR_DL_FRAME_PARMS *frame_parms, nfapi_nr_pusch_pdu_t } else return(rel15_ul->rb_size * NR_NB_SC_PER_RB); } -// compute average channel_level on each (TX,RX) antenna pair -static void nr_ulsch_channel_level(int size_est, - int ul_ch_estimates_ext[][size_est], - NR_DL_FRAME_PARMS *frame_parms, - int32_t *avg, - uint8_t symbol, - uint32_t len, - uint8_t nrOfLayers) -{ - int16_t x = factor2(len); - int16_t y = (len)>>x; - - for (int aatx = 0; aatx < nrOfLayers; aatx++) { - for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) { - - simde__m128i *ul_ch128 = (simde__m128i *)&ul_ch_estimates_ext[aatx * frame_parms->nb_antennas_rx + aarx][symbol * len]; - - //compute average level - avg[aatx * frame_parms->nb_antennas_rx + aarx] = simde_mm_average(ul_ch128, len, x, y); - //LOG_D(PHY, "Channel level: %d\n", avg[aatx * frame_parms->nb_antennas_rx + aarx]); - } - } - -} - static void nr_ulsch_channel_compensation(uint32_t buffer_length, int nb_rx_ant, c16_t rxFext[][buffer_length], @@ -1394,8 +1370,6 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, rel15_ul, frame_parms); - int avgs = 0; - int avg[frame_parms->nb_antennas_rx*rel15_ul->nrOfLayers]; uint8_t shift_ch_ext = rel15_ul->nrOfLayers > 1 ? log2_approx(max_ch >> 11) : 0; //---------------------------------------------------------- @@ -1410,15 +1384,17 @@ int nr_rx_pusch_tp(PHY_VARS_gNB *gNB, rel15_ul->nrOfLayers, rel15_ul->rb_size, shift_ch_ext); - - nr_ulsch_channel_level(size_est, - ul_ch_estimates_ext, - frame_parms, - avg, - meas_symbol, // index of the start symbol - nb_re_pusch, // number of the re in pusch - rel15_ul->nrOfLayers); + int avg[frame_parms->nb_antennas_rx*rel15_ul->nrOfLayers]; + nr_channel_level(meas_symbol, + size_est, + (c16_t (*)[size_est])ul_ch_estimates_ext, + frame_parms->nb_antennas_rx, + rel15_ul->nrOfLayers, + avg, + nb_re_pusch); + + int avgs = 0; for (int nl = 0; nl < rel15_ul->nrOfLayers; nl++) for (int aarx = 0; aarx < frame_parms->nb_antennas_rx; aarx++) avgs = cmax(avgs, avg[nl * frame_parms->nb_antennas_rx + aarx]); diff --git a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c index b4e2c413cf..43b27e3932 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c +++ b/openair1/PHY/NR_UE_TRANSPORT/dci_nr.c @@ -36,6 +36,7 @@ #include "PHY/CODING/nrPolar_tools/nr_polar_dci_defs.h" #include "PHY/phy_extern.h" #include "PHY/CODING/coding_extern.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "PHY/sse_intrin.h" #include "common/utils/nr/nr_common.h" #include @@ -197,20 +198,6 @@ static void nr_pdcch_llr(uint32_t sz, c16_t *rxF, c16_t *llr) } } -//compute average channel_level on each (TX,RX) antenna pair -static int32_t nr_pdcch_channel_level(int nb_re, int arraySz, c16_t dl_ch_estimates_ext[][arraySz], int nb_antennas_rx) -{ - int32_t avgs = 0; - for (int aarx = 0; aarx < nb_antennas_rx; aarx++) { - simde__m128i *dl_ch128 = (simde__m128i *)&dl_ch_estimates_ext[aarx]; - // compute average level - // if nb_re is not %4, some samples are missed inside simde_mm_average, so let's discard it - int32_t tmp = simde_mm_average(dl_ch128, nb_re & ~3, 0, nb_re & ~3); - avgs = cmax(avgs, tmp); - } - return avgs; -} - // This function will extract the mapped DM-RS PDCCH REs as per 38.211 Section 7.4.1.3.2 (Mapping to physical resources) static void nr_pdcch_extract_rbs_single(uint32_t rxdataF_sz, uint32_t coreset_nbr_rb, @@ -372,9 +359,13 @@ void nr_rx_pdcch(PHY_VARS_NR_UE *ue, rel15->coreset.frequency_domain_resource, rel15->BWPStart); - LOG_D(NR_PHY_DCI, "in nr_pdcch_channel_level(dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); + LOG_D(NR_PHY_DCI, "in channel level function (dl_ch_estimates_ext -> dl_ch_estimates_ext)\n"); // compute channel level based on ofdm symbol 0 - int32_t avgs = nr_pdcch_channel_level(n_rb, arraySz, pdcch_dl_ch_estimates_ext, antRx); + int avg[antRx]; + nr_channel_level(0, arraySz, pdcch_dl_ch_estimates_ext, antRx, 1, avg, n_rb); + int avgs = avg[0]; + for (int i = 1; i < antRx; i++) + avgs = cmax(avgs, avg[i]); uint8_t log2_maxh = (log2_approx(avgs) / 2) + 5; //+antRx; #ifdef UE_DEBUG_TRACE diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c index d0457b3bb9..1da2ef8d45 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_dlsch_demodulation.c @@ -193,20 +193,6 @@ static void nr_dlsch_channel_compensation(uint32_t rx_size_symbol, unsigned char output_shift, PHY_NR_MEASUREMENTS *measurements); -/** \brief This function computes the average channel level over all allocated RBs and antennas (TX/RX) in order to compute output shift for compensated signal - @param dl_ch_estimates_ext Channel estimates in allocated RBs - @param frame_parms Pointer to frame descriptor - @param avg Pointer to average signal strength - @param pilots_flag Flag to indicate pilots in symbol - @param nb_rb Number of allocated RBs -*/ -static void nr_dlsch_channel_level(uint32_t rx_size_symbol, - int32_t dl_ch_estimates_ext[][rx_size_symbol], - uint8_t n_rx, - uint8_t n_tx, - int32_t avg[MAX_ANT][MAX_ANT], - uint32_t len); - void nr_dlsch_scale_channel(uint32_t rx_size_symbol, int32_t dl_ch_estimates_ext[][rx_size_symbol], NR_DL_FRAME_PARMS *frame_parms, @@ -538,25 +524,25 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, //---------------------------------------------------------- start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS); if (first_symbol_flag) { - int32_t avg[MAX_ANT][MAX_ANT] = {}; + int32_t avg[nl * n_rx]; if (nb_re_pdsch) - nr_dlsch_channel_level(rx_size_symbol, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, nl, avg, nb_re_pdsch); + nr_channel_level(0, rx_size_symbol, (c16_t (*)[rx_size_symbol])dl_ch_estimates_ext, n_rx, nl, avg, nb_re_pdsch); else LOG_E(NR_PHY, "Average channel level is 0: nb_rb_pdsch = %d, nb_re_pdsch = %d\n", nb_rb_pdsch, nb_re_pdsch); int avgs = 0; int32_t median[MAX_ANT][MAX_ANT]; - for (int aatx = 0; aatx < nl; aatx++) + for (int l = 0; l < nl; l++) for (int aarx = 0; aarx < n_rx; aarx++) { - avgs = cmax(avgs, avg[aatx][aarx]); - LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, aatx, avg[aatx][aarx]); + avgs = cmax(avgs, avg[l * n_rx + aarx]); + LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, l, avg[l * n_rx + aarx]); LOG_D(PHY, "avgs Power per SC is %d\n", avgs); - median[aatx][aarx] = avg[aatx][aarx]; + median[l][aarx] = avg[l * n_rx + aarx]; } if (nl > 1) { nr_dlsch_channel_level_median(rx_size_symbol, dl_ch_estimates_ext, median, nl, n_rx, nb_re_pdsch); - for (int aatx = 0; aatx < nl; aatx++) { + for (int l = 0; l < nl; l++) { for (int aarx = 0; aarx < n_rx; aarx++) { - avgs = cmax(avgs, median[aatx][aarx]); + avgs = cmax(avgs, median[l][aarx]); } } } @@ -795,53 +781,6 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue, return (0); } -void nr_dlsch_deinterleaving(uint8_t symbol, - uint8_t start_symbol, - uint16_t L, - uint16_t *llr, - uint16_t *llr_deint, - uint16_t nb_rb_pdsch) -{ - - uint32_t bundle_idx, N_bundle, R, C, r,c; - int32_t m,k; - uint8_t nb_re; - - R=2; - N_bundle = nb_rb_pdsch/L; - C=N_bundle/R; - - uint32_t bundle_deint[N_bundle]; - memset(bundle_deint, 0 , sizeof(bundle_deint)); - - printf("N_bundle %u L %d nb_rb_pdsch %d\n",N_bundle, L,nb_rb_pdsch); - - if (symbol==start_symbol) - nb_re = 6; - else - nb_re = 12; - - - AssertFatal(llr!=NULL,"nr_dlsch_deinterleaving: FATAL llr is Null\n"); - - - for (c =0; c< C; c++){ - for (r=0; r> x; - - uint32_t nb_rb_0 = len / NR_NB_SC_PER_RB + ((len % NR_NB_SC_PER_RB) ? 1 : 0); - LOG_D(NR_PHY, "nb_rb_0 %d len %d = %d * 2^(%d)\n", nb_rb_0, len, y, x); - - for (int aatx = 0; aatx < n_tx; aatx++) { - for (int aarx = 0; aarx < n_rx; aarx++) { - - simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[(aatx * n_rx) + aarx]; - - //compute average level - avg[aatx][aarx] = simde_mm_average(dl_ch128, nb_rb_0 * 12, x, y); - //LOG_D(PHY, "Channel level: %d\n", avg[aatx][aarx]); - } - } -} - static void nr_dlsch_channel_level_median(uint32_t rx_size_symbol, int32_t dl_ch_estimates_ext[][rx_size_symbol], int32_t median[MAX_ANT][MAX_ANT], diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c index 298de612ee..8065da24b2 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_pbch.c @@ -38,6 +38,7 @@ #include #include #include "openair1/PHY/NR_REFSIG/nr_refsig_common.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" #include "instrumentation.h" //#define DEBUG_PBCH //#define DEBUG_PBCH_ENCODING @@ -201,31 +202,6 @@ static uint16_t nr_pbch_extract(uint32_t rxdataF_sz, return(0); } -//__m128i avg128; - -//compute average channel_level on each (TX,RX) antenna pair -int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], - const NR_DL_FRAME_PARMS *frame_parms, - int nb_re) -{ - int32_t avg2 = 0; - - for (int aarx=0; aarxnb_antennas_rx; aarx++) { - - simde__m128i *dl_ch128 = (simde__m128i *)dl_ch_estimates_ext[aarx]; - - //compute average level - int32_t avg1 = simde_mm_average(dl_ch128, nb_re, 0, nb_re); - - if (avg1>avg2) - avg2 = avg1; - - //LOG_I(PHY,"Channel level : %d, %d\n",avg1, avg2); - } - - return(avg2); -} - void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL], struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], int nb_re, @@ -346,7 +322,6 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, const struct complex16 rxdataF[][rxdataFSize]) { TracyCZone(ctx, true); - int max_h=0; int symbol; uint8_t Lmax=frame_parms->Lmax; int M = NR_POLAR_PBCH_E; @@ -390,11 +365,14 @@ int nr_rx_pbch(PHY_VARS_NR_UE *ue, LOG_I(PHY,"[PHY] PBCH starting channel_level\n"); #endif + int max_h = 0; if (symbol == 1) { - max_h = nr_pbch_channel_level(dl_ch_estimates_ext, - frame_parms, - nb_re); - log2_maxh = 3+(log2_approx(max_h)/2); + int avg[frame_parms->nb_antennas_rx]; + nr_channel_level(0, PBCH_MAX_RE_PER_SYMBOL, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, 1, avg, nb_re); + max_h = avg[0]; + for (int i = 1; i < frame_parms->nb_antennas_rx; i++) + max_h = cmax(avg[i], max_h); + log2_maxh = 3 + (log2_approx(max_h) / 2); } #ifdef DEBUG_PBCH diff --git a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c index 7406855cc4..1010a6e5e8 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_psbch_rx.c @@ -25,6 +25,7 @@ #include "common/utils/LOG/log.h" #include "PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h" #include "PHY/TOOLS/phy_scope_interface.h" +#include "PHY/nr_phy_common/inc/nr_phy_common.h" // #define DEBUG_PSBCH @@ -158,8 +159,11 @@ int nr_rx_psbch(PHY_VARS_NR_UE *ue, int max_h = 0; if (symbol == 0) { - max_h = nr_pbch_channel_level(dl_ch_estimates_ext, frame_parms, nb_re); - // log2_maxh = 3+(log2_approx(max_h)/2); + int avg[frame_parms->nb_antennas_rx]; + nr_channel_level(0, PBCH_MAX_RE_PER_SYMBOL, dl_ch_estimates_ext, frame_parms->nb_antennas_rx, 1, avg, nb_re); + max_h = avg[0]; + for (int i = 1; i < frame_parms->nb_antennas_rx; i++) + max_h = cmax(avg[i], max_h); log2_maxh = 5 + (log2_approx(max_h) / 2); // LLR32 crc error. LLR 16 CRC works } #ifdef DEBUG_PSBCH 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 a4098394ac..44a3611bf0 100644 --- a/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h +++ b/openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h @@ -59,13 +59,6 @@ typedef struct pdsch_scope_req_s { */ void nr_ue_dlsch_init(NR_UE_DLSCH_t *dlsch_list, int num_dlsch, uint8_t max_ldpc_iterations); -void nr_dlsch_deinterleaving(uint8_t symbol, - uint8_t start_symbol, - uint16_t L, - uint16_t *llr, - uint16_t *llr_deint, - uint16_t nb_rb_pdsch); - void nr_conjch0_mult_ch1(c16_t *ch0, c16_t *ch1, c16_t *ch0conj_ch1, unsigned short nb_rb, unsigned char output_shift0); /** \brief This is the alternative top-level entry point for DLSCH decoding in UE. @@ -346,9 +339,6 @@ void nr_tx_psbch(PHY_VARS_NR_UE *UE, uint32_t frame_tx, uint32_t slot_tx, sl_nr_ nr_initial_sync_t sl_nr_slss_search(PHY_VARS_NR_UE *UE, UE_nr_rxtx_proc_t *proc, int num_frames); // Reuse already existing PBCH functions -int nr_pbch_channel_level(struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], - const NR_DL_FRAME_PARMS *frame_parms, - int nb_re); void nr_pbch_channel_compensation(struct complex16 rxdataF_ext[][PBCH_MAX_RE_PER_SYMBOL], struct complex16 dl_ch_estimates_ext[][PBCH_MAX_RE_PER_SYMBOL], int nb_re, diff --git a/openair1/PHY/defs_RU.h b/openair1/PHY/defs_RU.h index a2c39852f5..e72465c4a9 100644 --- a/openair1/PHY/defs_RU.h +++ b/openair1/PHY/defs_RU.h @@ -255,7 +255,6 @@ typedef struct RU_proc_t_s { int instance_cnt_feptx; /// \internal This variable is protected by \ref mutex_ru_thread int instance_cnt_ru; - int instance_cnt_emulateRF; /// pthread structure for RU FH processing thread pthread_t pthread_FH; pthread_t pthread_FH1; @@ -271,8 +270,6 @@ typedef struct RU_proc_t_s { pthread_t pthread_fep[8]; /// pthread struct for RU TX FEP worker thread pthread_t pthread_feptx; - /// pthread struct for emulated RF - pthread_t pthread_emulateRF; /// pthread structure for asychronous RX/TX processing thread pthread_t pthread_asynch_rxtx; /// flag to indicate first RX acquisition @@ -296,8 +293,6 @@ typedef struct RU_proc_t_s { pthread_attr_t attr_fep; /// pthread attributes for worker feptx thread pthread_attr_t attr_feptx; - /// pthread attributes for emulated RF - pthread_attr_t attr_emulateRF; /// scheduling parameters for RU FH thread struct sched_param sched_param_FH; struct sched_param sched_param_FH1; @@ -324,8 +319,6 @@ typedef struct RU_proc_t_s { pthread_cond_t cond_fep[8]; /// condition varible for RU TX FEP thread pthread_cond_t cond_feptx; - /// condition varible for emulated RF - pthread_cond_t cond_emulateRF; /// condition variable for eNB signal pthread_cond_t cond_eNBs; /// condition variable for gNB signal @@ -353,8 +346,6 @@ typedef struct RU_proc_t_s { pthread_mutex_t mutex_feptx; /// mutex for ru_thread pthread_mutex_t mutex_ru; - /// mutex for emulated RF thread - pthread_mutex_t mutex_emulateRF; /// symbol mask for IF4p5 reception per subframe uint32_t symbol_mask[10]; /// time measurements for each subframe @@ -394,7 +385,6 @@ typedef struct RU_proc_t_s { /// pipeline ready state int ru_rx_ready; int ru_tx_ready; - int emulate_rf_busy; /// structure for precoding thread RU_prec_t prec[16]; @@ -407,7 +397,6 @@ typedef enum { REMOTE_IF4p5 =3, REMOTE_IF1pp =4, MAX_RU_IF_TYPES =5 - //EMULATE_RF =6 } RU_if_south_t; @@ -453,8 +442,6 @@ typedef struct RU_t_s { node_function_t function; /// Ethernet parameters for fronthaul interface eth_params_t eth_params; - /// flag to indicate RF emulation mode - int emulate_rf; /// numerology index int numerology; /// flag to indicate the RU is in sync with a master reference diff --git a/openair1/PHY/defs_UE.h b/openair1/PHY/defs_UE.h index 17ae02610e..20d5a9ab8d 100644 --- a/openair1/PHY/defs_UE.h +++ b/openair1/PHY/defs_UE.h @@ -640,7 +640,7 @@ typedef struct { /// mbsfn reference symbols uint32_t lte_gold_mbsfn_khz_1dot25_table[10][150]; - uint32_t X_u[64][839]; + c16_t X_u[64][839]; uint32_t high_speed_flag; uint32_t perfect_ce; diff --git a/openair1/PHY/defs_eNB.h b/openair1/PHY/defs_eNB.h index 68ebf65462..ef1db2e05f 100644 --- a/openair1/PHY/defs_eNB.h +++ b/openair1/PHY/defs_eNB.h @@ -255,11 +255,6 @@ typedef struct { tpool_t *threadPool; int nbDecode; notifiedFIFO_t *respDecode; - pthread_mutex_t mutex_emulateRF; - int instance_cnt_emulateRF; - pthread_t pthread_emulateRF; - pthread_attr_t attr_emulateRF; - pthread_cond_t cond_emulateRF; int first_rx; } L1_rxtx_proc_t; @@ -600,8 +595,8 @@ typedef struct PHY_VARS_eNB_s { // PUCCH1a/b energy detection parameters for eMTC per CE-level int pucch1ab_DTX_threshold_emtc[4]; - uint32_t X_u[64][839]; - uint32_t X_u_br[4][64][839]; + c16_t X_u[64][839]; + c16_t X_u_br[4][64][839]; uint8_t pbch_configured; uint8_t pbch_pdu[4]; //PBCH_PDU_SIZE char eNB_generate_rar; diff --git a/openair1/PHY/defs_gNB.h b/openair1/PHY/defs_gNB.h index ad17c243ae..987f77d953 100644 --- a/openair1/PHY/defs_gNB.h +++ b/openair1/PHY/defs_gNB.h @@ -521,6 +521,9 @@ typedef struct PHY_VARS_gNB_s { time_stats_t slot_indication_stats; time_stats_t schedule_response_stats; time_stats_t ulsch_decoding_stats; + time_stats_t ts_deinterleave; + time_stats_t ts_rate_unmatch; + time_stats_t ts_ldpc_decode; time_stats_t ulsch_deinterleaving_stats; time_stats_t ulsch_channel_estimation_stats; time_stats_t pusch_channel_estimation_antenna_processing_stats; 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 d52b6d9c43..79e5b8746e 100644 --- a/openair1/PHY/nr_phy_common/inc/nr_phy_common.h +++ b/openair1/PHY/nr_phy_common/inc/nr_phy_common.h @@ -26,15 +26,8 @@ #include "PHY/NR_REFSIG/nr_refsig_common.h" #include "PHY/MODULATION/nr_modulation.h" -extern simde__m128i byte2bit16_lut[256]; -void init_byte2bit16(void); void init_byte2m128i(void); -static inline simde__m128i byte2bit16(uint8_t b) -{ - return byte2bit16_lut[b]; -} - typedef struct { int size; int ports; @@ -54,6 +47,13 @@ void freq2time(uint16_t ofdm_symbol_size, int16_t *freq_signal, int16_t *time_si 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); void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size); +void nr_channel_level(const int symbol, + const int size_est, + const c16_t ch_estimates_ext[][size_est], + const int nb_rx, + const int Nl, + int32_t avg[nb_rx * Nl], + const uint32_t len); void nr_generate_csi_rs(const NR_DL_FRAME_PARMS *frame_parms, const csi_mapping_parms_t *phy_csi_parms, const int16_t amp, 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 e86652bd49..82418cbb53 100644 --- a/openair1/PHY/nr_phy_common/src/nr_phy_common.c +++ b/openair1/PHY/nr_phy_common/src/nr_phy_common.c @@ -26,22 +26,6 @@ #endif #define PEAK_DETECT_THRESHOLD 15 - -simde__m128i byte2bit16_lut[256]; -void init_byte2bit16(void) -{ - for (int s = 0; s < 256; s++) { - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], s & 1, 0); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 1) & 1, 1); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 2) & 1, 2); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 3) & 1, 3); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 4) & 1, 4); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 5) & 1, 5); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 6) & 1, 6); - byte2bit16_lut[s] = simde_mm_insert_epi16(byte2bit16_lut[s], (s >> 7) & 1, 7); - } -} - simde__m128i byte2m128i[256]; void init_byte2m128i(void) { @@ -420,6 +404,28 @@ unsigned int nr_get_tx_amp(int power_dBm, int power_max_dBm, int total_nb_rb, in return (0); } +// compute average channel_level on each antenna +void nr_channel_level(const int symbol, + const int size_est, + const c16_t ch_estimates_ext[][size_est], + const int nb_rx, + const int Nl, + int32_t avg[nb_rx * Nl], + const uint32_t len) +{ + int16_t x = factor2(len); + int16_t y = len >> x; + for (int aarx = 0; aarx < nb_rx; aarx++) { + for (int l = 0; l < Nl; l++) { + simde__m128i *ch128 = (simde__m128i *)&ch_estimates_ext[l * nb_rx + aarx][symbol * len]; + //compute average level + avg[l * nb_rx + aarx] = simde_mm_average(ch128, len, x, y); + LOG_D(PHY, "Channel level: %d\n", avg[l * nb_rx + aarx]); + } + } +} + + void nr_fo_compensation(double fo_Hz, int samples_per_ms, int sample_offset, const c16_t *rxdata_in, c16_t *rxdata_out, int size) { const double phase_inc = -fo_Hz / (samples_per_ms * 1000); diff --git a/openair1/SIMULATION/LTE_PHY/dlsim.c b/openair1/SIMULATION/LTE_PHY/dlsim.c index 5e6140db9d..a593a0dfb4 100644 --- a/openair1/SIMULATION/LTE_PHY/dlsim.c +++ b/openair1/SIMULATION/LTE_PHY/dlsim.c @@ -90,7 +90,6 @@ static int cmpdouble(const void *p1, const void *p2) { } RAN_CONTEXT_t RC; -int emulate_rf = 0; void handler(int sig) { void *array[10]; size_t size; diff --git a/openair1/SIMULATION/LTE_PHY/mbmssim.c b/openair1/SIMULATION/LTE_PHY/mbmssim.c index ac50bf62bd..dac151e4fe 100644 --- a/openair1/SIMULATION/LTE_PHY/mbmssim.c +++ b/openair1/SIMULATION/LTE_PHY/mbmssim.c @@ -89,8 +89,6 @@ int n_rx_dropped = 0; /*!< \brief initial max process time for rx */ THREAD_STRUCT thread_struct; -int emulate_rf = 0; - void handler(int sig) { void *array[10]; size_t size; diff --git a/openair1/SIMULATION/NR_PHY/dlsim.c b/openair1/SIMULATION/NR_PHY/dlsim.c index e355ca51c2..0f21fdb145 100644 --- a/openair1/SIMULATION/NR_PHY/dlsim.c +++ b/openair1/SIMULATION/NR_PHY/dlsim.c @@ -1341,7 +1341,7 @@ printf("%d\n", slot); printStatIndent3(&gNB->tprep,"DLSCH LDPC input preparation time"); printStatIndent3(&gNB->tparity,"DLSCH LDPC parity generation time"); printStatIndent3(&gNB->toutput,"DLSCH LDPC output generation time"); - printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Mataching time"); + printStatIndent3(&gNB->dlsch_rate_matching_stats,"DLSCH Rate Matching time"); printStatIndent3(&gNB->dlsch_interleaving_stats, "DLSCH Interleaving time"); printStatIndent2(&gNB->dlsch_modulation_stats,"DLSCH modulation time"); printStatIndent2(&gNB->dlsch_scrambling_stats, "DLSCH scrambling time"); diff --git a/openair1/SIMULATION/NR_PHY/ulsim.c b/openair1/SIMULATION/NR_PHY/ulsim.c index b2b2dc38bb..e5361a3e2d 100644 --- a/openair1/SIMULATION/NR_PHY/ulsim.c +++ b/openair1/SIMULATION/NR_PHY/ulsim.c @@ -237,6 +237,8 @@ int main(int argc, char *argv[]) int max_ldpc_iterations = 5; int num_antennas_per_thread = 1; uint32_t log_format = 0; + int threequarter_fs = 0; + if ((uniqCfg = load_configmodule(argc, argv, CONFIG_ENABLECMDLINEONLY)) == 0) { exit_fun("[NR_ULSIM] Error, configuration module init failed\n"); } @@ -351,7 +353,7 @@ int main(int argc, char *argv[]) case 'k': printf("Setting threequarter_fs_flag\n"); - openair0_cfg[0].threequarter_fs= 1; + threequarter_fs = 1; break; case 'm': @@ -584,7 +586,7 @@ int main(int argc, char *argv[]) uint32_t samples; get_samplerate_and_bw(mu, N_RB_DL, - openair0_cfg[0].threequarter_fs, + threequarter_fs, &sampling_frequency, &samples, &tx_bandwidth, @@ -1026,6 +1028,9 @@ int main(int argc, char *argv[]) reset_meas(&gNB->rx_pusch_init_stats); reset_meas(&gNB->rx_pusch_symbol_processing_stats); reset_meas(&gNB->ulsch_decoding_stats); + reset_meas(&gNB->ts_deinterleave); + reset_meas(&gNB->ts_rate_unmatch); + reset_meas(&gNB->ts_ldpc_decode); reset_meas(&gNB->ulsch_channel_estimation_stats); reset_meas(&gNB->pusch_channel_estimation_antenna_processing_stats); reset_meas(&gNB->rx_srs_stats); @@ -1587,6 +1592,9 @@ int main(int argc, char *argv[]) printStatIndent2(&gNB->rx_pusch_init_stats, "RX PUSCH Initialization time"); printStatIndent2(&gNB->rx_pusch_symbol_processing_stats, "RX PUSCH Symbol Processing time"); printStatIndent(&gNB->ulsch_decoding_stats,"ULSCH total decoding time"); + printStatIndent2(&gNB->ts_deinterleave, "ULSCH segment deinterleaving time"); + printStatIndent2(&gNB->ts_rate_unmatch, "ULSCH segment rate recovery time"); + printStatIndent2(&gNB->ts_ldpc_decode, "ULSCH segments decoding time"); printf("\nUE TX\n"); for (int i = PHY_PROC_TX; i <= OFDM_MOD_STATS; i++) { diff --git a/openair1/SIMULATION/TOOLS/sim.h b/openair1/SIMULATION/TOOLS/sim.h index 98b4fb26c6..359df00b55 100644 --- a/openair1/SIMULATION/TOOLS/sim.h +++ b/openair1/SIMULATION/TOOLS/sim.h @@ -77,6 +77,8 @@ typedef struct { struct complexd **a; ///interpolated (sample-spaced) channel impulse response. size(ch) = (n_tx * n_rx) * channel_length. ATTENTION: the dimensions of ch are the transposed ones of a. This is to allow the use of BLAS when applying the correlation matrices to the state. struct complexd **ch; + ///Same as above but single precision + struct complexf **ch_ps; ///Sampled frequency response (90 kHz resolution) struct complexd **chF; ///Maximum path delay in mus. diff --git a/openair2/E1AP/e1ap_common.h b/openair2/E1AP/e1ap_common.h index 452762965c..6c556a6cc6 100644 --- a/openair2/E1AP/e1ap_common.h +++ b/openair2/E1AP/e1ap_common.h @@ -41,8 +41,6 @@ typedef struct e1ap_upcp_inst_s { e1ap_net_config_t net_config; } e1ap_upcp_inst_t; -extern int asn1_xer_print; - // forward declaration so we don't require E1AP ASN.1 when including this // header struct E1AP_E1AP_PDU; diff --git a/openair2/GNB_APP/gnb_paramdef.h b/openair2/GNB_APP/gnb_paramdef.h index db04c5a8da..b751d78733 100644 --- a/openair2/GNB_APP/gnb_paramdef.h +++ b/openair2/GNB_APP/gnb_paramdef.h @@ -49,9 +49,6 @@ #define GNB_CONF_STRING_OTG_APP_TYPE "app_type" #define GNB_CONF_STRING_OTG_BG_TRAFFIC "bg_traffic" -extern int asn_debug; -extern int asn1_xer_print; - #ifdef LIBCONFIG_LONG #define libconfig_int long #else diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c index cd51f6a48f..1a5431bdd2 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.c @@ -1972,49 +1972,6 @@ static const int32_t table_6_4_1_1_3_4_pusch_dmrs_positions_l[12][8] = { {0, 3072, -1, -1, 3, 1539, -1, -1}, // 14 // (DMRS l' position) }; -// TS 38.212 -static const uint16_t table_7_3_1_1_2_2_1layer[28] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 32, 33, - 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; -static const uint16_t table_7_3_1_1_2_2_2layers[22] = {4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, - 25, 26, 27, 48, 49, 50, 51, 52, 53, 54, 55}; -static const uint16_t table_7_3_1_1_2_2_3layers[7] = {10, 28, 29, 56, 57, 58, 59}; -static const uint16_t table_7_3_1_1_2_2_4layers[5] = {11, 30, 31, 60, 61}; -static const uint16_t table_7_3_1_1_2_2B_1layer[16] = {0, 1, 2, 3, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 24, 25}; -static const uint16_t table_7_3_1_1_2_2B_2layers[14] = {4, 5, 6, 7, 8, 9, 13, 26, 27, 28, 29, 30, 31, 32}; -static const uint16_t table_7_3_1_1_2_2B_3layers[3] = {10, 14, 33}; -static const uint16_t table_7_3_1_1_2_2B_4layers[3] = {11, 34, 35}; -static const uint16_t table_7_3_1_1_2_2A_1layer[16] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 20, 10, 21, 22}; -static const uint16_t table_7_3_1_1_2_2A_2layers[14] = {4, 5, 6, 7, 8, 9, 11, 23, 24, 25, 26, 27, 28, 29}; -static const uint16_t table_7_3_1_1_2_3A[16] = {0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 14, 15}; -static const uint16_t table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[6] = {0, 1, 3, 4, 5, 6}; -static const uint16_t table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[3] = {2, 7, 8}; -static const uint16_t table_7_3_1_1_2_4A_1layer[3] = {0, 1, 3}; -static const uint16_t table_7_3_1_1_2_28[3][15] = { - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; -static const uint16_t table_7_3_1_1_2_29[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0}, -}; -static const uint16_t table_7_3_1_1_2_30[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0}, -}; -static const uint16_t table_7_3_1_1_2_31[3][15] = { - {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, - {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, -}; -static const uint16_t table_7_3_1_1_2_32[3][15] = { - {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, - {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, -}; - // the following tables contain 10 times the value reported in 214 (in line with SCF specification and to avoid fractional values) //Table 5.1.3.1-1 of 38.214 static const uint16_t Table_51311[32][2] = {{2, 1200}, {2, 1570}, {2, 1930}, {2, 2510}, {2, 3080}, {2, 3790}, {2, 4490}, {2, 5260}, @@ -2503,61 +2460,61 @@ static int binomial(int n, int k) return c; } +int srs_codebook_nb_res(NR_SRS_Config_t *srs_config) +{ + int count = 0; + for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { + if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_codebook) + count++; + } + return count; +} + +int srs_non_codebook_nb_res(NR_SRS_Config_t *srs_config) +{ + int count = 0; + for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { + if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_nonCodebook) + count++; + } + return count; +} + +int srs_binomial_sum(int count, int Lmax) +{ + int lmin = 0; + int lsum = 0; + lmin = count < Lmax ? count : Lmax; + for (int k = 1; k <= lmin; k++) + lsum += binomial(count, k); + return lsum; +} + // #define DEBUG_SRS_RESOURCE_IND -uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, - NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - nr_srs_feedback_t *srs_feedback, - uint32_t *val) + +static uint8_t compute_srs_resource_indicator_size(long *maxMIMO_Layers, NR_PUSCH_Config_t *pusch_Config, NR_SRS_Config_t *srs_config) { uint8_t nbits = 0; - - // SRI occupies a number of bits which is dependent upon the uplink transmission scheme, and it is used to determine - // the antenna ports and uplink transmission beam to use for PUSCH transmission. In the case of codebook based - // transmission, the SRI is used to select between SRS Resources belonging to different antenna panels - // (kind of directional antenna). There can be up to 2 SRS Resources (2 antenna panels). In the case of non-codebook - // based transmission, the SRI is used to select one or more SRS Resources from a set of N_SRS resources. The number - // of SRS Resources selected corresponds to the number of layers (rank) to be transmitted. - if (val) { - *val = 0; - } - if (srs_config && pusch_Config && pusch_Config->txConfig != NULL) { - if (*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook) { - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "*pusch_Config->txConfig = NR_PUSCH_Config__txConfig_codebook\n"); #endif - // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(N_SRS)) bits according to // Tables 7.3.1.1.2-32, 7.3.1.1.2-32A and 7.3.1.1.2-32B if the higher layer parameter txConfig = codebook, // where N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer // parameter srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value codeBook. - int count = 0; - for (int i=0; isrs_ResourceSetToAddModList->list.count; i++) { - if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_codebook) { - count++; - } - } - if (count>0) { + int count = srs_codebook_nb_res(srs_config); + if (count > 0) nbits = ceil(log2(count)); - if (val && srs_feedback && count > 1) { - *val = table_7_3_1_1_2_32[count-2][srs_feedback->sri]; - } - } - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "srs_config->srs_ResourceSetToAddModList->list.count = %i\n", srs_config->srs_ResourceSetToAddModList->list.count); LOG_I(NR_MAC, "count = %i\n", count); #endif - } else { - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "*pusch_Config->txConfig = NR_PUSCH_Config__txConfig_nonCodebook\n"); #endif - // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(sum(k = 1 until min(Lmax,N_SRS) of binomial(N_SRS,k)))) // bits according to Tables 7.3.1.1.2-28/29/30/31 if the higher layer parameter txConfig = nonCodebook, where // N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer parameter @@ -2572,68 +2529,29 @@ uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, if (maxMIMO_Layers != NULL) Lmax = *maxMIMO_Layers; else - AssertFatal(1 == 0, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); - int lmin = 0; - int lsum = 0; - int count = 0; - for (int i = 0; i < srs_config->srs_ResourceSetToAddModList->list.count; i++) { - if (srs_config->srs_ResourceSetToAddModList->list.array[i]->usage == NR_SRS_ResourceSet__usage_nonCodebook) { - count++; - } - } - lmin = count < Lmax ? count : Lmax; - for (int k=1;k<=lmin;k++) { - lsum += binomial(count,k); - } - if (lsum>0) { + AssertFatal(false, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); + int count = srs_non_codebook_nb_res(srs_config); + int lsum = srs_binomial_sum(count, Lmax); + if (lsum > 0) nbits = ceil(log2(lsum)); - if (val && srs_feedback && nbits > 0) { - switch(Lmax) { - case 1: - *val = table_7_3_1_1_2_28[count-2][srs_feedback->sri]; - break; - case 2: - *val = table_7_3_1_1_2_29[count-2][srs_feedback->sri]; - break; - case 3: - *val = table_7_3_1_1_2_30[count-2][srs_feedback->sri]; - break; - case 4: - *val = table_7_3_1_1_2_31[count-2][srs_feedback->sri]; - break; - default: - LOG_E(NR_MAC, "%s (%d) - Invalid Lmax %d\n", __FUNCTION__, __LINE__, Lmax); - } - } - } - #ifdef DEBUG_SRS_RESOURCE_IND LOG_I(NR_MAC, "srs_config->srs_ResourceSetToAddModList->list.count = %i\n", srs_config->srs_ResourceSetToAddModList->list.count); LOG_I(NR_MAC, "count = %i\n", count); LOG_I(NR_MAC, "Lmax = %i\n", Lmax); LOG_I(NR_MAC, "lsum = %i\n", lsum); #endif - } } - return nbits; } -uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - dci_field_t srs_resource_indicator, - nr_srs_feedback_t *srs_feedback, - const uint8_t *nrOfLayers, - int *tpmi, - uint32_t *val) +static uint8_t compute_precoding_info_size(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator) { // It is only applicable to codebook based transmission. This field occupies 0 bits for non-codebook based // transmission. It also occupies 0 bits for codebook based transmission using a single antenna port. uint8_t nbits = 0; - if (val) { - *val = 0; - } uint8_t pusch_antenna_ports = get_pusch_nb_antenna_ports(pusch_Config, srs_config, srs_resource_indicator); if (!pusch_Config @@ -2645,7 +2563,6 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, long max_rank = *pusch_Config->maxRank; long *ul_FullPowerTransmission = pusch_Config->ext1 ? pusch_Config->ext1->ul_FullPowerTransmission_r16 : NULL; long *codebookSubset = pusch_Config->codebookSubset; - int ul_tpmi = tpmi ? *tpmi : srs_feedback ? srs_feedback->tpmi : -1; if (pusch_antenna_ports == 2) { @@ -2658,23 +2575,11 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, // of higher layer parameter codebookSubset; if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 2,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 1; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 1,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } else { nbits = 3; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 5,"TPMI %d is invalid!\n", ul_tpmi); - *val = ul_tpmi; - } } } } else { @@ -2685,27 +2590,11 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, // fullpowerMode1, transform precoder is disabled, maxRank=2, and codebookSubset=nonCoherent; if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { nbits = 2; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 2) || (*nrOfLayers==2 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4A_1layer[ul_tpmi] : 2; - } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 1) || (*nrOfLayers==2 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? ul_tpmi : 2; - } } else { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 5) || (*nrOfLayers==2 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - *val = *nrOfLayers==1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[ul_tpmi] : - table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[ul_tpmi]; - } } } } @@ -2722,37 +2611,16 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 3; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 3 || ul_tpmi == 13, "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 4; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 15, "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = table_7_3_1_1_2_3A[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 2; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 3, "TPMI %d is invalid!\n", ul_tpmi); - } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 11, "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 5; - if (val && srs_feedback) { - AssertFatal(ul_tpmi <= 27, "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = ul_tpmi; } } } else { @@ -2769,99 +2637,26 @@ uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, if (max_rank == 2) { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi==13)) || (*nrOfLayers==2 && ul_tpmi <= 6), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 5; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - *val = *nrOfLayers==1 ? table_7_3_1_1_2_2A_1layer[ul_tpmi] : table_7_3_1_1_2_2A_2layers[ul_tpmi]; } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && (ul_tpmi <= 3 || ul_tpmi == 13)) || (*nrOfLayers==2 && ul_tpmi <= 6) || - (*nrOfLayers==3 && ul_tpmi <= 1) || (*nrOfLayers==4 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 6; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 15) || (*nrOfLayers==2 && ul_tpmi <= 13) || - (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - switch (*nrOfLayers) { - case 1: - *val = table_7_3_1_1_2_2B_1layer[ul_tpmi]; - break; - case 2: - *val = table_7_3_1_1_2_2B_2layers[ul_tpmi]; - break; - case 3: - *val = table_7_3_1_1_2_2B_3layers[ul_tpmi]; - break; - case 4: - *val = table_7_3_1_1_2_2B_4layers[ul_tpmi]; - break; - default: - LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); - } } } } else { if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { nbits = 4; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 3) || (*nrOfLayers==2 && ul_tpmi <= 5) || - (*nrOfLayers==3 && ul_tpmi == 0) || (*nrOfLayers==4 && ul_tpmi == 0), - "TPMI %d is invalid!\n", ul_tpmi); - } } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { nbits = 5; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 11) || (*nrOfLayers==2 && ul_tpmi <= 13) || - (*nrOfLayers==3 && ul_tpmi <= 2) || (*nrOfLayers==4 && ul_tpmi <= 2), - "TPMI %d is invalid!\n", ul_tpmi); - } } else { nbits = 6; - if (val && srs_feedback) { - AssertFatal((*nrOfLayers==1 && ul_tpmi <= 28) || (*nrOfLayers==2 && ul_tpmi <= 22) || - (*nrOfLayers==3 && ul_tpmi <= 7) || (*nrOfLayers==4 && ul_tpmi <= 5), - "TPMI %d is invalid!\n", ul_tpmi); - } - } - if (val && srs_feedback) { - switch (*nrOfLayers) { - case 1: - *val = table_7_3_1_1_2_2_1layer[ul_tpmi]; - break; - case 2: - *val = table_7_3_1_1_2_2_2layers[ul_tpmi]; - break; - case 3: - *val = table_7_3_1_1_2_2_3layers[ul_tpmi]; - break; - case 4: - *val = table_7_3_1_1_2_2_4layers[ul_tpmi]; - break; - default: - LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); - } } } } - } return nbits; } @@ -3100,12 +2895,11 @@ uint16_t nr_dci_size(const NR_UE_DL_BWP_t *DL_BWP, } // SRS resource indicator dci_pdu->srs_resource_indicator.nbits = - compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, pusch_Config, srs_config, NULL, NULL); + compute_srs_resource_indicator_size(sc_info->maxMIMO_Layers_PUSCH, pusch_Config, srs_config); size += dci_pdu->srs_resource_indicator.nbits; LOG_D(NR_MAC, "dci_pdu->srs_resource_indicator.nbits %d\n", dci_pdu->srs_resource_indicator.nbits); // Precoding info and number of layers - dci_pdu->precoding_information.nbits = - compute_precoding_information(pusch_Config, srs_config, dci_pdu->srs_resource_indicator, NULL, NULL, NULL, NULL); + dci_pdu->precoding_information.nbits = compute_precoding_info_size(pusch_Config, srs_config, dci_pdu->srs_resource_indicator); size += dci_pdu->precoding_information.nbits; LOG_D(NR_MAC, "dci_pdu->precoding_informaiton.nbits=%d\n", dci_pdu->precoding_information.nbits); // Antenna ports diff --git a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h index ff6775df96..f76f54b158 100644 --- a/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h +++ b/openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h @@ -41,6 +41,49 @@ #define NB_SRS_PERIOD (18) static const uint16_t srs_period[NB_SRS_PERIOD] = { 0, 1, 2, 4, 5, 8, 10, 16, 20, 32, 40, 64, 80, 160, 320, 640, 1280, 2560}; +// TS 38.212 +static const uint16_t table_7_3_1_1_2_2_1layer[28] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 32, 33, + 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47}; +static const uint16_t table_7_3_1_1_2_2_2layers[22] = {4, 5, 6, 7, 8, 9, 20, 21, 22, 23, 24, + 25, 26, 27, 48, 49, 50, 51, 52, 53, 54, 55}; +static const uint16_t table_7_3_1_1_2_2_3layers[7] = {10, 28, 29, 56, 57, 58, 59}; +static const uint16_t table_7_3_1_1_2_2_4layers[5] = {11, 30, 31, 60, 61}; +static const uint16_t table_7_3_1_1_2_2B_1layer[16] = {0, 1, 2, 3, 15, 16, 17, 18, 19, 20, 21, 22, 23, 12, 24, 25}; +static const uint16_t table_7_3_1_1_2_2B_2layers[14] = {4, 5, 6, 7, 8, 9, 13, 26, 27, 28, 29, 30, 31, 32}; +static const uint16_t table_7_3_1_1_2_2B_3layers[3] = {10, 14, 33}; +static const uint16_t table_7_3_1_1_2_2B_4layers[3] = {11, 34, 35}; +static const uint16_t table_7_3_1_1_2_2A_1layer[16] = {0, 1, 2, 3, 12, 13, 14, 15, 16, 17, 18, 19, 20, 10, 21, 22}; +static const uint16_t table_7_3_1_1_2_2A_2layers[14] = {4, 5, 6, 7, 8, 9, 11, 23, 24, 25, 26, 27, 28, 29}; +static const uint16_t table_7_3_1_1_2_3A[16] = {0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13, 4, 14, 15}; +static const uint16_t table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[6] = {0, 1, 3, 4, 5, 6}; +static const uint16_t table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[3] = {2, 7, 8}; +static const uint16_t table_7_3_1_1_2_4A_1layer[3] = {0, 1, 3}; +static const uint16_t table_7_3_1_1_2_28[3][15] = { + {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; +static const uint16_t table_7_3_1_1_2_29[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0}, +}; +static const uint16_t table_7_3_1_1_2_30[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0}, +}; +static const uint16_t table_7_3_1_1_2_31[3][15] = { + {0, 1, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 3, 4, 0, 5, 0, 0, 6, 0, 0, 0, 0}, + {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, +}; +static const uint16_t table_7_3_1_1_2_32[3][15] = { + {0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, + {0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, +}; + typedef enum { pusch_dmrs_pos0 = 0, pusch_dmrs_pos1 = 1, @@ -81,20 +124,6 @@ void config_frame_structure(int mu, uint8_t frame_type, frame_structure_t *fs); -uint8_t compute_srs_resource_indicator(long *maxMIMO_Layers, - NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - nr_srs_feedback_t *srs_feedback, - uint32_t *val); - -uint8_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, - NR_SRS_Config_t *srs_config, - dci_field_t srs_resource_indicator, - nr_srs_feedback_t *srs_feedback, - const uint8_t *nrOfLayers, - int *tpmi, - uint32_t *val); - NR_PDSCH_TimeDomainResourceAllocationList_t *get_dl_tdalist(const NR_UE_DL_BWP_t *DL_BWP, int controlResourceSetId, int ss_type, @@ -197,7 +226,9 @@ uint32_t nr_get_code_rate_dl(uint8_t Imcs, uint8_t table_idx); /** \brief Computes Q based on I_MCS PDSCH and table_idx for uplink. Implements MCS Tables from 38.214. */ uint8_t nr_get_Qm_ul(uint8_t Imcs, uint8_t table_idx); uint32_t nr_get_code_rate_ul(uint8_t Imcs, uint8_t table_idx); - +int srs_binomial_sum(int count, int Lmax); +int srs_codebook_nb_res(NR_SRS_Config_t *srs_config); +int srs_non_codebook_nb_res(NR_SRS_Config_t *srs_config); uint16_t get_nr_srs_offset(NR_SRS_PeriodicityAndOffset_t periodicityAndOffset); void get_monitoring_period_offset(const NR_SearchSpace_t *ss, int *period, int *offset); diff --git a/openair2/LAYER2/NR_MAC_UE/config_ue.c b/openair2/LAYER2/NR_MAC_UE/config_ue.c index 73bb02a575..d764f7cb86 100644 --- a/openair2/LAYER2/NR_MAC_UE/config_ue.c +++ b/openair2/LAYER2/NR_MAC_UE/config_ue.c @@ -700,8 +700,11 @@ static void modlist_coreset(NR_ControlResourceSet_t *source, NR_ControlResourceS if (id == *target->tci_StatesPDCCH_ToAddList->list.array[j]) break; } - if (j == target->tci_StatesPDCCH_ToAddList->list.count) - ASN_SEQUENCE_ADD(&target->tci_StatesPDCCH_ToAddList->list, source->tci_StatesPDCCH_ToAddList->list.array[i]); + if (j == target->tci_StatesPDCCH_ToAddList->list.count) { + long *tci_id = calloc(1, sizeof(*tci_id)); + *tci_id = id; + ASN_SEQUENCE_ADD(&target->tci_StatesPDCCH_ToAddList->list, tci_id); + } } } else UPDATE_IE(target->tci_StatesPDCCH_ToAddList, diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c index 44d4affb08..c88ad3bd96 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c @@ -594,8 +594,15 @@ typedef struct UEsched_s { NR_UE_info_t * UE; } UEsched_t; -static int comparator(const void *p, const void *q) { - return ((UEsched_t*)p)->coef < ((UEsched_t*)q)->coef; +static int comparator(const void *p, const void *q) +{ + const UEsched_t *pp = p; + const UEsched_t *qq = q; + if (pp->coef < qq->coef) + return 1; + else if (pp->coef > qq->coef) + return -1; + return 0; } static void pf_dl(module_id_t module_id, @@ -613,7 +620,7 @@ static void pf_dl(module_id_t module_id, int remainUEs[num_beams]; for (int i = 0; i < num_beams; i++) remainUEs[i] = max_num_ue; - int curUE = 0; + int numUE = 0; int CC_id = 0; int slots_per_frame = mac->frame_structure.numb_slots_frame; @@ -704,13 +711,13 @@ static void pf_dl(module_id_t module_id, tbs, coeff_ue); /* Create UE_sched list for UEs eligible for new transmission*/ - UE_sched[curUE].coef=coeff_ue; - UE_sched[curUE].UE=UE; - curUE++; + UE_sched[numUE].coef = coeff_ue; + UE_sched[numUE].UE = UE; + numUE++; } } - qsort(UE_sched, sizeofArray(UE_sched), sizeof(UEsched_t), comparator); + qsort(UE_sched, numUE, sizeof(UEsched_t), comparator); UEsched_t *iterator = UE_sched; const int min_rbSize = 5; diff --git a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c index 7e0f84e2fe..264150f314 100644 --- a/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c +++ b/openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_primitives.c @@ -895,6 +895,265 @@ dci_pdu_rel15_t prepare_dci_dl_payload(const gNB_MAC_INST *gNB_mac, return dci_payload; } +static uint32_t compute_srs_resource_indicator(long *maxMIMO_Layers, + NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + nr_srs_feedback_t *srs_feedback) +{ + uint32_t val = 0; + if (srs_config && pusch_Config && pusch_Config->txConfig != NULL) { + if (*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook) { + + // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(N_SRS)) bits according to + // Tables 7.3.1.1.2-32, 7.3.1.1.2-32A and 7.3.1.1.2-32B if the higher layer parameter txConfig = codebook, + // where N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer + // parameter srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value codeBook. + int count = srs_codebook_nb_res(srs_config); + if (count > 1) + val = table_7_3_1_1_2_32[count - 2][srs_feedback->sri]; + } else { + // TS 38.212 - Section 7.3.1.1.2: SRS resource indicator has ceil(log2(sum(k = 1 until min(Lmax,N_SRS) of binomial(N_SRS,k)))) + // bits according to Tables 7.3.1.1.2-28/29/30/31 if the higher layer parameter txConfig = nonCodebook, where + // N_SRS is the number of configured SRS resources in the SRS resource set configured by higher layer parameter + // srs-ResourceSetToAddModList, and associated with the higher layer parameter usage of value nonCodeBook and: + // + // - if UE supports operation with maxMIMO-Layers and the higher layer parameter maxMIMO-Layers of + // PUSCH-ServingCellConfig of the serving cell is configured, Lmax is given by that parameter; + // + // - otherwise, Lmax is given by the maximum number of layers for PUSCH supported by the UE for the serving cell + // for non-codebook based operation. + int Lmax = 0; + if (maxMIMO_Layers != NULL) + Lmax = *maxMIMO_Layers; + else + AssertFatal(false, "MIMO on PUSCH not supported, maxMIMO_Layers needs to be set to 1\n"); + int count = srs_non_codebook_nb_res(srs_config); + int lsum = srs_binomial_sum(count, Lmax); + if (lsum > 0 && ceil(log2(lsum)) > 0) { + switch(Lmax) { + case 1: + val = table_7_3_1_1_2_28[count-2][srs_feedback->sri]; + break; + case 2: + val = table_7_3_1_1_2_29[count-2][srs_feedback->sri]; + break; + case 3: + val = table_7_3_1_1_2_30[count-2][srs_feedback->sri]; + break; + case 4: + val = table_7_3_1_1_2_31[count-2][srs_feedback->sri]; + break; + default: + LOG_E(NR_MAC, "%s (%d) - Invalid Lmax %d\n", __FUNCTION__, __LINE__, Lmax); + } + } + } + } + return val; +} + +static uint32_t compute_precoding_information(NR_PUSCH_Config_t *pusch_Config, + NR_SRS_Config_t *srs_config, + dci_field_t srs_resource_indicator, + nr_srs_feedback_t *srs_feedback, + const uint8_t *nrOfLayers, + int *tpmi) +{ + uint32_t val = 0; + + uint8_t pusch_antenna_ports = get_pusch_nb_antenna_ports(pusch_Config, srs_config, srs_resource_indicator); + if (!pusch_Config + || (pusch_Config->txConfig != NULL && *pusch_Config->txConfig == NR_PUSCH_Config__txConfig_nonCodebook) + || pusch_antenna_ports == 1) { + return val; + } + + long max_rank = *pusch_Config->maxRank; + long *ul_FullPowerTransmission = pusch_Config->ext1 ? pusch_Config->ext1->ul_FullPowerTransmission_r16 : NULL; + long *codebookSubset = pusch_Config->codebookSubset; + int ul_tpmi = tpmi ? *tpmi : srs_feedback ? srs_feedback->tpmi : -1; + + if (pusch_antenna_ports == 2) { + + if (max_rank == 1) { + // - 1 or 3 bits according to Table 7.3.1.1.2-5 for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank and codebookSubset; + // - 2 bits according to Table 7.3.1.1.2-5A for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=1, and according to whether transform precoder is enabled or disabled, and the values + // of higher layer parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + AssertFatal(ul_tpmi <= 2,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 1,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } else { + AssertFatal(ul_tpmi <= 5,"TPMI %d is invalid!\n", ul_tpmi); + val = ul_tpmi; + } + } + } else { + // - 2 or 4 bits according to Table 7.3.1.1.2-4 for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank and codebookSubset; + // - 2 bits according to Table 7.3.1.1.2-4A for 2 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, transform precoder is disabled, maxRank=2, and codebookSubset=nonCoherent; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 2) + || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? table_7_3_1_1_2_4A_1layer[ul_tpmi] : 2; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 1) + || (*nrOfLayers==2 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? ul_tpmi : 2; + } else { + AssertFatal((*nrOfLayers==1 && ul_tpmi <= 5) + || (*nrOfLayers==2 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + val = *nrOfLayers == 1 ? table_7_3_1_1_2_4_1layer_fullyAndPartialAndNonCoherent[ul_tpmi] : + table_7_3_1_1_2_4_2layers_fullyAndPartialAndNonCoherent[ul_tpmi]; + } + } + } + + } else if (pusch_antenna_ports == 4) { + + if (max_rank == 1) { + // - 2, 4, or 5 bits according to Table 7.3.1.1.2-3 for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank, and codebookSubset; + // - 3 or 4 bits according to Table 7.3.1.1.2-3A for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=1, and according to whether transform precoder is enabled or disabled, and the values + // of higher layer parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 3 || ul_tpmi == 13, "TPMI %d is invalid!\n", ul_tpmi); + } else { + AssertFatal(ul_tpmi <= 15, "TPMI %d is invalid!\n", ul_tpmi); + } + val = table_7_3_1_1_2_3A[ul_tpmi]; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal(ul_tpmi <= 3, "TPMI %d is invalid!\n", ul_tpmi); + } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { + AssertFatal(ul_tpmi <= 11, "TPMI %d is invalid!\n", ul_tpmi); + } else { + AssertFatal(ul_tpmi <= 27, "TPMI %d is invalid!\n", ul_tpmi); + } + val = ul_tpmi; + } + } else { + // - 4, 5, or 6 bits according to Table 7.3.1.1.2-2 for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission + // is not configured or configured to fullpowerMode2 or configured to fullpower, and according to whether transform + // precoder is enabled or disabled, and the values of higher layer parameters maxRank, and codebookSubset; + // - 4 or 5 bits according to Table 7.3.1.1.2-2A for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=2, transform precoder is disabled, and according to the values of higher layer parameter + // codebookSubset; + // - 4 or 6 bits according to Table 7.3.1.1.2-2B for 4 antenna ports, if txConfig = codebook, ul-FullPowerTransmission = + // fullpowerMode1, maxRank=3 or 4, transform precoder is disabled, and according to the values of higher layer + // parameter codebookSubset; + if (ul_FullPowerTransmission && *ul_FullPowerTransmission == NR_PUSCH_Config__ext1__ul_FullPowerTransmission_r16_fullpowerMode1) { + if (max_rank == 2) { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers==1 + && (ul_tpmi <= 3 || ul_tpmi == 13)) + || (*nrOfLayers == 2 && ul_tpmi <= 6), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 15) + || (*nrOfLayers == 2 && ul_tpmi <= 13), + "TPMI %d is invalid!\n", + ul_tpmi); + } + val = *nrOfLayers == 1 ? table_7_3_1_1_2_2A_1layer[ul_tpmi] : table_7_3_1_1_2_2A_2layers[ul_tpmi]; + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers == 1 + && (ul_tpmi <= 3 || ul_tpmi == 13)) + || (*nrOfLayers == 2 && ul_tpmi <= 6) + || (*nrOfLayers == 3 && ul_tpmi <= 1) + || (*nrOfLayers == 4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 15) + || (*nrOfLayers == 2 && ul_tpmi <= 13) + || (*nrOfLayers == 3 && ul_tpmi <= 2) + || (*nrOfLayers == 4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + } + switch (*nrOfLayers) { + case 1: + val = table_7_3_1_1_2_2B_1layer[ul_tpmi]; + break; + case 2: + val = table_7_3_1_1_2_2B_2layers[ul_tpmi]; + break; + case 3: + val = table_7_3_1_1_2_2B_3layers[ul_tpmi]; + break; + case 4: + val = table_7_3_1_1_2_2B_4layers[ul_tpmi]; + break; + default: + LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); + } + } + } else { + if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_nonCoherent) { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 3) + || (*nrOfLayers == 2 && ul_tpmi <= 5) + || (*nrOfLayers == 3 && ul_tpmi == 0) + || (*nrOfLayers == 4 && ul_tpmi == 0), + "TPMI %d is invalid!\n", + ul_tpmi); + } else if (codebookSubset && *codebookSubset == NR_PUSCH_Config__codebookSubset_partialAndNonCoherent) { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 11) + || (*nrOfLayers == 2 && ul_tpmi <= 13) + || (*nrOfLayers == 3 && ul_tpmi <= 2) + || (*nrOfLayers == 4 && ul_tpmi <= 2), + "TPMI %d is invalid!\n", + ul_tpmi); + } else { + AssertFatal((*nrOfLayers == 1 && ul_tpmi <= 28) + || (*nrOfLayers == 2 && ul_tpmi <= 22) + || (*nrOfLayers == 3 && ul_tpmi <= 7) + || (*nrOfLayers == 4 && ul_tpmi <= 5), + "TPMI %d is invalid!\n", + ul_tpmi); + } + switch (*nrOfLayers) { + case 1: + val = table_7_3_1_1_2_2_1layer[ul_tpmi]; + break; + case 2: + val = table_7_3_1_1_2_2_2layers[ul_tpmi]; + break; + case 3: + val = table_7_3_1_1_2_2_3layers[ul_tpmi]; + break; + case 4: + val = table_7_3_1_1_2_2_4layers[ul_tpmi]; + break; + default: + LOG_E(NR_MAC,"Number of layers %d is invalid!\n", *nrOfLayers); + } + } + } + } + return val; +} + void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, const nfapi_nr_pusch_pdu_t *pusch_pdu, dci_pdu_rel15_t *dci_pdu_rel15, @@ -940,23 +1199,20 @@ void config_uldci(const NR_UE_ServingCell_Info_t *sc_info, // bwp indicator as per table 7.3.1.1.2-1 in 38.212 dci_pdu_rel15->bwp_indicator.val = sc_info->n_ul_bwp < 4 ? bwp_id : bwp_id - 1; // SRS resource indicator - if (pusch_Config && - pusch_Config->txConfig != NULL) { + if (pusch_Config && pusch_Config->txConfig != NULL) { AssertFatal(*pusch_Config->txConfig == NR_PUSCH_Config__txConfig_codebook, "Non Codebook configuration non supported\n"); - compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, - pusch_Config, - ul_bwp->srs_Config, - srs_feedback, - &dci_pdu_rel15->srs_resource_indicator.val); + dci_pdu_rel15->srs_resource_indicator.val = compute_srs_resource_indicator(sc_info->maxMIMO_Layers_PUSCH, + pusch_Config, + ul_bwp->srs_Config, + srs_feedback); } - compute_precoding_information(pusch_Config, - ul_bwp->srs_Config, - dci_pdu_rel15->srs_resource_indicator, - srs_feedback, - &pusch_pdu->nrOfLayers, - tpmi, - &dci_pdu_rel15->precoding_information.val); + dci_pdu_rel15->precoding_information.val = compute_precoding_information(pusch_Config, + ul_bwp->srs_Config, + dci_pdu_rel15->srs_resource_indicator, + srs_feedback, + &pusch_pdu->nrOfLayers, + tpmi); // antenna_ports.val = 0 for transform precoder is disabled, dmrs-Type=1, maxLength=1, Rank=1/2/3/4 // Antenna Ports diff --git a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c index e170e90823..4d1c0b5ba0 100644 --- a/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c +++ b/openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.c @@ -469,7 +469,7 @@ static void deliver_pdu_drb_gnb(void *deliver_pdu_data, ue_id_t ue_id, int rb_id if (NODE_IS_CU(node_type)) { LOG_D(PDCP, "%s() (drb %d) sending message to gtp size %d\n", __func__, rb_id, size); extern instance_t CUuniqInstance; - gtpv1uSendDirect(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size, false, false); + gtpv1uSendDirectWithNRUSeqNum(CUuniqInstance, ue_id, rb_id, (uint8_t *)buf, size); } else { uint8_t *memblock = malloc16(size); memcpy(memblock, buf, size); diff --git a/openair2/M2AP/m2ap_common.c b/openair2/M2AP/m2ap_common.c index aa090d124d..d9183df6fb 100644 --- a/openair2/M2AP/m2ap_common.c +++ b/openair2/M2AP/m2ap_common.c @@ -47,7 +47,7 @@ ssize_t m2ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)&pdu); } @@ -80,7 +80,7 @@ ssize_t m2ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)&pdu); } diff --git a/openair2/M2AP/m2ap_common.h b/openair2/M2AP/m2ap_common.h index 266b303518..a875d8d078 100644 --- a/openair2/M2AP/m2ap_common.h +++ b/openair2/M2AP/m2ap_common.h @@ -50,8 +50,6 @@ # define M2AP_PORT 36423 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "m2ap_default_values.h" #define M2AP_INFO(x, args...) LOG_I(M2AP, x, ##args) diff --git a/openair2/M2AP/m2ap_decoder.c b/openair2/M2AP/m2ap_decoder.c index 1237c4ec8e..4abd43f564 100644 --- a/openair2/M2AP/m2ap_decoder.c +++ b/openair2/M2AP/m2ap_decoder.c @@ -165,7 +165,7 @@ int m2ap_decode_pdu(M2AP_M2AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, pdu); } diff --git a/openair2/M2AP/m2ap_encoder.c b/openair2/M2AP/m2ap_encoder.c index 8f798ff55c..81a3bfce92 100644 --- a/openair2/M2AP/m2ap_encoder.c +++ b/openair2/M2AP/m2ap_encoder.c @@ -44,7 +44,7 @@ int m2ap_encode_pdu(M2AP_M2AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M2AP_M2AP_PDU, (void *)pdu); } diff --git a/openair2/RRC/NR/rrc_gNB.c b/openair2/RRC/NR/rrc_gNB.c index 75f6838a78..000c4f46cb 100644 --- a/openair2/RRC/NR/rrc_gNB.c +++ b/openair2/RRC/NR/rrc_gNB.c @@ -2305,13 +2305,19 @@ static void rrc_CU_process_ue_context_modification_response(MessageDef *msg_p, i uper_decode_complete(NULL, &asn_DEF_NR_CellGroupConfig, (void **)&cellGroupConfig, (uint8_t *)cgc->buf, cgc->len); AssertFatal(dec_rval.code == RC_OK && dec_rval.consumed > 0, "Cell group config decode error\n"); - if (UE->masterCellGroup) { - ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); - LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); + /* hack for interoperability with srsRAN: ignore cell group config if empty */ + /* cell group config is empty if buffer length is 2 and content is all 0 */ + bool cgc_is_empty = cgc->len == 2 && cgc->buf[0] == 0 && cgc->buf[1] == 0; + if (!cgc_is_empty) { + if (UE->masterCellGroup) { + ASN_STRUCT_FREE(asn_DEF_NR_CellGroupConfig, UE->masterCellGroup); + LOG_I(RRC, "UE %04x replacing existing CellGroupConfig with new one received from DU\n", UE->rnti); + } + UE->masterCellGroup = cellGroupConfig; + rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); + } else { + LOG_W(NR_RRC, "hack: UE %d: ignore empty CellGroupConfig in UEContextModificationResponse\n", UE->rrc_ue_id); } - UE->masterCellGroup = cellGroupConfig; - - rrc_gNB_generate_dedicatedRRCReconfiguration(rrc, UE); } // Reconfiguration should have been sent to the UE, so it will attempt the diff --git a/openair2/RRC/NR/rrc_gNB_nsa.c b/openair2/RRC/NR/rrc_gNB_nsa.c index d0cc63062b..aa97a026e0 100644 --- a/openair2/RRC/NR/rrc_gNB_nsa.c +++ b/openair2/RRC/NR/rrc_gNB_nsa.c @@ -77,7 +77,7 @@ // and put it into a CG-ConfigInfo field static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) { - OCTET_STRING_t *os_cap = NULL; + OCTET_STRING_t *list_buf = NULL; if (uecap_file != NULL) { LOG_I(NR_RRC, "creating CG-ConfigInfo from UE capability file %s\n", uecap_file); @@ -98,8 +98,17 @@ static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) DevAssert(dec_rval.code == RC_OK); //xer_fprint(stdout, &asn_DEF_NR_UE_NR_Capability, cap); - os_cap = calloc_or_fail(1, sizeof(*os_cap)); - os_cap->size = uper_encode_to_new_buffer(&asn_DEF_NR_UE_NR_Capability, NULL, cap, (void **)&os_cap->buf); + uint8_t *buf = NULL; + int sizebuf = uper_encode_to_new_buffer(&asn_DEF_NR_UE_NR_Capability, NULL, cap, (void **)&buf); + NR_UE_CapabilityRAT_Container_t *cont = calloc_or_fail(1, sizeof(*cont)); + cont->rat_Type = NR_RAT_Type_nr; + cont->ue_CapabilityRAT_Container.buf = buf; + cont->ue_CapabilityRAT_Container.size = sizebuf; + NR_UE_CapabilityRAT_ContainerList_t *clist = calloc_or_fail(1, sizeof(*clist)); + asn1cSeqAdd(&clist->list, cont); + list_buf = calloc_or_fail(1, sizeof(*list_buf)); + list_buf->size = uper_encode_to_new_buffer(&asn_DEF_NR_UE_CapabilityRAT_ContainerList, NULL, clist, (void **)&list_buf->buf); + ASN_STRUCT_FREE(asn_DEF_NR_UE_CapabilityRAT_ContainerList, clist); } NR_CG_ConfigInfo_t *cgci = calloc_or_fail(1, sizeof(*cgci)); @@ -108,7 +117,7 @@ static int cg_config_info_from_ue_cap_file(uint32_t maxlen, uint8_t buf[maxlen]) cgci->criticalExtensions.choice.c1->present = NR_CG_ConfigInfo__criticalExtensions__c1_PR_cg_ConfigInfo; NR_CG_ConfigInfo_IEs_t *cgci_ies = calloc_or_fail(1, sizeof(*cgci_ies)); cgci->criticalExtensions.choice.c1->choice.cg_ConfigInfo = cgci_ies; - cgci_ies->ue_CapabilityInfo = os_cap; + cgci_ies->ue_CapabilityInfo = list_buf; //xer_fprint(stdout, &asn_DEF_NR_CG_ConfigInfo, cgci); asn_enc_rval_t rval = uper_encode_to_buffer(&asn_DEF_NR_CG_ConfigInfo, NULL, cgci, buf, maxlen); diff --git a/openair2/X2AP/x2ap_common.c b/openair2/X2AP/x2ap_common.c index a687b90666..0fc63b7de5 100644 --- a/openair2/X2AP/x2ap_common.c +++ b/openair2/X2AP/x2ap_common.c @@ -47,7 +47,7 @@ ssize_t x2ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } @@ -79,7 +79,7 @@ ssize_t x2ap_generate_successfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } @@ -111,7 +111,7 @@ ssize_t x2ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)&pdu); } diff --git a/openair2/X2AP/x2ap_common.h b/openair2/X2AP/x2ap_common.h index 81880cb71c..36dfe24287 100644 --- a/openair2/X2AP/x2ap_common.h +++ b/openair2/X2AP/x2ap_common.h @@ -49,8 +49,6 @@ # define X2AP_PORT 36422 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #define X2AP_INFO(x, args...) LOG_I(X2AP, x, ##args) #define X2AP_ERROR(x, args...) LOG_E(X2AP, x, ##args) diff --git a/openair2/X2AP/x2ap_eNB_decoder.c b/openair2/X2AP/x2ap_eNB_decoder.c index 6d7897bf14..81cb77dfef 100644 --- a/openair2/X2AP/x2ap_eNB_decoder.c +++ b/openair2/X2AP/x2ap_eNB_decoder.c @@ -156,7 +156,7 @@ int x2ap_eNB_decode_pdu(X2AP_X2AP_PDU_t *pdu, const uint8_t *const buffer, uint3 length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, pdu); } diff --git a/openair2/X2AP/x2ap_eNB_encoder.c b/openair2/X2AP/x2ap_eNB_encoder.c index 8b1c030d35..a82eb03f9a 100644 --- a/openair2/X2AP/x2ap_eNB_encoder.c +++ b/openair2/X2AP/x2ap_eNB_encoder.c @@ -44,7 +44,7 @@ int x2ap_eNB_encode_pdu(X2AP_X2AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_X2AP_X2AP_PDU, (void *)pdu); } diff --git a/openair3/M3AP/m3ap_common.c b/openair3/M3AP/m3ap_common.c index e43275d96c..669d7eaa5e 100644 --- a/openair3/M3AP/m3ap_common.c +++ b/openair3/M3AP/m3ap_common.c @@ -47,7 +47,7 @@ ssize_t m3ap_generate_initiating_message( pdu.choice.initiatingMessage.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.initiatingMessage.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)&pdu); } @@ -80,7 +80,7 @@ ssize_t m3ap_generate_unsuccessfull_outcome( pdu.choice.successfulOutcome.criticality = criticality; ANY_fromType_aper((ANY_t *)&pdu.choice.successfulOutcome.value, td, sptr); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)&pdu); } diff --git a/openair3/M3AP/m3ap_common.h b/openair3/M3AP/m3ap_common.h index 7c8ea18a5d..f5acb6bdf5 100644 --- a/openair3/M3AP/m3ap_common.h +++ b/openair3/M3AP/m3ap_common.h @@ -48,8 +48,6 @@ # define M3AP_PORT 36444 #endif -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "m3ap_default_values.h" #define M3AP_INFO(x, args...) LOG_I(M3AP, x, ##args) diff --git a/openair3/M3AP/m3ap_decoder.c b/openair3/M3AP/m3ap_decoder.c index cee0a5ff41..36c8f64fae 100644 --- a/openair3/M3AP/m3ap_decoder.c +++ b/openair3/M3AP/m3ap_decoder.c @@ -188,7 +188,7 @@ int m3ap_decode_pdu(M3AP_M3AP_PDU_t *pdu, const uint8_t *const buffer, uint32_t length, 0, 0); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, pdu); } diff --git a/openair3/M3AP/m3ap_encoder.c b/openair3/M3AP/m3ap_encoder.c index 2e1cbf80af..86a710ca90 100644 --- a/openair3/M3AP/m3ap_encoder.c +++ b/openair3/M3AP/m3ap_encoder.c @@ -44,7 +44,7 @@ int m3ap_encode_pdu(M3AP_M3AP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_M3AP_M3AP_PDU, (void *)pdu); } diff --git a/openair3/NAS/UE/API/USIM/aka_functions.c b/openair3/NAS/UE/API/USIM/aka_functions.c index d2e3fc36c5..1f461c3bbf 100644 --- a/openair3/NAS/UE/API/USIM/aka_functions.c +++ b/openair3/NAS/UE/API/USIM/aka_functions.c @@ -303,7 +303,7 @@ void ComputeOPc(const u8 op[16], u8 op_c_pP[16]) return; } /* end of function ComputeOPc */ /*-------------------- Rijndael round subkeys ---------------------*/ -u8 roundKeys[11][4][4]; +static u8 roundKeys[11][4][4]; /*--------------------- Rijndael S box table ----------------------*/ const u8 S[256] = { 99, 124, 119, 123, 242, 107, 111, 197, 48, 1, 103, 43, 254, 215, 171, 118, 202, 130, 201, 125, 250, 89, 71, 240, diff --git a/openair3/NGAP/ngap_common.c b/openair3/NGAP/ngap_common.c index 7b960fed0b..82c2ccaf2a 100644 --- a/openair3/NGAP/ngap_common.c +++ b/openair3/NGAP/ngap_common.c @@ -31,8 +31,6 @@ #include "conversions.h" #include "ngap_common.h" -int asn1_xer_print = 0; - void encode_ngap_cause(NGAP_Cause_t *out, const ngap_cause_t *in) { switch (in->type) { diff --git a/openair3/NGAP/ngap_common.h b/openair3/NGAP/ngap_common.h index 82a8fe9234..dd7fdebc54 100644 --- a/openair3/NGAP/ngap_common.h +++ b/openair3/NGAP/ngap_common.h @@ -48,9 +48,6 @@ #define NGAP_UE_ID_FMT "0x%06"PRIX32 -extern int asn_debug; -extern int asn1_xer_print; - # include "common/utils/LOG/log.h" # include "ngap_gNB_default_values.h" # define NGAP_ERROR(x, args...) LOG_E(NGAP, x, ##args) diff --git a/openair3/NGAP/ngap_gNB_encoder.c b/openair3/NGAP/ngap_gNB_encoder.c index ee3b37e07d..c3c095541d 100644 --- a/openair3/NGAP/ngap_gNB_encoder.c +++ b/openair3/NGAP/ngap_gNB_encoder.c @@ -115,7 +115,7 @@ int ngap_gNB_encode_pdu(NGAP_NGAP_PDU_t *pdu, uint8_t **buffer, uint32_t *len) DevAssert(pdu != NULL); DevAssert(buffer != NULL); DevAssert(len != NULL); - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, (void *)pdu); } switch (pdu->present) { diff --git a/openair3/NGAP/ngap_gNB_nas_procedures.c b/openair3/NGAP/ngap_gNB_nas_procedures.c index 1aafeca8c9..b291f6b96c 100644 --- a/openair3/NGAP/ngap_gNB_nas_procedures.c +++ b/openair3/NGAP/ngap_gNB_nas_procedures.c @@ -690,7 +690,7 @@ int ngap_gNB_initial_ctxt_resp(instance_t instance, ngap_initial_context_setup_r // ie->value.choice.CriticalityDiagnostics =; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu); } @@ -772,7 +772,7 @@ int ngap_gNB_initial_ctxt_fail(instance_t instance, ngap_initial_context_setup_f ie->value.present = NGAP_InitialContextSetupFailureIEs__value_PR_Cause; encode_ngap_cause(&ie->value.choice.Cause, &initial_ctxt_fail->cause); } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_NGAP_NGAP_PDU, &pdu); } if (ngap_gNB_encode_pdu(&pdu, &buffer, &length) < 0) { diff --git a/openair3/S1AP/s1ap_common.h b/openair3/S1AP/s1ap_common.h index 797bc7be8a..7ef7e589ca 100644 --- a/openair3/S1AP/s1ap_common.h +++ b/openair3/S1AP/s1ap_common.h @@ -52,8 +52,6 @@ #define S1AP_UE_ID_FMT "0x%06"PRIX32 -extern int asn1_xer_print; - #include "common/utils/LOG/log.h" #include "s1ap_eNB_default_values.h" #define S1AP_ERROR(x, args...) LOG_E(S1AP, x, ##args) @@ -89,10 +87,4 @@ extern int asn1_xer_print; **/ typedef int (*s1ap_message_decoded_callback)(sctp_assoc_t assoc_id, uint32_t stream, S1AP_S1AP_PDU_t *pdu); -/** \brief Handle criticality - \param criticality Criticality of the IE - @returns void - **/ -void s1ap_handle_criticality(S1AP_Criticality_t criticality); -/** @}*/ #endif /* S1AP_COMMON_H_ */ diff --git a/openair3/S1AP/s1ap_eNB_encoder.c b/openair3/S1AP/s1ap_eNB_encoder.c index 8317f02731..6ff837a7f1 100644 --- a/openair3/S1AP/s1ap_eNB_encoder.c +++ b/openair3/S1AP/s1ap_eNB_encoder.c @@ -129,7 +129,7 @@ int s1ap_eNB_encode_initiating(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } @@ -183,7 +183,7 @@ int s1ap_eNB_encode_successfull_outcome(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } @@ -214,7 +214,7 @@ int s1ap_eNB_encode_unsuccessfull_outcome(S1AP_S1AP_PDU_t *pdu, return -1; } - if (asn1_xer_print) { + if (LOG_DEBUGFLAG(DEBUG_ASN1)) { xer_fprint(stdout, &asn_DEF_S1AP_S1AP_PDU, (void *)pdu); } diff --git a/openair3/TEST/test_s1ap.c b/openair3/TEST/test_s1ap.c index 8e61c338ac..1ecb30d476 100644 --- a/openair3/TEST/test_s1ap.c +++ b/openair3/TEST/test_s1ap.c @@ -154,10 +154,6 @@ void doit (void) { int i; break_on_error = 1; - - // asn1_xer_print = 1; - // asn_debug = 1; - for (i = 0; i < sizeof(s1ap_test) / sizeof(s1ap_test_t); i++) { S1AP_S1AP_PDU_t pdu; uint8_t *buffer; diff --git a/openair3/ocp-gtpu/gtp_itf.cpp b/openair3/ocp-gtpu/gtp_itf.cpp index 9e07a44747..9be1873612 100644 --- a/openair3/ocp-gtpu/gtp_itf.cpp +++ b/openair3/ocp-gtpu/gtp_itf.cpp @@ -21,6 +21,7 @@ extern "C" { #include #include #include "openair2/SDAP/nr_sdap/nr_sdap.h" +#include "openair3/ocp-gtpu/gtpu_extensions.h" #include "sim.h" #pragma pack(1) @@ -112,6 +113,7 @@ typedef struct gtpv1u_bearer_s { teid_t teid_outgoing; uint16_t seqNum; uint8_t npduNum; + int32_t nru_sequence_number; int outgoing_qfi; } gtpv1u_bearer_t; @@ -230,11 +232,9 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, int msgLen, bool seqNumFlag, bool npduNumFlag, - int extHdrType, - uint8_t *extensionHeader_buffer, - uint8_t extensionHeader_length) + gtpu_extension_header_t *extensions, + int extensions_count) { - DevAssert(msgLen + HDR_MAX < 65536); // maximum size of UDP packet uint8_t buffer[msgLen + HDR_MAX]; uint8_t *curPtr = buffer; @@ -242,7 +242,7 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, // N should be 0 for us (it was used only in 2G and 3G) msgHdr->PN = npduNumFlag; msgHdr->S = seqNumFlag; - msgHdr->E = extHdrType != NO_MORE_EXT_HDRS; + msgHdr->E = extensions_count != 0; msgHdr->spare = 0; // PT=0 is for GTP' TS 32.295 (charging) msgHdr->PT = 1; @@ -252,32 +252,32 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, curPtr += sizeof(Gtpv1uMsgHeaderT); - if (seqNumFlag || (extHdrType != NO_MORE_EXT_HDRS) || npduNumFlag) { + if (msgHdr->PN || msgHdr->S || msgHdr->E) { *(uint16_t *)curPtr = seqNumFlag ? bearer->seqNum : 0x0000; curPtr += sizeof(uint16_t); *(uint8_t *)curPtr = npduNumFlag ? bearer->npduNum : 0x00; curPtr++; - *(uint8_t *)curPtr = extHdrType; + *curPtr = extensions_count ? serialize_gtpu_extension_type(extensions[0].type) : 0; curPtr++; } - // Bug: if there is more than one extension, infinite loop on extensionHeader_buffer - while (extHdrType != NO_MORE_EXT_HDRS) { - if (extensionHeader_length > 0) { - memcpy(curPtr, extensionHeader_buffer, extensionHeader_length); - curPtr += extensionHeader_length; - LOG_D(GTPU, - "Extension Header for DDD added. The length is: %d, extension header type is: %x \n", - extensionHeader_length, - *((uint8_t *)(buffer + 11))); - extHdrType = extensionHeader_buffer[extensionHeader_length - 1]; - LOG_D(GTPU, "Next extension header type is: %x \n", *((uint8_t *)(buffer + 11))); - } else { - LOG_W(GTPU, "Extension header type not supported, returning... \n"); + for (int i = 0; i < extensions_count; i++) { + int available_size = sizeof(buffer) - (curPtr - buffer); + gtpu_extension_header_type_t next = i == extensions_count - 1 ? GTPU_EXT_NONE : extensions[i + 1].type; + int len = serialize_extension(&extensions[i], next, curPtr, available_size); + if (len == -1) { + LOG_E(GTPU, "GTP extension serialization: buffer too small\n"); + return GTPNOK; } + curPtr += len; } if (Msg != NULL) { + int available_size = sizeof(buffer) - (curPtr - buffer); + if (msgLen > available_size) { + LOG_E(GTPU, "GTP message creation: buffer too small\n"); + return GTPNOK; + } memcpy(curPtr, Msg, msgLen); curPtr += msgLen; } @@ -307,13 +307,14 @@ static int gtpv1uCreateAndSendMsg(gtpv1u_bearer_t *bearer, return !GTPNOK; } -void gtpv1uSendDirect(instance_t instance, - ue_id_t ue_id, - int bearer_id, - uint8_t *buf, - size_t len, - bool seqNumFlag, - bool npduNumFlag) +static void _gtpv1uSendDirect(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len, + bool seqNumFlag, + bool npduNumFlag, + int32_t nru_seqnum) { pthread_mutex_lock(&globGtp.gtp_lock); getInstRetVoid(compatInst(instance)); @@ -347,18 +348,44 @@ void gtpv1uSendDirect(instance_t instance, gtpv1u_bearer_t bearer = ptr2->second; pthread_mutex_unlock(&globGtp.gtp_lock); - Gtpv1uExtHeaderT *opt_ext = NULL; - Gtpv1uExtHeaderT ext = {0}; + int extension_count = 0; + gtpu_extension_header_t ext[2]; if (bearer.outgoing_qfi != -1) { - opt_ext = &ext; - ext.ExtHeaderLen = 1; // in quad bytes EXT_HDR_LNTH_OCTET_UNITS - ext.pdusession_cntr.spare = 0; - ext.pdusession_cntr.PDU_type = UL_PDU_SESSION_INFORMATION; - ext.pdusession_cntr.QFI = bearer.outgoing_qfi; - ext.pdusession_cntr.Reflective_QoS_activation = false; - ext.pdusession_cntr.Paging_Policy_Indicator = false; - ext.NextExtHeaderType = NO_MORE_EXT_HDRS; + /* 29.281 Figure 5.2.1-3 note 4 says PDU Session Container must come first. + * GTPU_EXT_UL_PDU_SESSION_INFORMATION is within a PDU Session Container + * so it must be put before any other extension. + */ + ext[extension_count] = { + .type = GTPU_EXT_UL_PDU_SESSION_INFORMATION, + .ul_pdu_session_information = { + .qmp = false, + .dl_delay_ind = false, + .ul_delay_ind = false, + .snp = false, + .n3n9_delay_ind = false, + .new_ie_flag = false, + .qfi = bearer.outgoing_qfi + } + }; + extension_count++; + } + if (nru_seqnum != -1) { + ext[extension_count] = { + .type = GTPU_EXT_DL_USER_DATA, + .dl_user_data = { + .dl_discard_blocks = false, + .dl_flush = false, + .report_polling = false, + .request_out_of_seq_report = false, + .report_delivered = false, + .user_data_existence_flag = false, + .assistance_info_report_polling_flag = false, + .retransmission_flag = false, + .nru_sequence_number = (uint32_t)nru_seqnum + } + }; + extension_count++; } DevAssert(compatInst(instance) == bearer.sock_fd); @@ -368,9 +395,81 @@ void gtpv1uSendDirect(instance_t instance, len, seqNumFlag, npduNumFlag, - opt_ext ? PDU_SESSION_CONTAINER : NO_MORE_EXT_HDRS, - (uint8_t *)opt_ext, - sizeof(*opt_ext)); + ext, + extension_count); +} + +void gtpv1uSendDirect(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len, + bool seqNumFlag, + bool npduNumFlag) +{ + _gtpv1uSendDirect(instance, + ue_id, + bearer_id, + buf, + len, + seqNumFlag, + npduNumFlag, + -1); +} + +void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len) +{ + pthread_mutex_lock(&globGtp.gtp_lock); + getInstRetVoid(compatInst(instance)); + getUeRetVoid(inst, ue_id); + auto ptr2 = ptrUe->second.bearers.find(bearer_id); + + if (ptr2 == ptrUe->second.bearers.end()) { + LOG_E(GTPU, "[%ld] GTP-U instance: sending a packet to a non existant UE:RAB: %lx/%x\n", instance, ue_id, bearer_id); + pthread_mutex_unlock(&globGtp.gtp_lock); + return; + } + + int32_t nru_seqnum = ptr2->second.nru_sequence_number; + ptr2->second.nru_sequence_number++; + ptr2->second.nru_sequence_number &= (1 << 24) - 1; + + pthread_mutex_unlock(&globGtp.gtp_lock); + + _gtpv1uSendDirect(instance, + ue_id, + bearer_id, + buf, + len, + false, + false, + nru_seqnum); +} + +static void fillDlDeliveryStatusReport(gtpu_extension_header_t *ext, uint32_t RLC_buffer_availability, uint32_t NR_PDCP_PDU_SN) +{ + *ext = { + .type = GTPU_EXT_DL_DATA_DELIVERY_STATUS, + .dl_data_delivery_status = { + /* previous version of the code was sending highest_transmitted_nr_pdcp_sn if + * it is != 0, let's do the same for the moment */ + .highest_transmitted_nr_pdcp_sn_ind = NR_PDCP_PDU_SN != 0, + .highest_delivered_nr_pdcp_sn_ind = false, + .final_frame_ind = false, + .lost_packet_report = false, + .delivered_nr_pdcp_sn_range_ind = false, + .data_rate_ind = false, + .retransmitted_nr_pdcp_sn_ind = false, + .delivered_retransmitted_nr_pdcp_ind = false, + .cause_report = false, + .desired_buffer_size = RLC_buffer_availability, + .highest_transmitted_nr_pdcp_sn = NR_PDCP_PDU_SN + } + }; } static void gtpv1uEndTunnel(instance_t instance, gtpv1u_enb_end_marker_req_t *req) @@ -950,7 +1049,6 @@ static int Gtpv1uHandleEchoReq(int h, uint8_t *msgBuf, uint32_t msgBufLen, const sizeof recovery, true, false, - NO_MORE_EXT_HDRS, NULL, 0); } @@ -1168,37 +1266,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st LOG_D(GTPU, "Create and send DL DATA Delivery status for the previously received PDU, NR_PDCP_PDU_SN: %u \n", NR_PDCP_PDU_SN); int rlc_tx_buffer_space = nr_rlc_get_available_tx_space(ctxt.rntiMaybeUEid, rb_id + 3); LOG_D(GTPU, "Available buffer size in RLC for Tx: %d \n", rlc_tx_buffer_space); - /*Total size of DDD_status PDU = 1 octet to report extension header length - * size of mandatory part + 3 octets for highest transmitted/delivered PDCP SN - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader_t *extensionHeader; - extensionHeader = (extensionHeader_t *)calloc(1, sizeof(extensionHeader_t)); - extensionHeader->buffer[0] = (1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1) / 4; - DlDataDeliveryStatus_flagsT DlDataDeliveryStatus; - DlDataDeliveryStatus.deliveredPdcpSn = 0; - DlDataDeliveryStatus.transmittedPdcpSn = 1; - DlDataDeliveryStatus.pduType = 1; - DlDataDeliveryStatus.drbBufferSize = - htonl(rlc_tx_buffer_space); // htonl(10000000); //hardcoded for now but normally we should extract it from RLC - memcpy(extensionHeader->buffer + 1, &DlDataDeliveryStatus, sizeof(DlDataDeliveryStatus_flagsT)); - uint8_t offset = sizeof(DlDataDeliveryStatus_flagsT) + 1; - - extensionHeader->buffer[offset] = (NR_PDCP_PDU_SN >> 16) & 0xff; - extensionHeader->buffer[offset + 1] = (NR_PDCP_PDU_SN >> 8) & 0xff; - extensionHeader->buffer[offset + 2] = NR_PDCP_PDU_SN & 0xff; - LOG_D(GTPU, - "Octets reporting NR_PDCP_PDU_SN, extensionHeader-> %u:%u:%u \n", - extensionHeader->buffer[offset], - extensionHeader->buffer[offset + 1], - extensionHeader->buffer[offset + 2]); - extensionHeader->buffer[offset + 3] = 0x00; // Padding octet - extensionHeader->buffer[offset + 4] = 0x00; // No more extension headers - /*Total size of DDD_status PDU = size of mandatory part + - * 3 octets for highest transmitted/delivered PDCP SN + - * 1 octet for padding + 1 octet for next extension header type, - * according to TS 38.425: Fig. 5.5.2.2-1 and section 5.5.3.24*/ - extensionHeader->length = 1 + sizeof(DlDataDeliveryStatus_flagsT) + 3 + 1 + 1; + gtpu_extension_header_t ext; + fillDlDeliveryStatusReport(&ext, rlc_tx_buffer_space, NR_PDCP_PDU_SN); uint32_t teid = globGtp.te2ue_mapping[ntohl(msgHdr->teid)].outgoing_teid; gtpv1u_bearer_t bearer = create_bearer(h, addr, teid, 0); gtpv1uCreateAndSendMsg(&bearer, @@ -1207,9 +1276,8 @@ static int Gtpv1uHandleGpdu(int h, uint8_t *msgBuf, uint32_t msgBufLen, const st 0, false, false, - NR_RAN_CONTAINER, - extensionHeader->buffer, - extensionHeader->length); + &ext, + 1); } LOG_D(GTPU, "[%d] Received a %d bytes packet for: teid:%x\n", h, msgBufLen - offset, ntohl(msgHdr->teid)); diff --git a/openair3/ocp-gtpu/gtp_itf.h b/openair3/ocp-gtpu/gtp_itf.h index d341acb311..4fcb8190e4 100644 --- a/openair3/ocp-gtpu/gtp_itf.h +++ b/openair3/ocp-gtpu/gtp_itf.h @@ -128,6 +128,11 @@ typedef struct gtpv1u_gnb_delete_tunnel_req_s gtpv1u_gnb_delete_tunnel_req_t; int newGtpuDeleteTunnels(instance_t instance, ue_id_t ue_id, int nbTunnels, int *pdusession_id); void gtpv1uSendDirect(instance_t instance, ue_id_t ue_id, int bearer_id, uint8_t *buf, size_t len, bool seqNumFlag, bool npduNumFlag); + void gtpv1uSendDirectWithNRUSeqNum(instance_t instance, + ue_id_t ue_id, + int bearer_id, + uint8_t *buf, + size_t len); instance_t gtpv1Init(openAddr_t context); void *gtpv1uTask(void *args); diff --git a/openair3/ocp-gtpu/gtpu_extensions.c b/openair3/ocp-gtpu/gtpu_extensions.c new file mode 100644 index 0000000000..13f0cee053 --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.c @@ -0,0 +1,167 @@ +/* + * 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 "gtpu_extensions.h" +#include "common/utils/assertions.h" +#include "common/utils/ds/byte_array_producer.h" +#include "LOG/log.h" + +/* 29.281 Figure 5.2.1-3 */ +#define NR_RAN_CONTAINER 0x84 +#define PDU_SESSION_CONTAINER 0x85 + +/* from an extension type, returns its "extension header type" + * as defined in 29.281 Figure 5.2.1-3 + */ +int serialize_gtpu_extension_type(gtpu_extension_header_type_t type) +{ + switch (type) { + case GTPU_EXT_NONE: + /* 0, no more extension */ + return 0; + case GTPU_EXT_UL_PDU_SESSION_INFORMATION: + return PDU_SESSION_CONTAINER; + case GTPU_EXT_DL_DATA_DELIVERY_STATUS: + case GTPU_EXT_DL_USER_DATA: + return NR_RAN_CONTAINER; + default: + AssertFatal(0, "unknown GTPU extension type %d\n", type); + } +} + +/* returns 0 on error, 1 on success */ +static int serialize_ul_pdu_session_information(byte_array_producer_t *b, ul_pdu_session_information_t *ext) +{ + /* see 38.415 5.5.2.2 */ + AssertFatal(!ext->qmp && !ext->dl_delay_ind && !ext->ul_delay_ind + && !ext->snp && !ext->n3n9_delay_ind && !ext->new_ie_flag, + "todo\n"); + + uint8_t b1 = (1 << 4) | (ext->qmp << 3) | (ext->dl_delay_ind << 2) | (ext->ul_delay_ind << 1) | ext->snp; + uint8_t b2 = (ext->n3n9_delay_ind << 7) | (ext->new_ie_flag << 6) | ext->qfi; + return byte_array_producer_put_byte(b, b1) && byte_array_producer_put_byte(b, b2); +} + +/* returns 0 on error, 1 on success */ +static int serialize_dl_data_delivery_status(byte_array_producer_t *b, dl_data_delivery_status_t *ext) +{ + /* see 38.425 5.5.2.2 */ + AssertFatal(!ext->highest_delivered_nr_pdcp_sn_ind && !ext->final_frame_ind + && !ext->lost_packet_report && !ext->delivered_nr_pdcp_sn_range_ind + && !ext->data_rate_ind && !ext->retransmitted_nr_pdcp_sn_ind + && !ext->delivered_retransmitted_nr_pdcp_ind && !ext->cause_report, + "todo\n"); + + uint8_t b1 = (1 << 4) + | (ext->highest_transmitted_nr_pdcp_sn_ind << 3) + | (ext->highest_delivered_nr_pdcp_sn_ind << 2) + | (ext->final_frame_ind << 1) + | ext->lost_packet_report; + uint8_t b2 = (ext->delivered_nr_pdcp_sn_range_ind << 4) + | (ext->data_rate_ind << 3) + | (ext->retransmitted_nr_pdcp_sn_ind << 2) + | (ext->delivered_retransmitted_nr_pdcp_ind << 1) + | ext->cause_report; + if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2)) + return 0; + + if (!byte_array_producer_put_u32_be(b, ext->desired_buffer_size)) + return 0; + + if (ext->highest_transmitted_nr_pdcp_sn_ind) + if (!byte_array_producer_put_u24_be(b, ext->highest_transmitted_nr_pdcp_sn)) + return 0; + + return 1; +} + +/* returns 0 on error, 1 on success */ +static int serialize_dl_user_data(byte_array_producer_t *b, dl_user_data_t *ext) +{ + /* see 38.425 5.5.2.1 */ + AssertFatal(!ext->dl_discard_blocks && !ext->dl_flush && !ext->report_polling + && !ext->request_out_of_seq_report && !ext->report_delivered + && !ext->user_data_existence_flag && !ext->assistance_info_report_polling_flag + && !ext->retransmission_flag, + "todo\n"); + + uint8_t b1 = (0 << 4) | (ext->dl_discard_blocks << 2) | (ext->dl_flush << 1) | ext->report_polling; + uint8_t b2 = (ext->request_out_of_seq_report << 4) + | (ext->report_delivered << 3) + | (ext->user_data_existence_flag << 2) + | (ext->assistance_info_report_polling_flag << 1) + | ext->retransmission_flag; + if (!byte_array_producer_put_byte(b, b1) || !byte_array_producer_put_byte(b, b2)) + return 0; + + if (!byte_array_producer_put_u24_be(b, ext->nru_sequence_number)) + return 0; + + return 1; +} + +/* returns -1 on error, number of serialized bytes on success */ +int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len) +{ + byte_array_producer_t b; + + b = byte_array_producer_from_buffer(out_buf, out_len); + /* length - will be set later */ + if (!byte_array_producer_put_byte(&b, 0)) + goto error; + + switch (ext->type) { + case GTPU_EXT_UL_PDU_SESSION_INFORMATION: + if (!serialize_ul_pdu_session_information(&b, &ext->ul_pdu_session_information)) + goto error; + break; + case GTPU_EXT_DL_DATA_DELIVERY_STATUS: + if (!serialize_dl_data_delivery_status(&b, &ext->dl_data_delivery_status)) + goto error; + break; + case GTPU_EXT_DL_USER_DATA: + if (!serialize_dl_user_data(&b, &ext->dl_user_data)) + goto error; + break; + default: + LOG_E(GTPU, "unknown extension type %d\n", ext->type); + return -1; + } + + /* padding */ + while ((b.pos & 3) != 3) + if (!byte_array_producer_put_byte(&b, 0)) + goto error; + + /* next */ + if (!byte_array_producer_put_byte(&b, serialize_gtpu_extension_type(next))) + goto error; + + /* length is now know */ + DevAssert(b.pos / 4 <= 255); + out_buf[0] = b.pos / 4; + + return b.pos; + +error: + LOG_E(GTPU, "error serializing extension\n"); + return -1; +} diff --git a/openair3/ocp-gtpu/gtpu_extensions.h b/openair3/ocp-gtpu/gtpu_extensions.h new file mode 100644 index 0000000000..b621d27e14 --- /dev/null +++ b/openair3/ocp-gtpu/gtpu_extensions.h @@ -0,0 +1,91 @@ +/* + * 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 GTPU_EXTENSIONS_H +#define GTPU_EXTENSIONS_H + +#include +#include + +typedef enum { + GTPU_EXT_NONE, + /* 38.415 */ + GTPU_EXT_UL_PDU_SESSION_INFORMATION, + /* 38.425 */ + GTPU_EXT_DL_DATA_DELIVERY_STATUS, + GTPU_EXT_DL_USER_DATA, +} gtpu_extension_header_type_t; + +/* 38.415 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool qmp; + bool dl_delay_ind; + bool ul_delay_ind; + bool snp; + bool n3n9_delay_ind; + bool new_ie_flag; + int qfi; +} ul_pdu_session_information_t; + +/* 38.425 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool highest_transmitted_nr_pdcp_sn_ind; + bool highest_delivered_nr_pdcp_sn_ind; + bool final_frame_ind; + bool lost_packet_report; + bool delivered_nr_pdcp_sn_range_ind; + bool data_rate_ind; + bool retransmitted_nr_pdcp_sn_ind; + bool delivered_retransmitted_nr_pdcp_ind; + bool cause_report; + uint32_t desired_buffer_size; + uint32_t highest_transmitted_nr_pdcp_sn; +} dl_data_delivery_status_t; + +/* 38.425 */ +typedef struct { + /* not all fields are present, to be refined if needed */ + bool dl_discard_blocks; + bool dl_flush; + bool report_polling; + bool request_out_of_seq_report; + bool report_delivered; + bool user_data_existence_flag; + bool assistance_info_report_polling_flag; + bool retransmission_flag; + uint32_t nru_sequence_number; +} dl_user_data_t; + +typedef struct { + gtpu_extension_header_type_t type; + union { + ul_pdu_session_information_t ul_pdu_session_information; + dl_data_delivery_status_t dl_data_delivery_status; + dl_user_data_t dl_user_data; + }; +} gtpu_extension_header_t; + +int serialize_gtpu_extension_type(gtpu_extension_header_type_t type); +int serialize_extension(gtpu_extension_header_t *ext, gtpu_extension_header_type_t next, uint8_t *out_buf, int out_len); + +#endif /* GTPU_EXTENSIONS_H */ diff --git a/radio/CMakeLists.txt b/radio/CMakeLists.txt index 654fb037f0..1159416afa 100644 --- a/radio/CMakeLists.txt +++ b/radio/CMakeLists.txt @@ -38,7 +38,12 @@ if(OAI_USRP) add_subdirectory(USRP) endif() -add_boolean_option(VRTSIM_RADIO ON "Activate OAI's shared memory radio driver" OFF) -if(VRTSIM_RADIO) +add_boolean_option(OAI_VRTSIM ON "Activate OAI's shared memory radio driver" OFF) +if(OAI_VRTSIM) add_subdirectory(vrtsim) endif() + +add_boolean_option(OAI_RF_EMULATOR ON "Activate OAI's RF emulator" OFF) +if(OAI_RF_EMULATOR) + add_subdirectory(emulator) +endif() diff --git a/radio/COMMON/common_lib.h b/radio/COMMON/common_lib.h index 86d09b00fb..f928d554a8 100644 --- a/radio/COMMON/common_lib.h +++ b/radio/COMMON/common_lib.h @@ -204,8 +204,6 @@ typedef struct split7_config { typedef struct openair0_config { //! Module ID for this configuration int Mod_id; - //! device log level - int log_level; //! duplexing mode duplex_mode_t duplex_mode; //! number of downlink resource blocks @@ -292,8 +290,6 @@ typedef struct openair0_config { double rx_sample_rate; //! the sample rate for transmit. double tx_sample_rate; - //! check for threequarter sampling rate - int8_t threequarter_fs; //! Flag to indicate this configuration is for NR int nr_flag; //! NR band number diff --git a/radio/ETHERNET/ethernet_lib.c b/radio/ETHERNET/ethernet_lib.c index eb23ef28cc..2a4d021afc 100644 --- a/radio/ETHERNET/ethernet_lib.c +++ b/radio/ETHERNET/ethernet_lib.c @@ -617,7 +617,6 @@ void dump_dev(openair0_device *device) eth_state_t *eth = (eth_state_t*)device->priv; printf("Ethernet device interface %i configuration:\n" ,device->openair0_cfg->Mod_id); - printf(" Log level is %i :\n" ,device->openair0_cfg->log_level); printf(" RB number: %i, sample rate: %lf \n" , device->openair0_cfg->num_rb_dl, device->openair0_cfg->sample_rate); printf(" RAU configured for %i tx/%i rx channels)\n", diff --git a/radio/emulator/CMakeLists.txt b/radio/emulator/CMakeLists.txt new file mode 100644 index 0000000000..42b196ee95 --- /dev/null +++ b/radio/emulator/CMakeLists.txt @@ -0,0 +1,4 @@ +add_library(rf_emulator MODULE rf_emulator.c) +set_target_properties(rf_emulator PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) +target_link_libraries(rf_emulator PRIVATE noise_device) +add_dependencies(rf_emulator generate_T) diff --git a/radio/emulator/README.md b/radio/emulator/README.md new file mode 100644 index 0000000000..b3b58ba6a7 --- /dev/null +++ b/radio/emulator/README.md @@ -0,0 +1,25 @@ +# RF emulator library + +The RF emulator is a simple network stack terminator on the RF side. +It is meant to run the network stack without real radio and without RF simulation. +It simply drops TX signal and generate noise as an RX signal. +It can be used in `phy-test` mode to simply benchmark the network stack in a determined scenario. +It synchronizes the network stack on the real time clock of the host to guarantee a realistic behavior of the network stack. +It detects and report late samples reads and writes to assess the real time behavior of the network stack. + +## Building + +`librf_emulator.so` is built by default by the `build_oai.sh` script. +It is then located in the root build directory (usually `cmake_targets/ran_build/build`). + +## Usage + +### Enabling `rf_emulator` + +Use the option `--device.name rf_emulator` of the softmodem to use the RF emulator. + +### Options + +Two options are available to configure the RF emulator: +* `--rf_emulator.enable_noise ` Enables (enable = 1) or disables (enable = 0) noise generation on RX. By default noise generation is enabled. +* `--rf_emulator.noise_level_dBFS ` Sets the level of noise generated in dB. If omitted no noise is generated. diff --git a/radio/emulator/rf_emulator.c b/radio/emulator/rf_emulator.c new file mode 100644 index 0000000000..861daab2ba --- /dev/null +++ b/radio/emulator/rf_emulator.c @@ -0,0 +1,369 @@ +/* + * 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 + */ + +/*! \file radio/emulator/rf_emulator.c + * \brief RF library that does nothing to be used in benchmarks with phy-test without RF + */ + +#include +#include + +#include +#include +#include "common/utils/threadPool/pthread_utils.h" +#include "common_lib.h" +#include "radio/vrtsim/noise_device.h" +#include "simde/x86/avx512.h" +#include "SIMULATION/TOOLS/sim.h" + +#define RF_EMULATOR_SECTION "rf_emulator" +// clang-format off +#define RF_EMULATOR_PARAMS_DESC \ + { \ + {"enable_noise", "Enable noise injection", 0, .iptr = &emulator_state->enable_noise, .defintval = 1, TYPE_INT, 0},\ + {"noise_level_dBFS", "Noise level", 0, .iptr = &emulator_state->noise_level_dBFS, .defintval = INVALID_DBFS_VALUE, TYPE_INT, 0},\ + }; +// clang-format on + +// structures and timing thread job for timing +typedef struct { + uint64_t timestamp; + pthread_mutex_t mutex; + pthread_cond_t cond; + uint64_t late_reads; + uint64_t late_writes; +} emulator_timestamp_t; + +typedef struct { + uint64_t last_received_sample; + double timescale; + double sample_rate; + emulator_timestamp_t emulator_timestamp; + pthread_t timing_thread; + bool run_timing_thread; + int enable_noise; + int noise_level_dBFS; +} emulator_state_t; + +static void *emulator_timing_job(void *arg) +{ + emulator_state_t *emulator_state = (emulator_state_t *)arg; + struct timespec timestamp; + if (clock_gettime(CLOCK_REALTIME, ×tamp)) { + LOG_E(UTIL, "clock_gettime failed\n"); + exit(1); + } + double leftover_samples = 0; + while (emulator_state->run_timing_thread) { + struct timespec current_time; + if (clock_gettime(CLOCK_REALTIME, ¤t_time)) { + LOG_E(UTIL, "clock_gettime failed\n"); + exit(1); + } + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + if (current_time.tv_sec != timestamp.tv_sec) { + if (emulator_timestamp->late_reads != 0) { + LOG_W(HW, "%ld RF reads were late\n", emulator_timestamp->late_reads); + } + emulator_timestamp->late_reads = 0; + if (emulator_timestamp->late_writes != 0) { + LOG_W(HW, "%ld RF writes were late\n", emulator_timestamp->late_writes); + } + emulator_timestamp->late_writes = 0; + } + + uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec); + timestamp = current_time; + double samples_to_produce = emulator_state->sample_rate * emulator_state->timescale * diff / 1e9; + + // Attempt to correct compounding rounding error + leftover_samples += samples_to_produce - (uint64_t)samples_to_produce; + if (leftover_samples > 1.0f) { + samples_to_produce += 1; + leftover_samples -= 1; + } + + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->timestamp += samples_to_produce; + condbroadcast(emulator_timestamp->cond); + mutexunlock(emulator_timestamp->mutex); + + usleep(1); + } + return 0; +} + +/*! \brief Called to start the RF transceiver. Return 0 if OK, < 0 if error + * \param device pointer to the device structure specific to the RF hardware target + */ +static int emulator_start(openair0_device *device) +{ + // Start the timing thread and the noise device + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + if (!emulator_state->run_timing_thread) { + if (emulator_state->enable_noise > 0) { + float noise_level = emulator_state->noise_level_dBFS == INVALID_DBFS_VALUE + ? 0.0f + : (32767.0 / powf(10.0, .05 * -(emulator_state->noise_level_dBFS))); + init_noise_device(noise_level); + LOG_I(HW, "Using noise level with %d dBFS/%f\n", emulator_state->noise_level_dBFS, noise_level); + } + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + memset(emulator_timestamp, 0, sizeof(emulator_timestamp_t)); + + pthread_mutexattr_t mutex_attr; + pthread_condattr_t cond_attr; + int ret = pthread_mutexattr_init(&mutex_attr); + AssertFatal(ret == 0, "pthread_mutexattr_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_condattr_init(&cond_attr); + AssertFatal(ret == 0, "pthread_condattr_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED); + AssertFatal(ret == 0, "pthread_mutexattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED); + AssertFatal(ret == 0, "pthread_condattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_mutex_init(&emulator_timestamp->mutex, &mutex_attr); + AssertFatal(ret == 0, "pthread_mutex_init() failed: errno %d, %s\n", errno, strerror(errno)); + + ret = pthread_cond_init(&emulator_timestamp->cond, &cond_attr); + AssertFatal(ret == 0, "pthread_cond_init() failed: errno %d, %s\n", errno, strerror(errno)); + + emulator_state->run_timing_thread = true; + ret = pthread_create(&emulator_state->timing_thread, NULL, emulator_timing_job, emulator_state); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); + } + + return 0; +} + +/*! \brief print the RF statistics + * \param device pointer to the device structure specific to the RF hardware target + * \returns 0 on success + */ +int emulator_get_stats(openair0_device *device) +{ + return (0); +} + +/*! \brief Reset the RF statistics + * \param device pointer to the device structure specific to the RF hardware target + * \returns 0 on success + */ +int emulator_reset_stats(openair0_device *device) +{ + return (0); +} + +/*! \brief Terminate operation of the RF transceiver -- free all associated resources (if any) + * \param pointer to the device structure specific to the RF hardware target + */ +static void emulator_end(openair0_device *device) +{ + // Stop the timing thread and the noise device + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + if (emulator_state->run_timing_thread) { + emulator_state->run_timing_thread = false; + int ret = pthread_join(emulator_state->timing_thread, NULL); + AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); + if (emulator_state->enable_noise > 0) { + free_noise_device(); + } + } +} + +/*! \brief Stop RF + * \param device pointer to the device structure specific to the RF hardware target + */ +int emulator_stop(openair0_device *device) +{ + return (0); +} + +/*! \brief Set Gains (TX/RX) + * \param device pointer to the device structure specific to the RF hardware target + * \param openair0_cfg RF frontend parameters set by application + * \returns 0 in success + */ +int emulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg) +{ + return (0); +} + +/*! \brief Set frequencies (TX/RX). + * \param device pointer to the device structure specific to the RF hardware target + * \param openair0_cfg RF frontend parameters set by application + * \returns 0 in success + */ +int emulator_set_freq(openair0_device *device, openair0_config_t *openair0_cfg) +{ + return (0); +} + +int emulator_write_init(openair0_device *device) +{ + return (0); +} + +/*! \brief Called to send samples to the RF target + * \param device pointer to the device structure specific to the RF hardware target + * \param timestamp The timestamp at which the first sample MUST be sent + * \param buff Buffer which holds the samples + * \param nsamps number of samples to be sent + * \param nbAnt number of antennas + * \param flags flags must be set to true if timestamp parameter needs to be applied + */ +static int emulator_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int nbAnt, int flags) +{ + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + + // timestamp in the past + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + uint64_t current_timestamp = emulator_timestamp->timestamp; + if (timestamp < current_timestamp) { + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->late_writes++; + mutexunlock(emulator_timestamp->mutex); + } + + return 0; +} + +/*! \brief Store noise in the buffer buff + * \param[out] buff A buffer for received samples. + * The buffers must be large enough to hold the number of samples \ref nsamps. + * \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte. + */ +static void read_noise(void *buff, int nsamps) +{ + int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t)); + cf_t samples[aligned_nsamps] __attribute__((aligned(64))); + // Apply noise from global settings + get_noise_vector((float *)samples, nsamps * 2); + + // Convert to c16_t + c16_t samples_out[aligned_nsamps] __attribute__((aligned(64))); +#if defined(__AVX512F__) + for (int i = 0; i < aligned_nsamps / 8; i++) { + simde__m512 *in = (simde__m512 *)&samples[i * 8]; + simde__m256i *out = (simde__m256i *)&samples_out[i * 8]; + *out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in)); + } +#elif defined(__AVX2__) + for (int i = 0; i < aligned_nsamps / 4; i++) { + simde__m256 *in = (simde__m256 *)&samples[i * 4]; + simde__m128i *out = (simde__m128i *)&samples_out[i * 4]; + *out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in)); + } +#else + for (int i = 0; i < nsamps; i++) { + samples_out[i].r = lroundf(samples[i].r); + samples_out[i].i = lroundf(samples[i].i); + } +#endif + memcpy(buff, samples_out, nsamps * sizeof(c16_t)); +} + +/*! \brief Receive samples from hardware. + * Read \ref nsamps samples from each channel to buffers. buff[0] is the array for + * the first channel. + * \param[out] ptimestamp time at which the first sample was received. + * \param device pointer to the device structure specific to the RF hardware target + * \param[out] ptimestamp the time at which the first sample was received. + * \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of + * samples \ref nsamps. \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte. + * \param nbAnt number of antennas + * \returns the number of samples read + */ +static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int nbAnt) +{ + emulator_state_t *emulator_state = (emulator_state_t *)device->priv; + + emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp; + uint64_t timestamp = emulator_state->last_received_sample + nsamps; + uint64_t current_timestamp = emulator_timestamp->timestamp; + if (current_timestamp < timestamp) { + mutexlock(emulator_timestamp->mutex); + while (current_timestamp < timestamp) { + condwait(emulator_timestamp->cond, emulator_timestamp->mutex); + current_timestamp = emulator_timestamp->timestamp; + } + mutexunlock(emulator_timestamp->mutex); + } else { + mutexlock(emulator_timestamp->mutex); + emulator_timestamp->late_reads++; + mutexunlock(emulator_timestamp->mutex); + } + *ptimestamp = emulator_state->last_received_sample; + emulator_state->last_received_sample += nsamps; + + if (emulator_state->enable_noise > 0) { + for (int i = 0; i < nbAnt; i++) { + read_noise(buff[i], nsamps); + } + } else { + for (int i = 0; i < nbAnt; i++) { + memset(buff[i], 0, nsamps * sizeof(c16_t)); + } + } + + return nsamps; +} + +static void emulator_readconfig(emulator_state_t *emulator_state) +{ + paramdef_t emulator_params[] = RF_EMULATOR_PARAMS_DESC; + int ret = config_get(config_get_if(), emulator_params, sizeofArray(emulator_params), RF_EMULATOR_SECTION); + AssertFatal(ret >= 0, "configuration couldn't be performed\n"); +} + +int device_init(openair0_device *device, openair0_config_t *openair0_cfg) +{ + LOG_I(HW, "This is emulator RF that does nothing\n"); + + emulator_state_t *emulator_state = calloc_or_fail(1, sizeof(emulator_state_t)); + emulator_state->last_received_sample = 0; + emulator_state->timescale = 1.0; + emulator_state->sample_rate = openair0_cfg->sample_rate; + emulator_state->run_timing_thread = false; + + device->priv = emulator_state; + device->openair0_cfg = openair0_cfg; + device->trx_start_func = emulator_start; + device->trx_get_stats_func = emulator_get_stats; + device->trx_reset_stats_func = emulator_reset_stats; + device->trx_end_func = emulator_end; + device->trx_stop_func = emulator_stop; + device->trx_set_freq_func = emulator_set_freq; + device->trx_set_gains_func = emulator_set_gains; + device->trx_write_init = emulator_write_init; + device->type = RFSIMULATOR; + device->trx_write_func = emulator_write; + device->trx_read_func = emulator_read; + + emulator_readconfig(emulator_state); + + return 0; +} diff --git a/radio/fhi_72/oran-config.c b/radio/fhi_72/oran-config.c index 4eff24aacf..89c1838fa3 100644 --- a/radio/fhi_72/oran-config.c +++ b/radio/fhi_72/oran-config.c @@ -884,8 +884,6 @@ static bool set_fh_config(void *mplane_api, int ru_idx, int num_rus, enum xran_c for (int i = 1; i < oai0->rx_num_channels; ++i) DevAssert(oai0->rx_freq[0] == oai0->rx_freq[i]); DevAssert(oai0->nr_band > 0); - AssertFatal(oai0->threequarter_fs == 0, "cannot use three-quarter sampling with O-RAN 7.2 split\n"); - paramdef_t FHconfigs[] = ORAN_FH_DESC; paramlist_def_t FH_ConfigList = {CONFIG_STRING_ORAN_FH}; char aprefix[MAX_OPTNAME_SIZE] = {0}; diff --git a/radio/vrtsim/CMakeLists.txt b/radio/vrtsim/CMakeLists.txt index 79eb4d88da..c5ee458666 100644 --- a/radio/vrtsim/CMakeLists.txt +++ b/radio/vrtsim/CMakeLists.txt @@ -1,4 +1,31 @@ -add_library(vrtsim MODULE vrtsim.c noise_device.c) +add_library(noise_device STATIC noise_device.c) + +add_library(vrtsim MODULE vrtsim.c) set_target_properties(vrtsim PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}) -target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor) +target_link_libraries(vrtsim PRIVATE SIMU shm_td_iq_channel actor noise_device) add_dependencies(vrtsim generate_T) + +add_boolean_option(OAI_VRTSIM_TAPS_CLIENT OFF "Enable taps client" ON) +if (OAI_VRTSIM_TAPS_CLIENT) + target_link_libraries(vrtsim PRIVATE taps_client) + file(DOWNLOAD + "https://gitlab.eurecom.fr/oai/raytracing-channel-emulator/-/raw/59589ad2a534881810b2aa7dd81e63066245bb8d/server/api/taps.fbs?inline=false" + ${CMAKE_SOURCE_DIR}/taps.fbs + ) + + set(TAPS_IF_PATH ${CMAKE_SOURCE_DIR}/taps.fbs) + set(TAPS_API_HEADER taps_generated.h) + + add_custom_command(OUTPUT ${TAPS_API_HEADER} + COMMAND flatc -c ${TAPS_IF_PATH} + DEPENDS ${TAPS_IF_PATH} + COMMENT "Generating flatbuffers API from ${TAPS_IF_PATH}" + ) + add_library(taps_api INTERFACE) + target_include_directories(taps_api INTERFACE ${CMAKE_CURRENT_BINARY_DIR}) + + add_library(taps_client taps_client.cpp ${TAPS_API_HEADER}) + target_include_directories(taps_client PUBLIC .) + find_package(Flatbuffers REQUIRED) + target_link_libraries(taps_client PUBLIC flatbuffers taps_api SIMU nanomsg) +endif() diff --git a/radio/vrtsim/README.md b/radio/vrtsim/README.md index 71455175c3..2cd90e7886 100644 --- a/radio/vrtsim/README.md +++ b/radio/vrtsim/README.md @@ -4,12 +4,24 @@ This implements a shared memory realtime and near-realtime radio interface. This is performed using `shm_td_iq_channel` which handles the shared memory interface. +# Architecture + +The `vrtsim` architecture follows a server-client model: + +- **Server (gNB)**: On startup, the server writes a file, default location is + `/tmp/vrtsim_connection`. This file contains information required by the client + to establish communication. +- **Client (UE)**: The client reads this file from, verifies its contents, and uses + the provided information to open and connect to the shared memory channel. +- **Shared Memory Channel**: Both server and client use the `shm_td_iq_channel` to + exchange IQ samples and control data in real time or near-real time. + +## Channel modelling: + + # Limitations - Only 1gNB to 1nrUE: gNB as a server, UE as a client. - - Server only accepts connections during device bringup. This means that - restarting the client will not make it reconnect, the server has to be - restarted as well. # Usage @@ -21,5 +33,63 @@ is the default and means realtime. Channel modelling can be enabled by adding `vrtsim.chanmod 1` to the command line and should work the same as channel modelling in rfsimulator, -see rfsimulator [documentation](../rfsimulator/README.md), provided that your -CPU is fast enough. +see rfsimulator [documentation](../rfsimulator/README.md) with a slight difference: +channel modelling is done on transmission instead of reception. To refect this, +the model names are slightly different: the server expects a model named +`server_tx_channel_model` while the client expects a model named `client_tx_channel_model` + +Additionally, `taps_client` is available in vrtsim. This allows to connect to +channel emulation server and receive external taps (or channel impulse responses). +To use external taps, use `--taps-socket` and provide a nanomsg PUB socket address +that will publish the taps. + +# Debugging + +## Realtime issues + +Realtime issues can prevent the UE from connecting to the gNB, especially with +channel modelling enabled. Consider adjusting `timescale` parameter until +the UE can connect reliably. + +On exit, vrtsim will output a histogram of the measured transmission budget, which +is defined as the number of microseconds before the transmitted samples could be +read by peer. + +Example: + +``` +[HW] VRTSIM: Average TX budget 939.210 uS (more is better) +[HW] VRTSIM: TX budget histogram: 216 samples +[HW] Bin 0 [0.0 - 100.0uS]: 0 +[HW] Bin 1 [100.0 - 200.0uS]: 0 +[HW] Bin 2 [200.0 - 300.0uS]: 0 +[HW] Bin 3 [300.0 - 400.0uS]: 0 +[HW] Bin 4 [400.0 - 500.0uS]: 0 +[HW] Bin 5 [500.0 - 600.0uS]: 0 +[HW] Bin 6 [600.0 - 700.0uS]: 0 +[HW] Bin 7 [700.0 - 800.0uS]: 0 +[HW] Bin 8 [800.0 - 900.0uS]: 0 +[HW] Bin 9 [900.0 - 1000.0uS]: 117 +[HW] Bin 10 [1000.0 - 1100.0uS]: 0 +[HW] Bin 11 [1100.0 - 1200.0uS]: 0 +[HW] Bin 12 [1200.0 - 1300.0uS]: 0 +[HW] Bin 13 [1300.0 - 1400.0uS]: 0 +[HW] Bin 14 [1400.0 - 1500.0uS]: 0 +[HW] Bin 15 [1500.0 - 1600.0uS]: 0 +[HW] Bin 16 [1600.0 - 1700.0uS]: 0 +[HW] Bin 17 [1700.0 - 1800.0uS]: 0 +[HW] Bin 18 [1800.0 - 1900.0uS]: 0 +[HW] Bin 19 [1900.0 - 2000.0uS]: 0 +[HW] Bin 20 [2000.0 - 2100.0uS]: 0 +[HW] Bin 21 [2100.0 - 2200.0uS]: 0 +[HW] Bin 22 [2200.0 - 2300.0uS]: 0 +[HW] Bin 23 [2300.0 - 2400.0uS]: 0 +[HW] Bin 24 [2400.0 - 2500.0uS]: 0 +[HW] Bin 25 [2500.0 - 2600.0uS]: 0 +[HW] Bin 26 [2600.0 - 2700.0uS]: 0 +[HW] Bin 27 [2700.0 - 2800.0uS]: 0 +[HW] Bin 28 [2800.0 - 2900.0uS]: 0 +[HW] Bin 29 [2900.0 - 3000.0uS]: 0 +``` + +Samples in bin 0 indicate realtime issues. diff --git a/radio/vrtsim/noise_device.c b/radio/vrtsim/noise_device.c index 2e15944d4a..acb3a2b5da 100644 --- a/radio/vrtsim/noise_device.c +++ b/radio/vrtsim/noise_device.c @@ -18,6 +18,7 @@ * For more information about the OpenAirInterface (OAI) Software Alliance: * contact@openairinterface.org */ +#include "assertions.h" #include #include #include "noise_device.h" @@ -74,7 +75,8 @@ void init_noise_device(float noise_power) void free_noise_device(void) { state.running = false; - pthread_join(state.noise_device_thread, NULL); + int ret = pthread_join(state.noise_device_thread, NULL); + AssertFatal(ret == 0, "pthread_join failed: %d, errno %d (%s)\n", ret, errno, strerror(errno)); } void get_noise_vector(float *noise_vector, int length) diff --git a/radio/vrtsim/taps_client.cpp b/radio/vrtsim/taps_client.cpp new file mode 100644 index 0000000000..c314c3981c --- /dev/null +++ b/radio/vrtsim/taps_client.cpp @@ -0,0 +1,203 @@ +/* + * 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 +#include +#include +#include +#include +#include "pthread.h" +#include "taps_generated.h" +#include "SIMULATION/TOOLS/sim.h" +extern "C" { +#include "assertions.h" +#include "common/utils/LOG/log.h" +#include "sim.h" +#include "utils.h" +} +#include + +#define NUM_TAPS_BUFFERS 4 +#define MAX_TAPS_LEN 100 +#define MAX_TAPS_MSG_SIZE (sizeof(struct complexf) * MAX_TAPS_LEN * 4 * 4 + 20) + +static pthread_t client_thread; +static bool should_run = true; +typedef struct { + int id; + int sock; + uint32_t num_tx_antennas; + uint32_t num_rx_antennas; + channel_desc_t **channel_desc; +} client_thread_args_t; + +typedef struct { + void *taps_msg; + channel_desc_t *channel_desc; +} taps_buffer_t; + +typedef struct { + taps_buffer_t taps_buffers[NUM_TAPS_BUFFERS]; + int current_buffer; +} taps_storage_t; + +void ascii_line_plot(const float *data, size_t size, char *buffer) +{ + const char levels[] = "_.-=#"; // ASCII characters for different levels + size_t num_levels = sizeof(levels) - 1; // Number of levels (excluding the null terminator) + + // Calculate min and max values from the data + float min_val = FLT_MAX; + float max_val = FLT_MIN; + for (size_t i = 0; i < size; i++) { + if (data[i] < min_val) + min_val = data[i]; + if (data[i] > max_val) + max_val = data[i]; + } + + // Handle edge case where all values are the same + if (min_val == max_val) { + min_val -= 1.0f; + max_val += 1.0f; + } + + // Normalize and map data to levels + for (size_t i = 0; i < size; i++) { + float normalized = (data[i] - min_val) / (max_val - min_val); // Normalize to [0, 1] + size_t level_index = (size_t)(normalized * num_levels); // Map to level index + if (level_index > num_levels) + level_index = num_levels; // Clamp to valid range + snprintf(buffer + i, 2, "%c", levels[level_index]); + } +} + +static void init_taps_storage(taps_storage_t *storage, int num_tx_antennas, int num_rx_antennas) +{ + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + storage->taps_buffers[i].taps_msg = calloc_or_fail(1, MAX_TAPS_MSG_SIZE); + storage->taps_buffers[i].channel_desc = (channel_desc_t *)calloc_or_fail(1, sizeof(channel_desc_t)); + storage->taps_buffers[i].channel_desc->ch_ps = + (struct complexf **)calloc_or_fail(num_rx_antennas * num_tx_antennas, sizeof(struct complexf *)); + } + storage->current_buffer = 0; +} + +static taps_storage_t taps_storage; + +void *client_thread_func(void *args) +{ + client_thread_args_t *client_thread_args = (client_thread_args_t *)args; + while (should_run) { + int next_buffer = (taps_storage.current_buffer + 1) % NUM_TAPS_BUFFERS; + taps_buffer_t *taps_buffer = &taps_storage.taps_buffers[next_buffer]; + int ret = nn_recv(client_thread_args->sock, taps_buffer->taps_msg, MAX_TAPS_MSG_SIZE, NN_DONTWAIT); + if (ret < 0) { + if (errno == EAGAIN) { + // Timeout: no message available, sleep briefly and continue + usleep(100); + continue; + } + LOG_E(HW, "nn_recv() failed: errno: %d, %s\n", errno, strerror(errno)); + continue; + } + auto taps_message = Phy::GetTaps(taps_buffer->taps_msg); + if (taps_message->num_rx_antennas() != client_thread_args->num_rx_antennas + || taps_message->num_tx_antennas() != client_thread_args->num_tx_antennas) { + LOG_E(HW, + "Number of antennas mismatch: expected %d x %d, got %d x %d\n", + client_thread_args->num_rx_antennas, + client_thread_args->num_tx_antennas, + taps_message->num_rx_antennas(), + taps_message->num_tx_antennas()); + continue; + } + channel_desc_t *channel_desc = taps_buffer->channel_desc; + const flatbuffers::Vector *taps = taps_message->taps(); + struct complexf *base_pointer = (struct complexf *)taps->data(); + int taps_len = taps_message->taps_len(); + AssertFatal(taps_len < MAX_TAPS_LEN, "Too many samples in the taps array, taps_len = %d\n", taps_len); + for (unsigned int aarx = 0U; aarx < client_thread_args->num_rx_antennas; aarx++) { + for (unsigned int aatx = 0U; aatx < client_thread_args->num_tx_antennas; aatx++) { + channel_desc->ch_ps[aarx + (client_thread_args->num_rx_antennas * aatx)] = + &base_pointer[(aarx + (client_thread_args->num_rx_antennas * aatx)) * taps_len]; + } + } + channel_desc->path_loss_dB = 0; + channel_desc->channel_length = taps_len; + *client_thread_args->channel_desc = channel_desc; + taps_storage.current_buffer = next_buffer; + LOG_A(HW, "Receved new taps message, channel_length %d, buffer %d\n", channel_desc->channel_length, next_buffer); + for (unsigned int aarx = 0; aarx < client_thread_args->num_rx_antennas; aarx++) { + for (unsigned int aatx = 0; aatx < client_thread_args->num_tx_antennas; aatx++) { + char buffer[MAX_TAPS_LEN + 1]; + memset(buffer, 0, sizeof(buffer)); + float magnitudes[MAX_TAPS_LEN]; + cf_t *channel = channel_desc->ch_ps[aarx + (client_thread_args->num_rx_antennas * aatx)]; + for (int i = 0; i < channel_desc->channel_length; i++) { + magnitudes[i] = sqrtf(powf(channel[i].r, 2) + powf(channel[i].i, 2)); + } + ascii_line_plot(magnitudes, channel_desc->channel_length, buffer); + LOG_A(HW, "Taps message %d, channel %d x %d: %s\n", client_thread_args->id, aarx, aatx, buffer); + } + } + } + return NULL; +} + +extern "C" void taps_client_connect(int id, + const char *socket_path, + int num_tx_antennas, + int num_rx_antennas, + channel_desc_t **channel_desc) +{ + // Create a socket + int sock = nn_socket(AF_SP, NN_SUB); + AssertFatal(sock >= 0, "nn_socket() failed: errno: %d, %s\n", errno, strerror(errno)); + + int ret = nn_connect(sock, socket_path); + AssertFatal(ret >= 0, "nn_connect() failed: errno: %d, %s\n", errno, strerror(errno)); + + // Subscribe to all messages + ret = nn_setsockopt(sock, NN_SUB, NN_SUB_SUBSCRIBE, "", 0); + AssertFatal(ret == 0, "nn_setsockopt() failed, errno %d, %s\n", errno, strerror(errno)); + + init_taps_storage(&taps_storage, num_tx_antennas, num_rx_antennas); + + client_thread_args_t *client_thread_args = static_cast(malloc(sizeof(client_thread_args_t))); + client_thread_args->id = id; + client_thread_args->sock = sock; + client_thread_args->num_rx_antennas = num_rx_antennas; + client_thread_args->num_tx_antennas = num_tx_antennas; + client_thread_args->channel_desc = channel_desc; + ret = pthread_create(&client_thread, NULL, client_thread_func, client_thread_args); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); +} + +extern "C" void taps_client_stop() +{ + should_run = false; + pthread_join(client_thread, NULL); + for (int i = 0; i < NUM_TAPS_BUFFERS; i++) { + free(taps_storage.taps_buffers[i].taps_msg); + free(taps_storage.taps_buffers[i].channel_desc->ch_ps); + free(taps_storage.taps_buffers[i].channel_desc); + } +} diff --git a/openair3/S1AP/s1ap_common.c b/radio/vrtsim/taps_client.h similarity index 75% rename from openair3/S1AP/s1ap_common.c rename to radio/vrtsim/taps_client.h index d31c6e5839..c0a577c787 100644 --- a/openair3/S1AP/s1ap_common.c +++ b/radio/vrtsim/taps_client.h @@ -19,23 +19,20 @@ * contact@openairinterface.org */ -/*! \file s1ap_common.c - * \brief s1ap procedures for both eNB and MME - * \author Sebastien ROUX and Navid Nikaein - * \email navid.nikaein@eurecom.fr - * \date 2012-2015 - * \version 0.1 - */ +#ifndef TAPS_CLIENT_H +#define TAPS_CLIENT_H -#include +#ifdef __cplusplus +extern "C" { +#endif -#include "s1ap_common.h" -#include "S1AP_S1AP-PDU.h" +#include "sim.h" +void taps_client_connect(int id, const char *socket_path, int num_tx_antennas, int num_rx_antennas, channel_desc_t **channel_desc); +void taps_client_stop(); -int asn1_xer_print = 0; - - -void s1ap_handle_criticality(S1AP_Criticality_t criticality) -{ +#ifdef __cplusplus } +#endif + +#endif diff --git a/radio/vrtsim/vrtsim.c b/radio/vrtsim/vrtsim.c index f0e0151aa6..9e678a5305 100644 --- a/radio/vrtsim/vrtsim.c +++ b/radio/vrtsim/vrtsim.c @@ -19,6 +19,7 @@ * contact@openairinterface.org */ +#include "PHY/TOOLS/tools_defs.h" #include #include #include @@ -34,6 +35,7 @@ #include #include #include +#include #include #include @@ -46,11 +48,13 @@ #include "actor.h" #include "noise_device.h" #include "simde/x86/avx512.h" +#include "taps_client.h" // Simulator role typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; -#define NUM_CHANMOD_THREADS 1 +#define MAX_NUM_ANTENNAS_TX 4 +#define MAX_CHANNEL_LENGTH (1 << 20) #define ROLE_CLIENT_STRING "client" #define ROLE_SERVER_STRING "server" @@ -58,14 +62,21 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role; #define VRTSIM_SECTION "vrtsim" #define TIME_SCALE_HLP \ "sample time scale. 1.0 means realtime. Values > 1 mean faster than realtime. Values < 1 mean slower than realtime\n" +#define TAPS_SOCKET_HLP "Socket to connect to the channel emulation server\n" +#define CLIENT_NUM_RX_HLP "Number of RX antennas of the client, specified on the server\n" +#define CONNECTION_DESCRIPTOR_HLP "Path to the file written by the server that the client can use to connect." +#define DEFAULT_CHANNEL_NAME "vrtsim_channel" +#define DEFAULT_DESCRIPTOR "/tmp/vrtsim_connection" // clang-format off #define VRTSIM_PARAMS_DESC \ { \ - {"channel_name", "shared memory channel name\n", 0, .strptr = &channel_name, .defstrval = "vrtsim_channel", TYPE_STRING, 0}, \ - {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ - {"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ - {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ + {"connection_descriptor", CONNECTION_DESCRIPTOR_HLP, 0, .strptr = &vrtsim_state->connection_descriptor, .defstrval = DEFAULT_DESCRIPTOR, TYPE_STRING, 0}, \ + {"role", "either client or server\n", 0, .strptr = &role, .defstrval = ROLE_CLIENT_STRING, TYPE_STRING, 0}, \ + {"timescale", TIME_SCALE_HLP, 0, .dblptr = &vrtsim_state->timescale, .defdblval = 1.0, TYPE_DOUBLE, 0}, \ + {"chanmod", "Enable channel modelling", 0, .iptr = &vrtsim_state->chanmod, .defintval = 0, TYPE_INT, 0}, \ + {"taps-socket", TAPS_SOCKET_HLP, 0, .strptr = &vrtsim_state->taps_socket, .defstrval = NULL, TYPE_STRING, 0}, \ + {"client-num-rx-antennas", CLIENT_NUM_RX_HLP, 0, .iptr = &vrtsim_state->client_num_rx_antennas, .defintval = 1, TYPE_INT, 0}, \ }; // clang-format on @@ -91,7 +102,7 @@ typedef struct tx_timing_s { typedef struct { int role; - char channel_name[40]; + char *connection_descriptor; ShmTDIQChannel *channel; uint64_t last_received_sample; pthread_t timing_thread; @@ -110,8 +121,13 @@ typedef struct { int rx_num_channels; channel_desc_t *channel_desc; Actor_t *channel_modelling_actors; + char *taps_socket; + int client_num_rx_antennas; } vrtsim_state_t; +// Sample history for channel impulse response +static c16_t saved_samples[MAX_NUM_ANTENNAS_TX][MAX_CHANNEL_LENGTH] __attribute__((aligned(32))) = {0}; + static void histogram_add(histogram_t *histogram, double diff) { histogram->num_samples++; @@ -149,18 +165,26 @@ static void load_channel_model(vrtsim_state_t *vrtsim_state) vrtsim_state->tx_bw); char *model_name = vrtsim_state->role == ROLE_CLIENT ? "client_tx_channel_model" : "server_tx_channel_model"; vrtsim_state->channel_desc = find_channel_desc_fromname(model_name); + AssertFatal(vrtsim_state->channel_desc != NULL, + "Could not find model name %s. Make sure it is present in the config file", + model_name); + LOG_I(HW, + "Channel model %s parameters: path_loss_dB=%.2f, nb_tx=%d, nb_rx=%d, channel_length=%d\n", + model_name, + vrtsim_state->channel_desc->path_loss_dB, + vrtsim_state->channel_desc->nb_tx, + vrtsim_state->channel_desc->nb_rx, + vrtsim_state->channel_desc->channel_length); random_channel(vrtsim_state->channel_desc, 0); AssertFatal(vrtsim_state->channel_desc != NULL, "Could not find channel model %s\n", model_name); } static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) { - char *channel_name = NULL; char *role = NULL; paramdef_t vrtsim_params[] = VRTSIM_PARAMS_DESC; int ret = config_get(config_get_if(), vrtsim_params, sizeofArray(vrtsim_params), VRTSIM_SECTION); AssertFatal(ret >= 0, "configuration couldn't be performed\n"); - strncpy(vrtsim_state->channel_name, channel_name, sizeof(vrtsim_state->channel_name) - 1); if (strncmp(role, ROLE_CLIENT_STRING, strlen(ROLE_CLIENT_STRING)) == 0) { vrtsim_state->role = ROLE_CLIENT; } else if (strncmp(role, ROLE_SERVER_STRING, strlen(ROLE_SERVER_STRING)) == 0) { @@ -168,6 +192,15 @@ static void vrtsim_readconfig(vrtsim_state_t *vrtsim_state) } else { AssertFatal(false, "Invalid role configuration\n"); } +#ifdef ENABLE_TAPS_CLIENT + if (vrtsim_state->taps_socket) { + LOG_A(HW, "VRTSIM: will use taps socket %s\n", vrtsim_state->taps_socket); + } +#else + if (vrtsim_state->taps_socket) { + AssertFatal(false, "Invalid configuration: Build with ENABLE_TAPS_CLIENT to use taps socket\n"); + } +#endif } static void *vrtsim_timing_job(void *arg) @@ -195,128 +228,105 @@ static void *vrtsim_timing_job(void *arg) samples_to_produce += 1; leftover_samples -= 1; } + AssertFatal(samples_to_produce >= 0, "Negative samples to produce: %f\n", samples_to_produce); shm_td_iq_channel_produce_samples(vrtsim_state->channel, samples_to_produce); usleep(1); } return 0; } +typedef struct client_info_s { + int server_num_rx_antennas; + int client_num_rx_antennas; +} client_info_t; + /** - * @brief Exchanges peer information between the server and the client. + * @brief Publishes the client information information to a file for the client to read. * - * This function sets up a Unix domain socket server, waits for a client to connect, - * and exchanges peer information (number of RX antennas) with the client. + * The server writes its client_info (number of RX antennas) to a file, which the client reads. + * The server does not wait for the client to write back; the client can connect at any point. * - * @param peer_info The peer information to send to the client. - * @return The peer information received from the client. - * - * @note This function will block until a client connects and the information is exchanged. + * @param client_info The client information to publish. + * @return The peer information (same as input, server is authoritative). */ -static peer_info_t server_exchange_peer_info(peer_info_t peer_info) +static void server_publish_client_info(client_info_t client_info, char *descriptor_file) { - int server_fd, client_fd; - - // Create a Unix domain socket - server_fd = socket(AF_UNIX, SOCK_STREAM, 0); - AssertFatal(server_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Bind the socket to a file path - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1); - unlink(addr.sun_path); // Ensure the path does not already exist - int ret = bind(server_fd, (struct sockaddr *)&addr, sizeof(addr)); - AssertFatal(ret == 0, "bind() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Listen for incoming connections - ret = listen(server_fd, 1); - AssertFatal(ret == 0, "listen() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Accept a connection from a client - socklen_t addr_len = sizeof(addr); - client_fd = accept(server_fd, (struct sockaddr *)&addr, &addr_len); - AssertFatal(client_fd >= 0, "accept() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Send the antenna info to the server - ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno)); - - ssize_t bytes_received = recv(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_received == sizeof(peer_info), "recv() failed: errno: %d, %s\n", errno, strerror(errno)); - - LOG_I(HW, "Received client info: RX %d\n", peer_info.num_rx_antennas); - - // Close the client and server sockets - close(client_fd); - close(server_fd); - return peer_info; + FILE *fp = fopen(descriptor_file, "wb"); + AssertFatal(fp != NULL, "Failed to open client info file for writing: %s\n", strerror(errno)); + size_t written = fwrite(&client_info, sizeof(client_info), 1, fp); + AssertFatal(written == 1, "Failed to write client info to file\n"); + fclose(fp); } -/** - * @brief Exchanges peer information between the client and the server. - * - * This function connects to a Unix domain socket server, exchanges peer information - * (number of RX antennas) with the server, and returns the peer information received - * from the server. - * - * @param peer_info The peer information to send to the server. - * @return The peer information received from the server. - * - * @note This function will block until the information is exchanged. - */ -static peer_info_t client_exchange_peer_info(peer_info_t peer_info) +static client_info_t client_read_info(char *descriptor_file) { - int client_fd; - - // Create a Unix domain socket - client_fd = socket(AF_UNIX, SOCK_STREAM, 0); - AssertFatal(client_fd >= 0, "socket() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Connect to the server - struct sockaddr_un addr = {.sun_family = AF_UNIX}; - strncpy(addr.sun_path, "/tmp/vrtsim_socket", sizeof(addr.sun_path) - 1); - int ret = -1; + client_info_t client_info; int tries = 0; - while (ret != 0 && tries < 10) { - ret = connect(client_fd, (struct sockaddr *)&addr, sizeof(addr)); - tries++; + while (tries < 10) { + FILE *fp = fopen(descriptor_file, "rb"); + if (fp) { + size_t read = fread(&client_info, sizeof(client_info), 1, fp); + fclose(fp); + if (read == 1) { + return client_info; + } + } sleep(1); + tries++; } - AssertFatal(ret == 0, "connect() failed: errno: %d, %s\n", errno, strerror(errno)); - - peer_info_t peer_info_received; - ssize_t bytes_received = recv(client_fd, &peer_info_received, sizeof(peer_info_received), 0); - AssertFatal(bytes_received == sizeof(peer_info_received), "recv() failed: errno: %d, %s\n", errno, strerror(errno)); - - // Send the antenna info to the server - ssize_t bytes_sent = send(client_fd, &peer_info, sizeof(peer_info), 0); - AssertFatal(bytes_sent == sizeof(peer_info), "send() failed: errno: %d, %s\n", errno, strerror(errno)); - - LOG_I(HW, "Received peer info: RX %d\n", peer_info_received.num_rx_antennas); - - // Close the client socket - close(client_fd); - return peer_info_received; + AssertFatal(0, "Timeout waiting for client info\n"); + return client_info; } static int vrtsim_connect(openair0_device *device) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - // Exchange peer info - peer_info_t peer_info = {.num_rx_antennas = device->openair0_cfg[0].rx_num_channels}; + + // Setup a shared memory channel if (vrtsim_state->role == ROLE_SERVER) { - vrtsim_state->peer_info = server_exchange_peer_info(peer_info); + vrtsim_state->peer_info.num_rx_antennas = vrtsim_state->client_num_rx_antennas; + vrtsim_state->channel = shm_td_iq_channel_create(DEFAULT_CHANNEL_NAME, + vrtsim_state->peer_info.num_rx_antennas, + device->openair0_cfg[0].rx_num_channels); + // Exchange peer info + client_info_t client_info = { + .server_num_rx_antennas = device->openair0_cfg[0].rx_num_channels, + .client_num_rx_antennas = vrtsim_state->client_num_rx_antennas, + }; + server_publish_client_info(client_info, vrtsim_state->connection_descriptor); + + vrtsim_state->run_timing_thread = true; + int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state); + AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); } else { - vrtsim_state->peer_info = client_exchange_peer_info(peer_info); + client_info_t client_info = client_read_info(vrtsim_state->connection_descriptor); + AssertFatal(client_info.server_num_rx_antennas > 0, "Server did not publish valid client info, aborting client connection\n"); + AssertFatal( + client_info.client_num_rx_antennas == device->openair0_cfg[0].rx_num_channels, + "Server expects different number of RX antennas. %d != %d. Use server command line option --client-num-rx-antennas\n", + client_info.client_num_rx_antennas, + device->openair0_cfg[0].rx_num_channels); + vrtsim_state->channel = shm_td_iq_channel_connect(DEFAULT_CHANNEL_NAME, 10); + vrtsim_state->peer_info.num_rx_antennas = client_info.server_num_rx_antennas; + vrtsim_state->last_received_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); } // Handle channel modelling after number of RX antennas are known int num_tx_stats = 1; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { vrtsim_state->channel_modelling_actors = calloc_or_fail(vrtsim_state->peer_info.num_rx_antennas, sizeof(Actor_t)); for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { init_actor(&vrtsim_state->channel_modelling_actors[i], "chanmod", -1); } - load_channel_model(vrtsim_state); + if (vrtsim_state->taps_socket) { + taps_client_connect(0, + vrtsim_state->taps_socket, + device->openair0_cfg[0].tx_num_channels, + vrtsim_state->peer_info.num_rx_antennas, + &vrtsim_state->channel_desc); + } else { + load_channel_model(vrtsim_state); + } num_tx_stats = vrtsim_state->peer_info.num_rx_antennas; } vrtsim_state->tx_timing = calloc_or_fail(num_tx_stats, sizeof(tx_timing_t)); @@ -326,22 +336,6 @@ static int vrtsim_connect(openair0_device *device) vrtsim_state->tx_timing[i].tx_histogram.range = 3000.0; } - // Setup a shared memory channel - if (vrtsim_state->role == ROLE_SERVER) { - vrtsim_state->channel = shm_td_iq_channel_create(vrtsim_state->channel_name, - vrtsim_state->peer_info.num_rx_antennas, - device->openair0_cfg[0].rx_num_channels); - vrtsim_state->run_timing_thread = true; - while (!shm_td_iq_channel_is_connected(vrtsim_state->channel)) { - LOG_I(HW, "Waiting for client\n"); - sleep(1); - } - int ret = pthread_create(&vrtsim_state->timing_thread, NULL, vrtsim_timing_job, vrtsim_state); - AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno)); - } else { - vrtsim_state->channel = shm_td_iq_channel_connect(vrtsim_state->channel_name, 10); - } - return 0; } @@ -376,7 +370,7 @@ static int vrtsim_write_internal(vrtsim_state_t *vrtsim_state, typedef struct { vrtsim_state_t *vrtsim_state; openair0_timestamp timestamp; - c16_t *samples[4]; + c16_t *samples[MAX_NUM_ANTENNAS_TX]; int nsamps; int nbAnt; int flags; @@ -386,6 +380,7 @@ typedef struct { static void perform_channel_modelling(void *arg) { channel_modelling_args_t *channel_modelling_args = arg; + vrtsim_state_t *vrtsim_state = channel_modelling_args->vrtsim_state; int nsamps = channel_modelling_args->nsamps; int aarx = channel_modelling_args->aarx; int nb_tx_ant = channel_modelling_args->nbAnt; @@ -396,31 +391,43 @@ static void perform_channel_modelling(void *arg) // Apply noise from global settings get_noise_vector((float *)samples, nsamps * 2); - channel_desc_t *channel_desc = channel_modelling_args->vrtsim_state->channel_desc; - const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); + channel_desc_t *channel_desc = vrtsim_state->channel_desc; + + if (channel_desc == NULL) { + return; + } - // Convert channel impulse response to float + apply pathloss cf_t channel_impulse_response[nb_tx_ant][channel_desc->channel_length]; - for (int aatx = 0; aatx < nb_tx_ant; aatx++) { - const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)]; - for (int i = 0; i < channel_desc->channel_length; i++) { - channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear; - channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear; + cf_t *channel_impulse_response_p[nb_tx_ant]; + if (!vrtsim_state->taps_socket) { + const float pathloss_linear = powf(10, channel_desc->path_loss_dB / 20.0); + // Convert channel impulse response to float + apply pathloss + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + const struct complexd *channelModel = channel_desc->ch[aarx + (aatx * channel_desc->nb_rx)]; + for (int i = 0; i < channel_desc->channel_length; i++) { + channel_impulse_response[aatx][i].r = channelModel[i].r * pathloss_linear; + channel_impulse_response[aatx][i].i = channelModel[i].i * pathloss_linear; + } + channel_impulse_response_p[aatx] = channel_impulse_response[aatx]; + } + } else { + for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + struct complexf *channelModel = channel_desc->ch_ps[aarx + (aatx * channel_desc->nb_rx)]; + channel_impulse_response_p[aatx] = channelModel; } } for (int aatx = 0; aatx < nb_tx_ant; aatx++) { + c16_t *previous_samples = saved_samples[aatx]; for (int i = 0; i < nsamps; i++) { - cf_t *impulse_response = channel_impulse_response[aatx]; + cf_t *impulse_response = channel_impulse_response_p[aatx]; for (int l = 0; l < channel_desc->channel_length; l++) { int idx = i - l; - // TODO: Use AVX512 for this - // TODO: What are the previously sent samples (for impulse response) - if (idx >= 0) { - c16_t tx_input = input_samples[aatx][idx]; - samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i; - samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i; - } + // TODO: Use AVX2 for this + c16_t tx_input = idx >= 0 ? input_samples[aatx][idx] + : previous_samples[(channel_modelling_args->timestamp + i + idx) % MAX_CHANNEL_LENGTH]; + samples[i].r += tx_input.r * impulse_response[l].r - tx_input.i * impulse_response[l].i; + samples[i].i += tx_input.i * impulse_response[l].r + tx_input.r * impulse_response[l].i; } } } @@ -462,6 +469,7 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, int nbAnt, int flags) { + AssertFatal(nbAnt < MAX_NUM_ANTENNAS_TX, "Number of antennas %d exceeds maximum %d\n", nbAnt, MAX_NUM_ANTENNAS_TX); for (int aarx = 0; aarx < vrtsim_state->peer_info.num_rx_antennas; aarx++) { notifiedFIFO_elt_t *task = newNotifiedFIFO_elt(sizeof(channel_modelling_args_t), 0, NULL, perform_channel_modelling); channel_modelling_args_t *args = (channel_modelling_args_t *)NotifiedFifoData(task); @@ -476,21 +484,68 @@ static int vrtsim_write_with_chanmod(vrtsim_state_t *vrtsim_state, } pushNotifiedFIFO(&vrtsim_state->channel_modelling_actors[aarx].fifo, task); } + int start_index = timestamp % MAX_CHANNEL_LENGTH; + int end_index = min(start_index + nsamps, MAX_CHANNEL_LENGTH); + int cp_nsamps = end_index - start_index; + for (int aatx = 0; aatx < nbAnt; aatx++) { + c16_t *samples = (c16_t *)samplesVoid[aatx]; + memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps); + } + + if (end_index < start_index + nsamps) { + // wrap around condition, write at beginning of buffer + cp_nsamps = nsamps - cp_nsamps; // remaining samples + start_index = 0; + for (int aatx = 0; aatx < nbAnt; aatx++) { + c16_t *samples = (c16_t *)samplesVoid[aatx]; + memcpy(&saved_samples[aatx][start_index], &samples[0], sizeof(c16_t) * cp_nsamps); + } + } return nsamps; } static int vrtsim_write(openair0_device *device, openair0_timestamp timestamp, void **samplesVoid, int nsamps, int nbAnt, int flags) { + AssertFatal(nsamps > 0, "Number of samples must be greater than 0\n"); + AssertFatal(nbAnt > 0 && nbAnt <= MAX_NUM_ANTENNAS_TX, + "Number of antennas %d must be between 1 and %d\n", + nbAnt, + MAX_NUM_ANTENNAS_TX); + AssertFatal(timestamp >= 0, "Timestamp must be non-negative, got %ld\n", timestamp); timestamp -= device->openair0_cfg->command_line_sample_advance; vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - return vrtsim_state->chanmod ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) - : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); + bool channel_modelling = vrtsim_state->chanmod || vrtsim_state->taps_socket; + return channel_modelling ? vrtsim_write_with_chanmod(vrtsim_state, timestamp, samplesVoid, nsamps, nbAnt, flags) + : vrtsim_write_internal(vrtsim_state, timestamp, (c16_t *)samplesVoid[0], nsamps, 0, flags, 0); } static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, void **samplesVoid, int nsamps, int nbAnt) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps); + if (shm_td_iq_channel_is_aborted(vrtsim_state->channel)) { + return 0; + } + if (vrtsim_state->role == ROLE_SERVER) { + uint64_t timeout_uS = 0; // 0 means no timeout + shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS); + } else { + uint64_t start_sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); + uint64_t timeout_uS = 2 * 1000 * 1000; // 2 seconds timeout waiting for sample number to change + // + while (shm_td_iq_channel_wait(vrtsim_state->channel, vrtsim_state->last_received_sample + nsamps, timeout_uS) == 1) { + uint64_t sample = shm_td_iq_channel_get_current_sample(vrtsim_state->channel); + if (sample == start_sample) { + LOG_E(HW, + "VRTSIM: Read timeout waiting for sample %lu to change, aborting channel\n", + vrtsim_state->last_received_sample + nsamps); + shm_td_iq_channel_abort(vrtsim_state->channel); + break; + } else { + start_sample = sample; + } + } + } + int ret = shm_td_iq_channel_rx(vrtsim_state->channel, vrtsim_state->last_received_sample, nsamps, 0, samplesVoid[0]); if (ret == CHANNEL_ERROR_TOO_LATE) { vrtsim_state->rx_samples_late += nsamps; @@ -506,14 +561,14 @@ static int vrtsim_read(openair0_device *device, openair0_timestamp *ptimestamp, static void vrtsim_end(openair0_device *device) { vrtsim_state_t *vrtsim_state = (vrtsim_state_t *)device->priv; - if (vrtsim_state->role == ROLE_SERVER) { + if (vrtsim_state->role == ROLE_SERVER && vrtsim_state->run_timing_thread) { vrtsim_state->run_timing_thread = false; int ret = pthread_join(vrtsim_state->timing_thread, NULL); AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno)); } tx_timing_t *tx_timing = vrtsim_state->tx_timing; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { for (int i = 0; i < vrtsim_state->peer_info.num_rx_antennas; i++) { shutdown_actor(&vrtsim_state->channel_modelling_actors[i]); } @@ -526,9 +581,12 @@ static void vrtsim_end(openair0_device *device) tx_timing->tx_samples_total += tx_timing[i].tx_samples_total; } tx_timing->average_tx_budget /= vrtsim_state->peer_info.num_rx_antennas; + free_noise_device(); + if (vrtsim_state->taps_socket) { + taps_client_stop(); + } } - // produce 1 second of extra samples so threads can finish - shm_td_iq_channel_produce_samples(vrtsim_state->channel, vrtsim_state->sample_rate); + shm_td_iq_channel_abort(vrtsim_state->channel); sleep(1); shm_td_iq_channel_destroy(vrtsim_state->channel); @@ -540,9 +598,18 @@ static void vrtsim_end(openair0_device *device) "VRTSIM: Read/write too early (suspected radio implementaton error) TX: %lu, RX: %lu\n", tx_timing->tx_early, vrtsim_state->rx_early); - LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS\n", tx_timing->average_tx_budget); + LOG_I(HW, "VRTSIM: Average TX budget %.3lf uS (more is better)\n", tx_timing->average_tx_budget); histogram_print(&tx_timing->tx_histogram); free(vrtsim_state->tx_timing); + + if (vrtsim_state->role == ROLE_SERVER) { + int ret = remove(vrtsim_state->connection_descriptor); + if (ret != 0) { + LOG_E(HW, "Failed to remove connection descriptor file %s: %s\n", vrtsim_state->connection_descriptor, strerror(errno)); + } else { + LOG_A(HW, "Removed connection descriptor file %s\n", vrtsim_state->connection_descriptor); + } + } } static int vrtsim_stub(openair0_device *device) @@ -588,10 +655,11 @@ __attribute__((__visibility__("default"))) int device_init(openair0_device *devi vrtsim_state->tx_num_channels = openair0_cfg->tx_num_channels; vrtsim_state->rx_num_channels = openair0_cfg->rx_num_channels; - if (vrtsim_state->chanmod) { + if (vrtsim_state->chanmod || vrtsim_state->taps_socket) { init_channelmod(); int noise_power_dBFS = get_noise_power_dBFS(); int16_t noise_power = noise_power_dBFS == INVALID_DBFS_VALUE ? 0 : (int16_t)(32767.0 / powf(10.0, .05 * -noise_power_dBFS)); + LOG_A(HW, "VRTSIM: Noise power %d sample value\n", noise_power); init_noise_device(noise_power); } return 0;