Merge branch 'integration_2025_w34' into 'develop'

Integration: `2025.w34`

* !3555 Remove dead code and variables, simplify code
* !3591 separate computation of size and value for DCI precoding information
* !3550 some work for better interoperability with srsRAN DU
* !3562 cleaning syntax of nr-ru.c
* !3598 Align add_boolean_option as per CMake documentation use only ON/OFF for uniformity
* !3407 Taps client for vrtsim
* !3035 NR L1 common channel level functions
* !3589 RF emulator and improvements of the timers
* !3577 Fix for UECAP file handling in dora/phytest mode
* !3600 Fix for UE TCI state ID configuration
* !3599 DLSCH scheduler: fix bugs in UE sorting

See merge request oai/openairinterface5g!3597
This commit is contained in:
Robert Schmidt
2025-08-22 08:07:59 +00:00
98 changed files with 2735 additions and 1925 deletions
+26 -24
View File
@@ -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
@@ -0,0 +1,70 @@
<!--
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
-->
<testCaseList>
<htmlTabRef>gNB-PHY-Test-100-2x2-RFemu</htmlTabRef>
<htmlTabName>Timing phytest 100 MHz 2x2 RFemulator</htmlTabName>
<htmlTabIcon>tasks</htmlTabIcon>
<TestCaseRequestedList>
100000 390101 000001 390109 200000
</TestCaseRequestedList>
<TestCaseExclusionList></TestCaseExclusionList>
<testCase id="100000">
<class>Custom_Command</class>
<desc>Disable Sleep States</desc>
<node>caracal</node>
<command>sudo cpupower idle-set -D 0</command>
</testCase>
<testCase id="200000">
<class>Custom_Command</class>
<always_exec>true</always_exec>
<desc>Enable Sleep States</desc>
<node>caracal</node>
<command>sudo cpupower idle-set -E</command>
</testCase>
<testCase id="390101">
<class>Initialize_eNB</class>
<desc>Initialize gNB USRP</desc>
<node>caracal</node>
<Initialize_eNB_args>-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</Initialize_eNB_args>
<rt_stats_cfg>datalog_rt_stats.100.2x2.yaml</rt_stats_cfg>
<air_interface>NR</air_interface>
<cmd_prefix>numactl --cpunodebind=netdev:enp94s0f0np0 --membind=netdev:enp94s0f0np0</cmd_prefix>
</testCase>
<testCase id="000001">
<class>IdleSleep</class>
<desc>Sleep</desc>
<idle_sleep_time_in_sec>60</idle_sleep_time_in_sec>
</testCase>
<testCase id="390109">
<class>Terminate_eNB</class>
<node>caracal</node>
<always_exec>true</always_exec>
<desc>Terminate gNB</desc>
<air_interface>NR</air_interface>
</testCase>
</testCaseList>
+1 -1
View File
@@ -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
-1
View File
@@ -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
+1
View File
@@ -1,5 +1,6 @@
add_library(ds OBJECT
byte_array.c
byte_array_producer.c
seq_arr.c
)
+64
View File
@@ -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);
}
+40
View File
@@ -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 <stdint.h>
#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
+51 -34
View File
@@ -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);
}
@@ -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.
@@ -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;
+1
View File
@@ -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;
}
+1 -1
View File
@@ -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)
-1
View File
@@ -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 \
+1
View File
@@ -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.
+39 -137
View File
@@ -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; i<ru->nb_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<<ru->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; i<ru->nb_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 );
-50
View File
@@ -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) {
-1
View File
@@ -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;
+2 -50
View File
@@ -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;
-1
View File
@@ -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;
+6
View File
@@ -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);
+518 -649
View File
File diff suppressed because it is too large Load Diff
-1
View File
@@ -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);
-48
View File
@@ -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) {
+1 -2
View File
@@ -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; card<MAX_CARDS; card++) {
uint64_t dl_carrier, ul_carrier;
openair0_cfg[card].configFilename = NULL;
openair0_cfg[card].threequarter_fs = frame_parms->threequarter_fs;
openair0_cfg[card].sample_rate = frame_parms->samples_per_subframe * 1e3;
openair0_cfg[card].samples_per_frame = frame_parms->samples_per_frame;
-5
View File
@@ -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=&parallel_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;
-1
View File
@@ -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;
@@ -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!");
-1
View File
@@ -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
+3 -4
View File
@@ -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));
+7 -6
View File
@@ -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; k<N_ZC; k++) {
// 420 is the multiplicative inverse of 2 (required since ru is exp[j 2\pi n])
X_u[i][k] = ((uint32_t *)ru)[(((k*(1+(inv_u*k)))%N_ZC)*420)%N_ZC];
X_u[i][k] = root_unit[(((k * (1 + (inv_u * k))) % N_ZC) * 420) % N_ZC];
}
}
@@ -525,8 +526,8 @@ void init_prach_tables(int N_ZC) {
// Compute quantized roots of unity
for (i=0; i<N_ZC; i++) {
ru[i<<1] = (int16_t)(floor(32767.0*cos(2*M_PI*(double)i/N_ZC)));
ru[1+(i<<1)] = (int16_t)(floor(32767.0*sin(2*M_PI*(double)i/N_ZC)));
root_unit[i].r = (int16_t)(floor(32767.0 * cos(2 * M_PI * (double)i / N_ZC)));
root_unit[i].i = (int16_t)(floor(32767.0 * sin(2 * M_PI * (double)i / N_ZC)));
#ifdef PRACH_DEBUG
if (i<16)
+3 -11
View File
@@ -43,22 +43,14 @@
extern const uint16_t NCS_unrestricted[16];
extern const uint16_t NCS_restricted[15];
extern const uint16_t NCS_4[7];
extern int16_t ru[2 * 839]; // quantized roots of unity
extern c16_t root_unit[839]; // quantized roots of unity
extern uint16_t du[838];
// This is table 5.7.1-4 from 36.211
extern PRACH_TDD_PREAMBLE_MAP tdd_preamble_map[64][7];
extern const uint16_t prach_root_sequence_map0_3[838];
extern uint16_t prach_root_sequence_map0_3[838];
extern uint16_t prach_root_sequence_map4[138];
extern const uint16_t prach_root_sequence_map4[138];
void dump_prach_config(LTE_DL_FRAME_PARMS *frame_parms,uint8_t subframe);
+17 -30
View File
@@ -63,7 +63,6 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1
int16_t Ncp;
uint8_t n_ra_prb;
uint16_t NCS;
uint16_t *prach_root_sequence_map;
uint16_t preamble_offset,preamble_shift;
uint16_t preamble_index0,n_shift_ra,n_shift_ra_bar;
uint16_t d_start=-1,numshift;
@@ -72,11 +71,7 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1
//uint8_t f_ra,t1_ra;
uint16_t N_ZC = (prach_fmt<4)?839:139;
uint8_t not_found;
int k;
int16_t *Xu;
uint16_t u;
int32_t Xu_re,Xu_im;
uint16_t offset,offset2;
int prach_start;
int i, prach_len;
uint16_t first_nonzero_root_idx=0;
@@ -109,7 +104,7 @@ int32_t generate_prach( PHY_VARS_UE *ue, uint8_t eNB_id, uint8_t subframe, uint1
ue->frame_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; offset++,offset2+=preamble_shift) {
if (offset2 >= 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;
@@ -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);
@@ -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);
}
}
@@ -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 <openair1/PHY/TOOLS/phy_scope_interface.h>
#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]);
+7 -16
View File
@@ -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 <openair1/PHY/TOOLS/phy_scope_interface.h>
@@ -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
@@ -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<R;r++){
bundle_idx = r*C+c;
bundle_deint[bundle_idx] = c*R+r;
//printf("c %u r %u bundle_idx %u bundle_deinter %u\n", c, r, bundle_idx, bundle_deint[bundle_idx]);
}
}
for (k=0; k<N_bundle;k++)
{
for (m=0; m<nb_re*L;m++){
llr_deint[bundle_deint[k]*nb_re*L+m]= llr[k*nb_re*L+m];
//printf("k %d m %d bundle_deint %d llr_deint %d\n", k, m, bundle_deint[k], llr_deint[bundle_deint[k]*nb_re*L+m]);
}
}
}
//==============================================================================================
// Pre-processing for LLR computation
//==============================================================================================
@@ -1032,41 +971,6 @@ void nr_dlsch_scale_channel(uint32_t rx_size_symbol,
}
}
/** @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 rx_size_symbol
@param dl_ch_estimates_ext Channel estimates in allocated RBs
@param n_rx number of antennas in RX
@param n_tx number of antennas in TX
@param avg Pointer to average signal strength
@param len Number of allocated PDSCH REs
*/
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)
{
int16_t x = factor2(len);
int16_t y = (len) >> 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],
+8 -30
View File
@@ -38,6 +38,7 @@
#include <openair1/PHY/NR_UE_TRANSPORT/nr_transport_proto_ue.h>
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
#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; aarx<frame_parms->nb_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
+6 -2
View File
@@ -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
@@ -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,
-13
View File
@@ -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
+1 -1
View File
@@ -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;
+2 -7
View File
@@ -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;
+3
View File
@@ -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;
@@ -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,
+22 -16
View File
@@ -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);
-1
View File
@@ -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;
-2
View File
@@ -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;
+1 -1
View File
@@ -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");
+10 -2
View File
@@ -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++) {
+2
View File
@@ -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.
-2
View File
@@ -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;
-3
View File
@@ -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
+43 -249
View File
@@ -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; i<srs_config->srs_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
+46 -15
View File
@@ -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);
+5 -2
View File
@@ -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,
@@ -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;
@@ -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
+1 -1
View File
@@ -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);
+2 -2
View File
@@ -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);
}
-2
View File
@@ -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)
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
}
+12 -6
View File
@@ -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
+13 -4
View File
@@ -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);
+3 -3
View File
@@ -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);
}
-2
View File
@@ -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)
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
}
+2 -2
View File
@@ -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);
}
-2
View File
@@ -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)
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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);
}
+1 -1
View File
@@ -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,
-2
View File
@@ -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) {
-3
View File
@@ -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)
+1 -1
View File
@@ -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) {
+2 -2
View File
@@ -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) {
-8
View File
@@ -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_ */
+3 -3
View File
@@ -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);
}
-4
View File
@@ -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;
+143 -75
View File
@@ -21,6 +21,7 @@ extern "C" {
#include <openair2/LAYER2/nr_pdcp/nr_pdcp_oai_api.h>
#include <openair2/LAYER2/nr_rlc/nr_rlc_oai_api.h>
#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));
+5
View File
@@ -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);
+167
View File
@@ -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;
}
+91
View File
@@ -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 <stdint.h>
#include <stdbool.h>
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 */
+7 -2
View File
@@ -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()
-4
View File
@@ -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
-1
View File
@@ -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",
+4
View File
@@ -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)
+25
View File
@@ -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 <enable>` Enables (enable = 1) or disables (enable = 0) noise generation on RX. By default noise generation is enabled.
* `--rf_emulator.noise_level_dBFS <noise_level>` Sets the level of noise generated in dB. If omitted no noise is generated.
+369
View File
@@ -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 <errno.h>
#include <string.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
#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, &timestamp)) {
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, &current_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;
}
-2
View File
@@ -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};
+29 -2
View File
@@ -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()
+75 -5
View File
@@ -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.
+3 -1
View File
@@ -18,6 +18,7 @@
* For more information about the OpenAirInterface (OAI) Software Alliance:
* contact@openairinterface.org
*/
#include "assertions.h"
#include <pthread.h>
#include <errno.h>
#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)
+203
View File
@@ -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 <nanomsg/nn.h>
#include <nanomsg/pubsub.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#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 <cfloat>
#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<float> *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<client_thread_args_t *>(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);
}
}
@@ -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 <stdint.h>
#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
+208 -140
View File
@@ -19,6 +19,7 @@
* contact@openairinterface.org
*/
#include "PHY/TOOLS/tools_defs.h"
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
@@ -34,6 +35,7 @@
#include <pthread.h>
#include <sys/un.h>
#include <sys/socket.h>
#include <linux/limits.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
@@ -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;