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:
+26
-24
@@ -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>
|
||||
@@ -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
|
||||
|
||||
@@ -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,5 +1,6 @@
|
||||
add_library(ds OBJECT
|
||||
byte_array.c
|
||||
byte_array_producer.c
|
||||
seq_arr.c
|
||||
)
|
||||
|
||||
|
||||
@@ -0,0 +1,64 @@
|
||||
/*
|
||||
* Licensed to the OpenAirInterface (OAI) Software Alliance under one or more
|
||||
* contributor license agreements. See the NOTICE file distributed with
|
||||
* this work for additional information regarding copyright ownership.
|
||||
* The OpenAirInterface Software Alliance licenses this file to You under
|
||||
* the OAI Public License, Version 1.1 (the "License"); you may not use this file
|
||||
* except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.openairinterface.org/?page_id=698
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*-------------------------------------------------------------------------------
|
||||
* For more information about the OpenAirInterface (OAI) Software Alliance:
|
||||
* contact@openairinterface.org
|
||||
*/
|
||||
|
||||
#include "byte_array_producer.h"
|
||||
|
||||
/* create a byte array producer from an existing buffer */
|
||||
byte_array_producer_t byte_array_producer_from_buffer(uint8_t *buffer, int len)
|
||||
{
|
||||
byte_array_producer_t result = { .b.len = len, .b.buf = buffer, .pos = 0 };
|
||||
return result;
|
||||
}
|
||||
|
||||
/* put a byte in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_byte(byte_array_producer_t *b, uint8_t byte)
|
||||
{
|
||||
if (b->pos == b->b.len)
|
||||
return 0;
|
||||
|
||||
b->b.buf[b->pos] = byte;
|
||||
b->pos++;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* put an u32 number as big endian in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_u32_be(byte_array_producer_t *b, uint32_t v)
|
||||
{
|
||||
return byte_array_producer_put_byte(b, (v >> 24) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 16) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 8) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, v & 0xff);
|
||||
}
|
||||
|
||||
/* put an u24 number as big endian in a byte array if there is space
|
||||
* returns 1 on success, 0 on error
|
||||
*/
|
||||
int byte_array_producer_put_u24_be(byte_array_producer_t *b, uint32_t v)
|
||||
{
|
||||
return byte_array_producer_put_byte(b, (v >> 16) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, (v >> 8) & 0xff)
|
||||
&& byte_array_producer_put_byte(b, v & 0xff);
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
@@ -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
@@ -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)
|
||||
|
||||
@@ -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 \
|
||||
|
||||
@@ -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
@@ -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 );
|
||||
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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);
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -96,7 +96,6 @@ extern "C"
|
||||
|
||||
#define CONFIG_HLP_NUMEROLOGY "adding numerology for 5G\n"
|
||||
#define CONFIG_HLP_BAND "band index\n"
|
||||
#define CONFIG_HLP_EMULATE_RF "Emulated RF enabled(disable by defult)\n"
|
||||
#define CONFIG_HLP_PARALLEL_CMD "three config for level of parallelism 'PARALLEL_SINGLE_THREAD', 'PARALLEL_RU_L1_SPLIT', or 'PARALLEL_RU_L1_TRX_SPLIT'\n"
|
||||
#define CONFIG_HLP_WORKER_CMD "two option for worker 'WORKER_DISABLE' or 'WORKER_ENABLE'\n"
|
||||
#define CONFIG_HLP_USRP_THREAD "having extra thead for usrp tx\n"
|
||||
@@ -128,7 +127,6 @@ extern "C"
|
||||
#define CHAIN_OFFSET softmodem_params.chain_offset
|
||||
#define NUMEROLOGY softmodem_params.numerology
|
||||
#define BAND softmodem_params.band
|
||||
#define EMULATE_RF softmodem_params.emulate_rf
|
||||
#define CLOCK_SOURCE softmodem_params.clock_source
|
||||
#define TIMING_SOURCE softmodem_params.timing_source
|
||||
#define TUNE_OFFSET softmodem_params.tune_offset
|
||||
@@ -164,7 +162,6 @@ extern int usrp_tx_thread;
|
||||
{"q" , CONFIG_HLP_STMON, PARAMFLAG_BOOL, .iptr=&cpu_meas_enabled, .defintval=0, TYPE_INT, 0}, \
|
||||
{"numerology" , CONFIG_HLP_NUMEROLOGY, 0, .iptr=&NUMEROLOGY, .defintval=1, TYPE_INT, 0}, \
|
||||
{"band" , CONFIG_HLP_BAND, 0, .iptr=&BAND, .defintval=78, TYPE_INT, 0}, \
|
||||
{"emulate-rf" , CONFIG_HLP_EMULATE_RF, PARAMFLAG_BOOL, .iptr=&EMULATE_RF, .defintval=0, TYPE_INT, 0}, \
|
||||
{"parallel-config", CONFIG_HLP_PARALLEL_CMD, 0, .strptr=¶llel_config, .defstrval=NULL, TYPE_STRING, 0}, \
|
||||
{"worker-config", CONFIG_HLP_WORKER_CMD, 0, .strptr=&worker_config, .defstrval=NULL, TYPE_STRING, 0}, \
|
||||
{"noS1", CONFIG_HLP_NOS1, PARAMFLAG_BOOL, .uptr=&noS1, .defintval=0, TYPE_UINT, 0}, \
|
||||
@@ -217,7 +214,6 @@ extern int usrp_tx_thread;
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s5 = { NULL } }, \
|
||||
{ .s3a = { config_checkstr_assign_integer, \
|
||||
{"MONOLITHIC", "PNF", "VNF", "AERIAL","UE_STUB_PNF","UE_STUB_OFFNET","STANDALONE_PNF"}, \
|
||||
{NFAPI_MONOLITHIC, NFAPI_MODE_PNF, NFAPI_MODE_VNF, NFAPI_MODE_AERIAL,NFAPI_UE_STUB_PNF,NFAPI_UE_STUB_OFFNET,NFAPI_MODE_STANDALONE_PNF}, \
|
||||
@@ -320,7 +316,6 @@ typedef struct {
|
||||
int phy_test;
|
||||
int do_ra;
|
||||
uint8_t sl_mode;
|
||||
int emulate_rf;
|
||||
int chain_offset;
|
||||
int numerology;
|
||||
int band;
|
||||
|
||||
@@ -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!");
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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]);
|
||||
|
||||
@@ -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],
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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++) {
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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_ */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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));
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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 */
|
||||
@@ -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()
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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",
|
||||
|
||||
@@ -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)
|
||||
@@ -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.
|
||||
@@ -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, ×tamp)) {
|
||||
LOG_E(UTIL, "clock_gettime failed\n");
|
||||
exit(1);
|
||||
}
|
||||
double leftover_samples = 0;
|
||||
while (emulator_state->run_timing_thread) {
|
||||
struct timespec current_time;
|
||||
if (clock_gettime(CLOCK_REALTIME, ¤t_time)) {
|
||||
LOG_E(UTIL, "clock_gettime failed\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp;
|
||||
if (current_time.tv_sec != timestamp.tv_sec) {
|
||||
if (emulator_timestamp->late_reads != 0) {
|
||||
LOG_W(HW, "%ld RF reads were late\n", emulator_timestamp->late_reads);
|
||||
}
|
||||
emulator_timestamp->late_reads = 0;
|
||||
if (emulator_timestamp->late_writes != 0) {
|
||||
LOG_W(HW, "%ld RF writes were late\n", emulator_timestamp->late_writes);
|
||||
}
|
||||
emulator_timestamp->late_writes = 0;
|
||||
}
|
||||
|
||||
uint64_t diff = (current_time.tv_sec - timestamp.tv_sec) * 1000000000 + (current_time.tv_nsec - timestamp.tv_nsec);
|
||||
timestamp = current_time;
|
||||
double samples_to_produce = emulator_state->sample_rate * emulator_state->timescale * diff / 1e9;
|
||||
|
||||
// Attempt to correct compounding rounding error
|
||||
leftover_samples += samples_to_produce - (uint64_t)samples_to_produce;
|
||||
if (leftover_samples > 1.0f) {
|
||||
samples_to_produce += 1;
|
||||
leftover_samples -= 1;
|
||||
}
|
||||
|
||||
mutexlock(emulator_timestamp->mutex);
|
||||
emulator_timestamp->timestamp += samples_to_produce;
|
||||
condbroadcast(emulator_timestamp->cond);
|
||||
mutexunlock(emulator_timestamp->mutex);
|
||||
|
||||
usleep(1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Called to start the RF transceiver. Return 0 if OK, < 0 if error
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
*/
|
||||
static int emulator_start(openair0_device *device)
|
||||
{
|
||||
// Start the timing thread and the noise device
|
||||
emulator_state_t *emulator_state = (emulator_state_t *)device->priv;
|
||||
if (!emulator_state->run_timing_thread) {
|
||||
if (emulator_state->enable_noise > 0) {
|
||||
float noise_level = emulator_state->noise_level_dBFS == INVALID_DBFS_VALUE
|
||||
? 0.0f
|
||||
: (32767.0 / powf(10.0, .05 * -(emulator_state->noise_level_dBFS)));
|
||||
init_noise_device(noise_level);
|
||||
LOG_I(HW, "Using noise level with %d dBFS/%f\n", emulator_state->noise_level_dBFS, noise_level);
|
||||
}
|
||||
|
||||
emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp;
|
||||
memset(emulator_timestamp, 0, sizeof(emulator_timestamp_t));
|
||||
|
||||
pthread_mutexattr_t mutex_attr;
|
||||
pthread_condattr_t cond_attr;
|
||||
int ret = pthread_mutexattr_init(&mutex_attr);
|
||||
AssertFatal(ret == 0, "pthread_mutexattr_init() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
ret = pthread_condattr_init(&cond_attr);
|
||||
AssertFatal(ret == 0, "pthread_condattr_init() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
ret = pthread_mutexattr_setpshared(&mutex_attr, PTHREAD_PROCESS_SHARED);
|
||||
AssertFatal(ret == 0, "pthread_mutexattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
ret = pthread_condattr_setpshared(&cond_attr, PTHREAD_PROCESS_SHARED);
|
||||
AssertFatal(ret == 0, "pthread_condattr_setpshared() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
ret = pthread_mutex_init(&emulator_timestamp->mutex, &mutex_attr);
|
||||
AssertFatal(ret == 0, "pthread_mutex_init() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
ret = pthread_cond_init(&emulator_timestamp->cond, &cond_attr);
|
||||
AssertFatal(ret == 0, "pthread_cond_init() failed: errno %d, %s\n", errno, strerror(errno));
|
||||
|
||||
emulator_state->run_timing_thread = true;
|
||||
ret = pthread_create(&emulator_state->timing_thread, NULL, emulator_timing_job, emulator_state);
|
||||
AssertFatal(ret == 0, "pthread_create() failed: errno: %d, %s\n", errno, strerror(errno));
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief print the RF statistics
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int emulator_get_stats(openair0_device *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Reset the RF statistics
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \returns 0 on success
|
||||
*/
|
||||
int emulator_reset_stats(openair0_device *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Terminate operation of the RF transceiver -- free all associated resources (if any)
|
||||
* \param pointer to the device structure specific to the RF hardware target
|
||||
*/
|
||||
static void emulator_end(openair0_device *device)
|
||||
{
|
||||
// Stop the timing thread and the noise device
|
||||
emulator_state_t *emulator_state = (emulator_state_t *)device->priv;
|
||||
if (emulator_state->run_timing_thread) {
|
||||
emulator_state->run_timing_thread = false;
|
||||
int ret = pthread_join(emulator_state->timing_thread, NULL);
|
||||
AssertFatal(ret == 0, "pthread_join() failed: errno: %d, %s\n", errno, strerror(errno));
|
||||
if (emulator_state->enable_noise > 0) {
|
||||
free_noise_device();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/*! \brief Stop RF
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
*/
|
||||
int emulator_stop(openair0_device *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Set Gains (TX/RX)
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
* \returns 0 in success
|
||||
*/
|
||||
int emulator_set_gains(openair0_device *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Set frequencies (TX/RX).
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \param openair0_cfg RF frontend parameters set by application
|
||||
* \returns 0 in success
|
||||
*/
|
||||
int emulator_set_freq(openair0_device *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
int emulator_write_init(openair0_device *device)
|
||||
{
|
||||
return (0);
|
||||
}
|
||||
|
||||
/*! \brief Called to send samples to the RF target
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \param timestamp The timestamp at which the first sample MUST be sent
|
||||
* \param buff Buffer which holds the samples
|
||||
* \param nsamps number of samples to be sent
|
||||
* \param nbAnt number of antennas
|
||||
* \param flags flags must be set to true if timestamp parameter needs to be applied
|
||||
*/
|
||||
static int emulator_write(openair0_device *device, openair0_timestamp timestamp, void **buff, int nsamps, int nbAnt, int flags)
|
||||
{
|
||||
emulator_state_t *emulator_state = (emulator_state_t *)device->priv;
|
||||
|
||||
// timestamp in the past
|
||||
emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp;
|
||||
uint64_t current_timestamp = emulator_timestamp->timestamp;
|
||||
if (timestamp < current_timestamp) {
|
||||
mutexlock(emulator_timestamp->mutex);
|
||||
emulator_timestamp->late_writes++;
|
||||
mutexunlock(emulator_timestamp->mutex);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*! \brief Store noise in the buffer buff
|
||||
* \param[out] buff A buffer for received samples.
|
||||
* The buffers must be large enough to hold the number of samples \ref nsamps.
|
||||
* \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
|
||||
*/
|
||||
static void read_noise(void *buff, int nsamps)
|
||||
{
|
||||
int aligned_nsamps = ceil_mod(nsamps, (512 / 8) / sizeof(cf_t));
|
||||
cf_t samples[aligned_nsamps] __attribute__((aligned(64)));
|
||||
// Apply noise from global settings
|
||||
get_noise_vector((float *)samples, nsamps * 2);
|
||||
|
||||
// Convert to c16_t
|
||||
c16_t samples_out[aligned_nsamps] __attribute__((aligned(64)));
|
||||
#if defined(__AVX512F__)
|
||||
for (int i = 0; i < aligned_nsamps / 8; i++) {
|
||||
simde__m512 *in = (simde__m512 *)&samples[i * 8];
|
||||
simde__m256i *out = (simde__m256i *)&samples_out[i * 8];
|
||||
*out = simde_mm512_cvtsepi32_epi16(simde_mm512_cvtps_epi32(*in));
|
||||
}
|
||||
#elif defined(__AVX2__)
|
||||
for (int i = 0; i < aligned_nsamps / 4; i++) {
|
||||
simde__m256 *in = (simde__m256 *)&samples[i * 4];
|
||||
simde__m128i *out = (simde__m128i *)&samples_out[i * 4];
|
||||
*out = simde_mm256_cvtsepi32_epi16(simde_mm256_cvtps_epi32(*in));
|
||||
}
|
||||
#else
|
||||
for (int i = 0; i < nsamps; i++) {
|
||||
samples_out[i].r = lroundf(samples[i].r);
|
||||
samples_out[i].i = lroundf(samples[i].i);
|
||||
}
|
||||
#endif
|
||||
memcpy(buff, samples_out, nsamps * sizeof(c16_t));
|
||||
}
|
||||
|
||||
/*! \brief Receive samples from hardware.
|
||||
* Read \ref nsamps samples from each channel to buffers. buff[0] is the array for
|
||||
* the first channel.
|
||||
* \param[out] ptimestamp time at which the first sample was received.
|
||||
* \param device pointer to the device structure specific to the RF hardware target
|
||||
* \param[out] ptimestamp the time at which the first sample was received.
|
||||
* \param[out] buff An array of pointers to buffers for received samples. The buffers must be large enough to hold the number of
|
||||
* samples \ref nsamps. \param nsamps Number of samples. One sample is 2 byte I + 2 byte Q => 4 byte.
|
||||
* \param nbAnt number of antennas
|
||||
* \returns the number of samples read
|
||||
*/
|
||||
static int emulator_read(openair0_device *device, openair0_timestamp *ptimestamp, void **buff, int nsamps, int nbAnt)
|
||||
{
|
||||
emulator_state_t *emulator_state = (emulator_state_t *)device->priv;
|
||||
|
||||
emulator_timestamp_t *emulator_timestamp = &emulator_state->emulator_timestamp;
|
||||
uint64_t timestamp = emulator_state->last_received_sample + nsamps;
|
||||
uint64_t current_timestamp = emulator_timestamp->timestamp;
|
||||
if (current_timestamp < timestamp) {
|
||||
mutexlock(emulator_timestamp->mutex);
|
||||
while (current_timestamp < timestamp) {
|
||||
condwait(emulator_timestamp->cond, emulator_timestamp->mutex);
|
||||
current_timestamp = emulator_timestamp->timestamp;
|
||||
}
|
||||
mutexunlock(emulator_timestamp->mutex);
|
||||
} else {
|
||||
mutexlock(emulator_timestamp->mutex);
|
||||
emulator_timestamp->late_reads++;
|
||||
mutexunlock(emulator_timestamp->mutex);
|
||||
}
|
||||
*ptimestamp = emulator_state->last_received_sample;
|
||||
emulator_state->last_received_sample += nsamps;
|
||||
|
||||
if (emulator_state->enable_noise > 0) {
|
||||
for (int i = 0; i < nbAnt; i++) {
|
||||
read_noise(buff[i], nsamps);
|
||||
}
|
||||
} else {
|
||||
for (int i = 0; i < nbAnt; i++) {
|
||||
memset(buff[i], 0, nsamps * sizeof(c16_t));
|
||||
}
|
||||
}
|
||||
|
||||
return nsamps;
|
||||
}
|
||||
|
||||
static void emulator_readconfig(emulator_state_t *emulator_state)
|
||||
{
|
||||
paramdef_t emulator_params[] = RF_EMULATOR_PARAMS_DESC;
|
||||
int ret = config_get(config_get_if(), emulator_params, sizeofArray(emulator_params), RF_EMULATOR_SECTION);
|
||||
AssertFatal(ret >= 0, "configuration couldn't be performed\n");
|
||||
}
|
||||
|
||||
int device_init(openair0_device *device, openair0_config_t *openair0_cfg)
|
||||
{
|
||||
LOG_I(HW, "This is emulator RF that does nothing\n");
|
||||
|
||||
emulator_state_t *emulator_state = calloc_or_fail(1, sizeof(emulator_state_t));
|
||||
emulator_state->last_received_sample = 0;
|
||||
emulator_state->timescale = 1.0;
|
||||
emulator_state->sample_rate = openair0_cfg->sample_rate;
|
||||
emulator_state->run_timing_thread = false;
|
||||
|
||||
device->priv = emulator_state;
|
||||
device->openair0_cfg = openair0_cfg;
|
||||
device->trx_start_func = emulator_start;
|
||||
device->trx_get_stats_func = emulator_get_stats;
|
||||
device->trx_reset_stats_func = emulator_reset_stats;
|
||||
device->trx_end_func = emulator_end;
|
||||
device->trx_stop_func = emulator_stop;
|
||||
device->trx_set_freq_func = emulator_set_freq;
|
||||
device->trx_set_gains_func = emulator_set_gains;
|
||||
device->trx_write_init = emulator_write_init;
|
||||
device->type = RFSIMULATOR;
|
||||
device->trx_write_func = emulator_write;
|
||||
device->trx_read_func = emulator_read;
|
||||
|
||||
emulator_readconfig(emulator_state);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -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};
|
||||
|
||||
@@ -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
@@ -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.
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
@@ -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;
|
||||
|
||||
Reference in New Issue
Block a user