mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-14 05:00:31 +00:00
Compare commits
59 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7d3ef0140b | ||
|
|
ecc6c9ec3e | ||
|
|
ce211dc2ef | ||
|
|
c3971d524f | ||
|
|
5f3ea814e5 | ||
|
|
c2fdea2f01 | ||
|
|
df5ddd2ea4 | ||
|
|
e5c922feba | ||
|
|
65df4e2afd | ||
|
|
af8d1daf36 | ||
|
|
e4ea2170b0 | ||
|
|
4bb6c48a2f | ||
|
|
4e6caca5a7 | ||
|
|
aa01359088 | ||
|
|
d92a51d468 | ||
|
|
876696494f | ||
|
|
7c6dd3b563 | ||
|
|
3ef37bfb37 | ||
|
|
a0a0f1dbcd | ||
|
|
7bdf1edf8a | ||
|
|
3c45d35972 | ||
|
|
4b14d6609b | ||
|
|
b3c9ef630a | ||
|
|
29d2586b8d | ||
|
|
51ca8ed10d | ||
|
|
c48e3e21e4 | ||
|
|
a9d8f6edb4 | ||
|
|
ea95f93427 | ||
|
|
7ccc29aed3 | ||
|
|
ac90a535f7 | ||
|
|
c5f3bae132 | ||
|
|
b6d928d7a1 | ||
|
|
21de22e0a3 | ||
|
|
ec5f6e7e57 | ||
|
|
7999a88ea0 | ||
|
|
3ea1ad3a54 | ||
|
|
2213ea1b31 | ||
|
|
54e0287330 | ||
|
|
aa4481ac08 | ||
|
|
74374975ca | ||
|
|
69efebb153 | ||
|
|
0f1bd004d3 | ||
|
|
efdf65d763 | ||
|
|
707b031007 | ||
|
|
8b59a83ab6 | ||
|
|
b33261e10c | ||
|
|
2048ba532f | ||
|
|
ac5212053f | ||
|
|
82a60c6d7b | ||
|
|
1043bf4a6c | ||
|
|
ad3158887c | ||
|
|
70a7d63888 | ||
|
|
a455ae1436 | ||
|
|
f71a0fd372 | ||
|
|
8aeb08d5e2 | ||
|
|
3ae1b6fc7c | ||
|
|
7d95e58ad8 | ||
|
|
79645dd554 | ||
|
|
02a95300cd |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,3 +34,5 @@ nfapi_nr_interface_scf
|
||||
*.log
|
||||
*.out
|
||||
CMakeUserPresets.json
|
||||
|
||||
build/*
|
||||
|
||||
@@ -377,6 +377,12 @@ endif()
|
||||
add_boolean_option(ENABLE_IMSCOPE OFF "Enable phy scope based on imgui" OFF)
|
||||
add_boolean_option(ENABLE_IMSCOPE_RECORD OFF "Enable recording IQ data for imscope" OFF)
|
||||
|
||||
#########################
|
||||
##### E3 AGENT
|
||||
#########################
|
||||
set(E3_AGENT "OFF" CACHE STRING "O-RAN nGRG E3 Agent for dApps")
|
||||
set_property(CACHE E3_AGENT PROPERTY STRINGS "ON" "OFF")
|
||||
|
||||
##################################################
|
||||
# ASN.1 grammar C code generation & dependencies #
|
||||
##################################################
|
||||
@@ -729,6 +735,9 @@ set(SCHED_NR_SRC
|
||||
add_library(SCHED_NR_LIB ${SCHED_NR_SRC})
|
||||
target_link_libraries(SCHED_NR_LIB PRIVATE asn1_nr_rrc_hdrs UTIL)
|
||||
target_link_libraries(SCHED_NR_LIB PRIVATE NFAPI_LIB) # some FAPI messages pack "subPDUs"
|
||||
if(E3_AGENT)
|
||||
target_compile_definitions(SCHED_NR_LIB PRIVATE E3_AGENT)
|
||||
endif()
|
||||
|
||||
set(SCHED_SRC_RU
|
||||
${OPENAIR1_DIR}/SCHED/ru_procedures.c
|
||||
@@ -1339,6 +1348,7 @@ if(E2_AGENT)
|
||||
target_compile_definitions(L2 PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(MAC_UE_NR ${MAC_NR_SRC_UE})
|
||||
target_link_libraries(MAC_UE_NR PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs PUBLIC nr_ue_power_procedures nr_ue_ra_procedures)
|
||||
|
||||
@@ -1369,6 +1379,10 @@ if(OAI_AERIAL)
|
||||
target_compile_definitions(L2_NR PRIVATE ENABLE_AERIAL)
|
||||
endif()
|
||||
|
||||
if(E3_AGENT)
|
||||
target_compile_definitions(L2_NR PRIVATE E3_AGENT)
|
||||
endif()
|
||||
|
||||
add_library(L2_LTE_NR
|
||||
# temporary solution until 4G/5G code completely untangled (as evidenced by deletion of the following file)
|
||||
${MAC_DIR}/dummy_functions.c
|
||||
@@ -1889,6 +1903,11 @@ if(E2_AGENT)
|
||||
target_compile_definitions(nr-softmodem PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
|
||||
endif()
|
||||
|
||||
if (E3_AGENT)
|
||||
target_link_libraries(nr-softmodem PRIVATE e3ap)
|
||||
target_compile_definitions(nr-softmodem PRIVATE E3_AGENT)
|
||||
endif()
|
||||
|
||||
|
||||
# force the generation of ASN.1 so that we don't need to wait during the build
|
||||
target_link_libraries(nr-softmodem PRIVATE
|
||||
@@ -1911,6 +1930,10 @@ target_link_libraries(nr-cuup PRIVATE
|
||||
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
|
||||
if(E2_AGENT)
|
||||
target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup)
|
||||
if(E3_AGENT)
|
||||
target_link_libraries(nr-cuup PRIVATE e3ap)
|
||||
target_compile_definitions(nr-cuup PRIVATE E3_AGENT)
|
||||
endif()
|
||||
target_compile_definitions(nr-cuup PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
|
||||
endif()
|
||||
|
||||
|
||||
@@ -43,6 +43,8 @@ Options:
|
||||
Sets build directory (will be <oai-root>/cmake_targets/<build-dir>/build)
|
||||
--build-e2
|
||||
Enable the the E2 Agent
|
||||
--build-e3
|
||||
Enable the the E3 Agent
|
||||
-I | --install-external-packages
|
||||
Installs required packages such as LibXML, asn1.1 compiler, ...
|
||||
This option will require root password
|
||||
@@ -143,6 +145,10 @@ function main() {
|
||||
CMAKE_CMD="$CMAKE_CMD -DE2_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
--build-e3 )
|
||||
CMAKE_CMD="$CMAKE_CMD -DE3_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
-I | --install-external-packages)
|
||||
INSTALL_EXTERNAL=1
|
||||
echo_info "Will install external packages"
|
||||
|
||||
@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
|
||||
LOG_T(<component>,<format>,<argument>,...)
|
||||
)
|
||||
```
|
||||
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functionnal module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
|
||||
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functional module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
|
||||
|
||||
| macro | level letter | level value | level name |
|
||||
|:---------|:---------------|:---------------|----------------:|
|
||||
|
||||
@@ -155,6 +155,7 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""};
|
||||
COMP_DEF(NFAPI_PNF, log) \
|
||||
COMP_DEF(ITTI, log) \
|
||||
COMP_DEF(UTIL, log) \
|
||||
COMP_DEF(E3AP, log) \
|
||||
COMP_DEF(MAX_LOG_PREDEF_COMPONENTS, )
|
||||
|
||||
#define COMP_ENUM(comp, file_extension) comp,
|
||||
|
||||
@@ -159,6 +159,10 @@ ID = GNB_PHY_UL_SRS_TOA_NS
|
||||
DESC = gNB ToA estimate from SRS (in ns)
|
||||
GROUP = ALL:PHY:GNB
|
||||
FORMAT = int,gNB_ID : int,rnti : int,frame : int,subframe : buffer,toa_ns
|
||||
ID = GNB_PHY_UL_FREQ_SENSING_SYMBOL
|
||||
DESC = gNodeB output data in the freq domain for sensing symbols
|
||||
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
|
||||
FORMAT = int,gNB_ID : int,frame : int,slot : int,antenna : buffer,rxdata
|
||||
|
||||
#MAC logs
|
||||
ID = ENB_MAC_UE_DL_SDU
|
||||
@@ -426,6 +430,27 @@ ID = LEGACY_MAC_TRACE
|
||||
GROUP = ALL:LEGACY_MAC:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_E3AP_INFO
|
||||
DESC = E3AP legacy logs - info level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_INFO:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_ERROR
|
||||
DESC = E3AP legacy logs - error level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_ERROR:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_WARNING
|
||||
DESC = E3AP legacy logs - warning level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_WARNING:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_DEBUG
|
||||
DESC = E3AP legacy logs - debug level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_DEBUG:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_TRACE
|
||||
DESC = E3AP legacy logs - trace level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_NR_MAC_INFO
|
||||
DESC = NR_MAC legacy logs - info level
|
||||
GROUP = ALL:LEGACY_NR_MAC:LEGACY_GROUP_INFO:LEGACY
|
||||
|
||||
@@ -12,6 +12,8 @@
|
||||
#include <netinet/in.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <math.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
void new_thread(void *(*f)(void *), void *data) {
|
||||
pthread_t t;
|
||||
@@ -233,6 +235,117 @@ again:
|
||||
return s;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* unix domain socket */
|
||||
/****************************************************************************/
|
||||
|
||||
int try_connect_to_uds(char *path)
|
||||
{
|
||||
int s;
|
||||
struct sockaddr_un a;
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (s == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&a, 0, sizeof(struct sockaddr_un));
|
||||
a.sun_family = AF_UNIX;
|
||||
strncpy(a.sun_path, path, sizeof(a.sun_path) - 1);
|
||||
|
||||
if (connect(s, (struct sockaddr *)&a, sizeof(a)) == -1) {
|
||||
perror("connect");
|
||||
close(s);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int connect_to_uds(char *path)
|
||||
{
|
||||
int s;
|
||||
printf("connecting to %s\n", path);
|
||||
again:
|
||||
s = try_connect_to_uds(path);
|
||||
|
||||
if (s == -1) {
|
||||
perror("trying again in 1s\n");
|
||||
sleep(1);
|
||||
goto again;
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int create_listen_uds_socket(char *path)
|
||||
{
|
||||
struct sockaddr_un a;
|
||||
int s;
|
||||
int v;
|
||||
s = socket(AF_UNIX, SOCK_STREAM, 0);
|
||||
|
||||
if (s == -1) {
|
||||
perror("socket");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
v = 1;
|
||||
|
||||
if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &v, sizeof(int))) {
|
||||
perror("setsockopt");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Bind the socket to a file path
|
||||
memset(&a, 0, sizeof(struct sockaddr_un));
|
||||
a.sun_family = AF_UNIX;
|
||||
strncpy(a.sun_path, path, sizeof(a.sun_path) - 1);
|
||||
|
||||
// Remove any existing socket file
|
||||
unlink(path);
|
||||
|
||||
if (bind(s, (struct sockaddr *)&a, sizeof(a)) == -1) {
|
||||
perror("Bind failed");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set permissions to 770 (rwxrwx---) to make sure groups can access
|
||||
if (chmod(path, 0770) == -1) {
|
||||
perror("Failed to change permissions");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Listen for incoming connections
|
||||
if (listen(s, 5) == -1) {
|
||||
perror("Listen failed");
|
||||
close(s);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return s;
|
||||
}
|
||||
|
||||
int get_uds_connection(char *path)
|
||||
{
|
||||
int s, t;
|
||||
printf("waiting for connection on %s\n", path);
|
||||
s = create_listen_uds_socket(path);
|
||||
t = socket_accept(s);
|
||||
|
||||
if (t == -1) {
|
||||
perror("accept");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
close(s);
|
||||
printf("connected\n");
|
||||
return t;
|
||||
}
|
||||
|
||||
/****************************************************************************/
|
||||
/* buffer */
|
||||
/****************************************************************************/
|
||||
|
||||
@@ -19,6 +19,11 @@ unsigned short config_frames[4] = {2,9,11,13};
|
||||
#include "openair2/E2AP/flexric/src/agent/e2_agent_api.h"
|
||||
#include "openair2/E2AP/RAN_FUNCTION/init_ran_func.h"
|
||||
#endif
|
||||
|
||||
#ifdef E3_AGENT
|
||||
#include "openair2/E3AP/e3_agent.h"
|
||||
#endif
|
||||
|
||||
#include "nr-softmodem.h"
|
||||
#include <common/utils/assertions.h>
|
||||
#include <openair2/GNB_APP/gnb_app.h>
|
||||
@@ -527,6 +532,13 @@ int main( int argc, char **argv ) {
|
||||
|
||||
softmodem_verify_mode(get_softmodem_params());
|
||||
|
||||
//////////////////////////////////
|
||||
//// Init the E3 Agent
|
||||
#ifdef E3_AGENT
|
||||
printf("Init E3 Agent\n");
|
||||
e3_init();
|
||||
#endif // E3_AGENT
|
||||
|
||||
#if T_TRACER
|
||||
T_Config_Init();
|
||||
#endif
|
||||
@@ -733,6 +745,11 @@ int main( int argc, char **argv ) {
|
||||
|
||||
time_manager_finish();
|
||||
|
||||
#ifdef E3_AGENT
|
||||
printf("Destroy E3 Agent\n");
|
||||
e3_destroy();
|
||||
#endif // E3_AGENT
|
||||
|
||||
free(pckg);
|
||||
logClean();
|
||||
printf("Bye.\n");
|
||||
|
||||
@@ -25,6 +25,10 @@
|
||||
#include <stdint.h>
|
||||
#include <openair1/PHY/TOOLS/phy_scope_interface.h>
|
||||
|
||||
#ifdef E3_AGENT
|
||||
#include <openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h>
|
||||
#endif // E3_AGENT
|
||||
|
||||
//#define DEBUG_RXDATA
|
||||
//#define SRS_IND_DEBUG
|
||||
|
||||
@@ -1233,6 +1237,32 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N
|
||||
stop_meas(&gNB->rx_srs_stats);
|
||||
}
|
||||
|
||||
#ifdef E3_AGENT
|
||||
// Spectrum Listening Symbols
|
||||
c16_t **rxdataF_sen = gNB->common_vars.rxdataF[0];
|
||||
if (nr_slot_select(&gNB->gNB_config, frame_rx, slot_rx) == NR_UPLINK_SLOT && slot_rx == 8) {
|
||||
e3_sm_spectrum_control->sampling_counter++;
|
||||
if (e3_sm_spectrum_control->sampling_counter > e3_sm_spectrum_control->sampling_threshold) {
|
||||
const uint16_t n_symbols = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot;
|
||||
// Extracting 12th symbol
|
||||
uint64_t symbol_offset = (n_symbols)*gNB->frame_parms.ofdm_symbol_size + (12) * gNB->frame_parms.ofdm_symbol_size;
|
||||
int32_t *rx_signal = (int32_t *)&rxdataF_sen[0][symbol_offset];
|
||||
|
||||
// for(int i = 0; i < gNB->frame_parms.ofdm_symbol_size; i++){
|
||||
// LOG_D(E3AP, "i = %d, (%d %d) \n",i, ((c16_t*) rx_signal)[i].r, ((c16_t*) rx_signal)[i].i);
|
||||
// }
|
||||
|
||||
T(T_GNB_PHY_UL_FREQ_SENSING_SYMBOL,
|
||||
T_INT(0),
|
||||
T_INT(frame_rx),
|
||||
T_INT(slot_rx),
|
||||
T_INT(0),
|
||||
T_BUFFER(rx_signal, (gNB->frame_parms.ofdm_symbol_size) * sizeof(int32_t)));
|
||||
e3_sm_spectrum_control->sampling_counter = 0;
|
||||
}
|
||||
}
|
||||
#endif // E3_AGENT
|
||||
|
||||
stop_meas(&gNB->phy_proc_rx);
|
||||
|
||||
if (n_pucch > 0 || num_pusch > 0) {
|
||||
|
||||
@@ -5,6 +5,10 @@ add_subdirectory(E1AP)
|
||||
if(E2_AGENT)
|
||||
add_subdirectory(E2AP)
|
||||
endif()
|
||||
if(E3_AGENT)
|
||||
message(STATUS "Add E3 Agent capabilities")
|
||||
add_subdirectory(E3AP)
|
||||
endif()
|
||||
add_subdirectory(F1AP)
|
||||
add_subdirectory(LAYER2)
|
||||
add_subdirectory(M2AP)
|
||||
|
||||
@@ -81,6 +81,12 @@ set(SM_ENCODING_GTP "PLAIN" CACHE STRING "The GTP SM encoding to use")
|
||||
set_property(CACHE SM_ENCODING_GTP PROPERTY STRINGS "PLAIN")
|
||||
message(STATUS "Selected GTP SM_ENCODING: ${SM_ENCODING_GTP}")
|
||||
|
||||
# DAPP Service Model
|
||||
if(E3_AGENT)
|
||||
set(SM_ENCODING_DAPP "ASN" CACHE STRING "The DAPP SM encoding to use")
|
||||
set_property(CACHE SM_ENCODING_DAPP PROPERTY STRINGS "ASN")
|
||||
message(STATUS "Selected DAPP SM_ENCODING: ${SM_ENCODING_DAPP}")
|
||||
endif()
|
||||
|
||||
if(E2AP_VERSION STREQUAL "E2AP_V1")
|
||||
add_library(e2ap_ran_func_obj OBJECT
|
||||
|
||||
@@ -39,6 +39,14 @@ set(UE_ID_COMMON_E2SM_SRCS
|
||||
../flexric/src/sm/rc_sm/ie/asn/UEID-NG-ENB.c
|
||||
)
|
||||
|
||||
if(E3_AGENT)
|
||||
set(DAPP_RAN_FUNC_SRC
|
||||
O-RAN/ran_func_dapp.c
|
||||
O-RAN/ran_func_dapp_subs.c
|
||||
../flexric/src/sm/dapp_sm/ie/dapp_data_ie.c
|
||||
)
|
||||
endif()
|
||||
|
||||
add_library(e2_ran_func_cuup STATIC
|
||||
${UE_ID_COMMON_E2SM_SRCS}
|
||||
init_ran_func.c
|
||||
@@ -58,6 +66,13 @@ add_library(e2_ran_func_cuup STATIC
|
||||
target_link_libraries(e2_ran_func_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj kpm_ric_info_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj ds alg sm_common_enc_asn_obj_rc 3gpp_derived_ie_enc_asn_obj_rc)
|
||||
target_compile_definitions(e2_ran_func_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_CUUP)
|
||||
|
||||
if(E3_AGENT)
|
||||
target_sources(e2_ran_func_cuup PRIVATE ${DAPP_RAN_FUNC_SRC})
|
||||
target_link_libraries(e2_ran_func_cuup PUBLIC e2sm_dapp_ir_obj e3sm_obj)
|
||||
target_include_directories(e2_ran_func_cuup PUBLIC ${OPENAIR_DIR}/openair2/E3AP)
|
||||
target_compile_definitions(e2_ran_func_cuup PUBLIC E3_AGENT)
|
||||
endif()
|
||||
|
||||
|
||||
add_library(e2_ran_func_du_cucp_cuup STATIC
|
||||
${UE_ID_COMMON_E2SM_SRCS}
|
||||
@@ -82,6 +97,13 @@ add_library(e2_ran_func_du_cucp_cuup STATIC
|
||||
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC asn1_nr_rrc nr_rrc asn1_nr_rrc_hdrs e2_time_obj kpm_ric_info_common_obj kpm_ric_info_obj 3gpp_derived_ie_obj e2sm_rc_ir_obj sm_common_ie_obj ds sm_common_enc_asn_obj_rc 3gpp_derived_ie_enc_asn_obj_rc)
|
||||
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KPM_VERSION} NGRAN_GNB_DU NGRAN_GNB_CUCP NGRAN_GNB_CUUP)
|
||||
|
||||
if(E3_AGENT)
|
||||
target_sources(e2_ran_func_du_cucp_cuup PRIVATE ${DAPP_RAN_FUNC_SRC})
|
||||
target_link_libraries(e2_ran_func_du_cucp_cuup PUBLIC e2sm_dapp_ir_obj e3sm_obj)
|
||||
target_include_directories(e2_ran_func_du_cucp_cuup PUBLIC ${OPENAIR_DIR}/openair2/E3AP)
|
||||
target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC E3_AGENT)
|
||||
endif()
|
||||
|
||||
# Current implementation:
|
||||
# - we only have one executable nr-softmodem for 4 node types: DU, CU, gNB-mono, CU-CP
|
||||
|
||||
@@ -90,4 +112,4 @@ target_compile_definitions(e2_ran_func_du_cucp_cuup PUBLIC ${E2AP_VERSION} ${KP
|
||||
# - CU-CP : e2_ran_func_cucp
|
||||
# - CU-UP: e2_ran_func_cuup (this is available at the moment, executable nr-cuup)
|
||||
# - CU: e2_ran_func_cucp + e2_ran_func_cuup
|
||||
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup
|
||||
# - gNB-mono: e2_ran_func_du + e2_ran_func_cucp + e2_ran_func_cuup
|
||||
478
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp.c
Normal file
478
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp.c
Normal file
@@ -0,0 +1,478 @@
|
||||
#include "ran_func_dapp.h"
|
||||
|
||||
static pthread_once_t once_dapp_mutex = PTHREAD_ONCE_INIT;
|
||||
static dapp_subs_data_t dapp_subs_data = {0};
|
||||
|
||||
/**
|
||||
* @brief Create a byte_array_t from a C-string.
|
||||
*
|
||||
* Allocates a new buffer, copies the content of @p s (without NUL terminator),
|
||||
* and returns a byte_array_t owning that buffer.
|
||||
*/
|
||||
static byte_array_t ba_from_cstr(const char* s)
|
||||
{
|
||||
byte_array_t ba = {0};
|
||||
size_t len = strlen(s);
|
||||
ba.buf = malloc(len);
|
||||
assert(ba.buf != NULL && "Memory exhausted");
|
||||
memcpy(ba.buf, s, len);
|
||||
ba.len = len;
|
||||
return ba;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Fill the DAPP RAN function name/description/oid fields.
|
||||
*
|
||||
* Initializes the name, OID, and description of the given DAPP function
|
||||
* definition using constant strings; leaves instance as NULL.
|
||||
*/
|
||||
static void fill_dapp_ran_function_name(e2sm_dapp_func_def_t* def)
|
||||
{
|
||||
assert(def != NULL);
|
||||
|
||||
def->name.name = ba_from_cstr("E2SM-DAPP");
|
||||
|
||||
def->name.oid = ba_from_cstr("1.3.6.1.4.1.53148.1.1.255.3");
|
||||
|
||||
def->name.description = ba_from_cstr("E2SM-DAPP for xApp-dApp synchronization");
|
||||
|
||||
def->name.instance = NULL;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build the DAPP report style definitions.
|
||||
*
|
||||
* Allocates and initializes a ran_func_def_report_dapp_sm_t with three
|
||||
* report styles:
|
||||
* - Style 1 (DAPP-E3-DATA-REPORT): E3 data reports only.
|
||||
* ind_hdr_type=1, ind_msg_type=1.
|
||||
* - Style 2 (DAPP-E3-SUBSCRIPTION-MAP): E3 subscription map only.
|
||||
* ind_hdr_type=2, ind_msg_type=2.
|
||||
*/
|
||||
static ran_func_def_report_dapp_sm_t* make_dapp_report_def(void)
|
||||
{
|
||||
ran_func_def_report_dapp_sm_t* rpt = calloc(1, sizeof(*rpt));
|
||||
assert(rpt != NULL && "Memory exhausted");
|
||||
|
||||
rpt->sz_seq_report_sty = 2;
|
||||
rpt->seq_report_sty = calloc(rpt->sz_seq_report_sty, sizeof(seq_report_sty_dapp_sm_t));
|
||||
assert(rpt->seq_report_sty != NULL && "Memory exhausted");
|
||||
|
||||
/* Style 1: E3 data reports only */
|
||||
rpt->seq_report_sty[0].report_type = DAPP_RIC_STYLE_E3_DATA_REPORT;
|
||||
rpt->seq_report_sty[0].name = ba_from_cstr("DAPP-E3-DATA-REPORT");
|
||||
rpt->seq_report_sty[0].ind_hdr_type = 1;
|
||||
rpt->seq_report_sty[0].ind_msg_type = 1;
|
||||
|
||||
/* Style 2: E3 subscription map only */
|
||||
rpt->seq_report_sty[1].report_type = DAPP_RIC_STYLE_E3_SUBSCRIPTION_MAP;
|
||||
rpt->seq_report_sty[1].name = ba_from_cstr("DAPP-E3-SUBSCRIPTION-MAP");
|
||||
rpt->seq_report_sty[1].ind_hdr_type = 2;
|
||||
rpt->seq_report_sty[1].ind_msg_type = 2;
|
||||
|
||||
return rpt;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build the DAPP control style definition.
|
||||
*
|
||||
* Allocates and initializes a ran_func_def_ctrl_dapp_sm_t with a single
|
||||
* control style (style 1) for the DAPP RAN function definition.
|
||||
*/
|
||||
static ran_func_def_ctrl_dapp_sm_t* make_dapp_ctrl_def(void)
|
||||
{
|
||||
ran_func_def_ctrl_dapp_sm_t* ctrl = calloc(1, sizeof(*ctrl));
|
||||
assert(ctrl != NULL && "Memory exhausted");
|
||||
|
||||
ctrl->sz_seq_ctrl_style = 1;
|
||||
ctrl->seq_ctrl_style = calloc(ctrl->sz_seq_ctrl_style, sizeof(seq_ctrl_style_dapp_sm_t));
|
||||
assert(ctrl->seq_ctrl_style != NULL && "Memory exhausted");
|
||||
|
||||
seq_ctrl_style_dapp_sm_t* s = &ctrl->seq_ctrl_style[0];
|
||||
|
||||
s->style_type = DAPP_RIC_STYLE_E3_DATA_REPORT;
|
||||
s->name = ba_from_cstr("DAPP-CONTROL-STYLE-1");
|
||||
s->hdr = 1;
|
||||
s->msg = 1;
|
||||
s->out_frmt = 1;
|
||||
|
||||
return ctrl;
|
||||
}
|
||||
|
||||
static void init_once_dapp(void)
|
||||
{
|
||||
init_dapp_subs_data(&dapp_subs_data);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build a dapp_e3_subscription_list_t from the current E3 subscription map.
|
||||
*
|
||||
* Queries the E3 agent for the current dApp subscription map, filters
|
||||
* out dApps with zero subscriptions, and returns a heap-allocated
|
||||
* subscription list. Returns NULL if no valid subscriptions exist.
|
||||
*
|
||||
* Caller takes ownership of the returned pointer and must free it
|
||||
* via free_dapp_e3_subscription_list() + free().
|
||||
*/
|
||||
static dapp_e3_subscription_list_t* build_dapp_e3_subs_from_map(void)
|
||||
{
|
||||
e3_dapp_subscription_map_t map = e3_get_dapp_subscription_map();
|
||||
|
||||
size_t valid_count = 0;
|
||||
for (size_t i = 0; i < map.num_dapps; i++) {
|
||||
if (map.dapps[i].num_e3_ran_funcs > 0) {
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
|
||||
if (valid_count == 0) {
|
||||
e3_free_dapp_subscription_map(&map);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
dapp_e3_subscription_list_t* subs = calloc(1, sizeof(dapp_e3_subscription_list_t));
|
||||
assert(subs != NULL && "Memory exhausted");
|
||||
subs->sz_dapp_e3_subscriptions = valid_count;
|
||||
subs->dapp_e3_subscriptions = calloc(valid_count, sizeof(dapp_e3_subscription_item_t));
|
||||
assert(subs->dapp_e3_subscriptions != NULL && "Memory exhausted");
|
||||
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < map.num_dapps; i++) {
|
||||
if (map.dapps[i].num_e3_ran_funcs == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
subs->dapp_e3_subscriptions[idx].dapp_id = map.dapps[i].dapp_id;
|
||||
subs->dapp_e3_subscriptions[idx].sz_subscribed_e3_ran_functions = map.dapps[i].num_e3_ran_funcs;
|
||||
|
||||
size_t sz = map.dapps[i].num_e3_ran_funcs * sizeof(uint32_t);
|
||||
subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions = malloc(sz);
|
||||
assert(subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions != NULL && "Memory exhausted");
|
||||
memcpy(subs->dapp_e3_subscriptions[idx].subscribed_e3_ran_functions, map.dapps[i].e3_ran_func_ids, sz);
|
||||
idx++;
|
||||
}
|
||||
|
||||
e3_free_dapp_subscription_map(&map);
|
||||
return subs;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Build the complete DAPP RAN function definition.
|
||||
*
|
||||
* Fills the RAN function name, event trigger, report styles, and control
|
||||
* styles. The current dApp E3 subscription map is attached to:
|
||||
* - Report style 1 (E3 data reports)
|
||||
* - Control style 1
|
||||
*
|
||||
* Report style 2 (subscription map management) does not carry subscriptions.
|
||||
*/
|
||||
static e2sm_dapp_func_def_t fill_dapp_ran_def(void)
|
||||
{
|
||||
e2sm_dapp_func_def_t def = (e2sm_dapp_func_def_t){0};
|
||||
|
||||
fill_dapp_ran_function_name(&def);
|
||||
|
||||
def.ev_trig = NULL;
|
||||
|
||||
def.report = make_dapp_report_def();
|
||||
|
||||
def.ctrl = make_dapp_ctrl_def();
|
||||
|
||||
dapp_e3_subscription_list_t* subs = build_dapp_e3_subs_from_map();
|
||||
|
||||
if (subs != NULL) {
|
||||
assert(def.report != NULL && def.report->sz_seq_report_sty >= 1);
|
||||
def.report->seq_report_sty[0].dapp_e3_subs = subs;
|
||||
|
||||
assert(def.ctrl != NULL && def.ctrl->sz_seq_ctrl_style >= 1);
|
||||
def.ctrl->seq_ctrl_style[0].dapp_e3_subs = calloc(1, sizeof(dapp_e3_subscription_list_t));
|
||||
assert(def.ctrl->seq_ctrl_style[0].dapp_e3_subs != NULL && "Memory exhausted");
|
||||
*def.ctrl->seq_ctrl_style[0].dapp_e3_subs = cp_dapp_e3_subscription_list(subs);
|
||||
}
|
||||
|
||||
return def;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Populate the DAPP RAN function definition during E2 setup.
|
||||
*
|
||||
* This is called by the E2 agent when building the DAPP RAN function
|
||||
* setup structure. It:
|
||||
* - Fills dapp->ran_func_def with the DAPP RAN function definition.
|
||||
* - Ensures global DAPP subscription state is initialized once.
|
||||
*/
|
||||
void read_dapp_setup_sm(void* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
dapp_e2_setup_t* dapp = (dapp_e2_setup_t*)data;
|
||||
|
||||
dapp->ran_func_def = fill_dapp_ran_def();
|
||||
|
||||
const int ret = pthread_once(&once_dapp_mutex, init_once_dapp);
|
||||
DevAssert(ret == 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Send the dApp E3 subscription map to all Format-2 subscribers.
|
||||
*
|
||||
* Queries the current dApp E3 subscription map, wraps it in an
|
||||
* E2SM-DAPP IndicationHeader (format 2) + IndicationMessage (format 2),
|
||||
* and pushes one indication per registered Format-2 RIC subscription.
|
||||
*/
|
||||
static void generate_e2_indication_dapp_e3_subscriptions(void)
|
||||
{
|
||||
uint32_t* ric_ids = NULL;
|
||||
size_t count = ric_subs_frmt2_snapshot(&ric_ids);
|
||||
if (count == 0 || ric_ids == NULL) {
|
||||
return;
|
||||
}
|
||||
e3_dapp_subscription_map_t map = e3_get_dapp_subscription_map();
|
||||
|
||||
size_t valid_count = 0;
|
||||
for (size_t i = 0; i < map.num_dapps; i++) {
|
||||
if (map.dapps[i].num_e3_ran_funcs > 0) {
|
||||
valid_count++;
|
||||
}
|
||||
}
|
||||
|
||||
const f1ap_setup_req_t* f1_req = RC.nrmac[0]->f1_config.setup_req;
|
||||
const f1ap_served_cell_info_t* cell = (f1_req && f1_req->num_cells_available > 0) ? &f1_req->cell[0].info : NULL;
|
||||
|
||||
for (size_t s = 0; s < count; ++s) {
|
||||
dapp_ind_data_t* ind = calloc(1, sizeof(*ind));
|
||||
assert(ind != NULL);
|
||||
|
||||
// Header format 2: node identity only
|
||||
ind->hdr.format = FORMAT_2_E2SM_DAPP_IND_HDR;
|
||||
e2sm_dapp_ind_hdr_frmt_2_t* hdr = &ind->hdr.frmt_2;
|
||||
|
||||
if (f1_req != NULL && cell != NULL) {
|
||||
hdr->node_type = (uint8_t)RC.nrrrc[0]->node_type;
|
||||
hdr->node_nb_id = (uint32_t)(cell->nr_cellid >> 14);
|
||||
|
||||
hdr->node_plmn_id[0] = (uint8_t)(cell->plmn.mcc >> 8);
|
||||
hdr->node_plmn_id[1] = (uint8_t)(cell->plmn.mcc & 0xFF);
|
||||
hdr->node_plmn_id[2] = (uint8_t)(cell->plmn.mnc & 0xFF);
|
||||
|
||||
hdr->node_cu_du_id_present = true;
|
||||
hdr->node_cu_du_id = f1_req->gNB_DU_id;
|
||||
}
|
||||
|
||||
// Message format 2: subscription map
|
||||
ind->msg.format = FORMAT_2_E2SM_DAPP_IND_MSG;
|
||||
e2sm_dapp_ind_msg_frmt_2_t* msg = &ind->msg.frmt_2;
|
||||
|
||||
msg->dapp_e3_subs.sz_dapp_e3_subscriptions = valid_count;
|
||||
msg->dapp_e3_subs.dapp_e3_subscriptions = valid_count > 0 ? calloc(valid_count, sizeof(dapp_e3_subscription_item_t)) : NULL;
|
||||
|
||||
size_t idx = 0;
|
||||
for (size_t i = 0; i < map.num_dapps; i++) {
|
||||
if (map.dapps[i].num_e3_ran_funcs == 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
dapp_e3_subscription_item_t* dst = &msg->dapp_e3_subs.dapp_e3_subscriptions[idx];
|
||||
dst->dapp_id = map.dapps[i].dapp_id;
|
||||
dst->sz_subscribed_e3_ran_functions = map.dapps[i].num_e3_ran_funcs;
|
||||
|
||||
size_t sz = map.dapps[i].num_e3_ran_funcs * sizeof(uint32_t);
|
||||
dst->subscribed_e3_ran_functions = malloc(sz);
|
||||
assert(dst->subscribed_e3_ran_functions != NULL);
|
||||
memcpy(dst->subscribed_e3_ran_functions, map.dapps[i].e3_ran_func_ids, sz);
|
||||
|
||||
idx++;
|
||||
}
|
||||
|
||||
/* No E3 payload for format 2 */
|
||||
ind->e3.type = DAPP_E3_SM_NONE;
|
||||
|
||||
async_event_agent_api(ric_ids[s], ind);
|
||||
}
|
||||
|
||||
e3_free_dapp_subscription_map(&map);
|
||||
free(ric_ids);
|
||||
}
|
||||
|
||||
void notify_dapp_status_changed(void)
|
||||
{
|
||||
trigger_ric_service_update_api();
|
||||
generate_e2_indication_dapp_e3_subscriptions();
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Convert an E3 DAPP report into one or more E2 indications.
|
||||
*
|
||||
* For each RIC subscription currently registered:
|
||||
* - Allocates a dapp_ind_data_t,
|
||||
* - Fills the E2SM-DAPP IndicationHeader (RAN function ID, dApp ID),
|
||||
* - Wraps the E3 report bytes in the IndicationMessage format 1 payload,
|
||||
* - Enqueues the indication on the FlexRIC async path.
|
||||
*
|
||||
* If @p report_data is NULL or @p report_size == 0, it returns immediately.
|
||||
*/
|
||||
void generate_e2_indication_from_e3_dapp_report(uint32_t ran_function_id,
|
||||
uint32_t dapp_id,
|
||||
size_t report_size,
|
||||
const uint8_t* report_data)
|
||||
{
|
||||
if (report_data == NULL || report_size == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
uint32_t* ric_ids = NULL;
|
||||
size_t count = ric_subs_frmt1_snapshot(&ric_ids);
|
||||
if (count == 0 || ric_ids == NULL) {
|
||||
return;
|
||||
}
|
||||
|
||||
const f1ap_setup_req_t* f1_req = RC.nrmac[0]->f1_config.setup_req;
|
||||
const f1ap_served_cell_info_t* cell = (f1_req && f1_req->num_cells_available > 0) ? &f1_req->cell[0].info : NULL;
|
||||
|
||||
for (size_t i = 0; i < count; ++i) {
|
||||
uint32_t ric_req_id = ric_ids[i];
|
||||
|
||||
dapp_ind_data_t* ind = calloc(1, sizeof(*ind));
|
||||
assert(ind);
|
||||
|
||||
ind->hdr.format = FORMAT_1_E2SM_DAPP_IND_HDR;
|
||||
e2sm_dapp_ind_hdr_frmt_1_t* hdr = &ind->hdr.frmt_1;
|
||||
hdr->ran_function_id = ran_function_id;
|
||||
hdr->dapp_id = dapp_id;
|
||||
|
||||
if (f1_req != NULL && cell != NULL) {
|
||||
hdr->node_type = (uint8_t)RC.nrrrc[0]->node_type;
|
||||
hdr->node_nb_id = (uint32_t)(cell->nr_cellid >> 14);
|
||||
|
||||
hdr->node_plmn_id[0] = (uint8_t)(cell->plmn.mcc >> 8);
|
||||
hdr->node_plmn_id[1] = (uint8_t)(cell->plmn.mcc & 0xFF);
|
||||
hdr->node_plmn_id[2] = (uint8_t)(cell->plmn.mnc & 0xFF);
|
||||
|
||||
hdr->node_cu_du_id_present = true;
|
||||
hdr->node_cu_du_id = f1_req->gNB_DU_id;
|
||||
}
|
||||
|
||||
ind->msg.format = FORMAT_1_E2SM_DAPP_IND_MSG;
|
||||
e2sm_dapp_ind_msg_frmt_1_t* msg = &ind->msg.frmt_1;
|
||||
msg->data_size = report_size;
|
||||
msg->data = malloc(report_size);
|
||||
assert(msg->data != NULL);
|
||||
memcpy(msg->data, report_data, report_size);
|
||||
|
||||
// Push to FlexRIC async path
|
||||
async_event_agent_api(ric_req_id, ind);
|
||||
}
|
||||
|
||||
free(ric_ids);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Callback invoked when an aperiodic subscription is released.
|
||||
*
|
||||
* Removes the given RIC request ID from the internal DAPP subscription
|
||||
* bookkeeping.
|
||||
*/
|
||||
static void free_aperiodic_subscription(uint32_t ric_req_id)
|
||||
{
|
||||
remove_dapp_subs_data(&dapp_subs_data, ric_req_id);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle DAPP SM subscription requests at the agent.
|
||||
*
|
||||
* Called when the RIC installs a subscription for the DAPP SM. For
|
||||
* - Stores the RIC request ID in DAPP subscription data,
|
||||
* - Registers the RIC subscription,
|
||||
* - Returns an aperiodic subscription outcome with a custom free callback.
|
||||
* - Supported ActionDefinition formats (currently FORMAT_1 only), it:
|
||||
*/
|
||||
sm_ag_if_ans_t write_subs_dapp_sm(void const* src)
|
||||
{
|
||||
assert(src != NULL);
|
||||
wr_dapp_sub_data_t* wr_dapp = (wr_dapp_sub_data_t*)src;
|
||||
|
||||
sm_ag_if_ans_t ans = {0};
|
||||
const uint32_t ric_req_id = wr_dapp->ric_req_id;
|
||||
assert(wr_dapp->dapp.action_def != NULL && "Action definition required");
|
||||
|
||||
const uint32_t style = wr_dapp->dapp.action_def->ric_style_type;
|
||||
|
||||
switch (style) {
|
||||
case DAPP_RIC_STYLE_E3_DATA_REPORT:
|
||||
/* E3 data reports only */
|
||||
insert_frmt_1_ric_id(&dapp_subs_data, ric_req_id);
|
||||
ric_subs_frmt1_add(ric_req_id);
|
||||
break;
|
||||
|
||||
case DAPP_RIC_STYLE_E3_SUBSCRIPTION_MAP:
|
||||
/* E3 subscription map only */
|
||||
insert_frmt_2_ric_id(&dapp_subs_data, ric_req_id);
|
||||
ric_subs_frmt2_add(ric_req_id);
|
||||
break;
|
||||
|
||||
default:
|
||||
AssertError(false, return ans, "[RAN FUNC DAPP SUBS] Unsupported Action Definition format %d\n", style);
|
||||
}
|
||||
|
||||
ans.type = SUBS_OUTCOME_SM_AG_IF_ANS_V0;
|
||||
ans.subs_out.type = APERIODIC_SUBSCRIPTION_FLRC;
|
||||
ans.subs_out.aper.free_aper_subs = free_aperiodic_subscription;
|
||||
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Handle DAPP SM control requests at the agent.
|
||||
*
|
||||
* For format-1 E2SM-DAPP ControlMessages, and when compiled with E3_AGENT:
|
||||
* - Extracts RAN function ID, dApp ID, and control payload,
|
||||
* - Forwards it to the E3 agent via libe3 C API wrapper.
|
||||
*
|
||||
* Always returns a control outcome of type DAPP_AGENT_IF_CTRL_ANS_V0.
|
||||
*/
|
||||
sm_ag_if_ans_t write_ctrl_dapp_sm(void const* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
|
||||
dapp_ctrl_req_data_t const* ctrl = (dapp_ctrl_req_data_t const*)data;
|
||||
sm_ag_if_ans_t ans = {0};
|
||||
|
||||
switch (ctrl->msg.format) {
|
||||
case FORMAT_1_E2SM_DAPP_CTRL_MSG: {
|
||||
#if defined(E3_AGENT)
|
||||
uint32_t ran_function_id = ctrl->hdr.frmt_1.ran_function_id;
|
||||
uint32_t dapp_id = ctrl->hdr.frmt_1.dapp_id;
|
||||
|
||||
uint32_t data_size = ctrl->msg.frmt_1.data_size;
|
||||
const uint8_t* payload = ctrl->msg.frmt_1.data;
|
||||
|
||||
if (e3_send_xapp_control(dapp_id, ran_function_id, payload, data_size) != 0) {
|
||||
printf("[RAN FUNC DAPP CTRL] Failed to forward xApp control via E3 agent\n");
|
||||
}
|
||||
#endif
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
AssertError(false, return ans, "[RAN FUNC DAPP CTRL] Unsupported Control Message format %d", ctrl->msg.format);
|
||||
}
|
||||
|
||||
ans.type = CTRL_OUTCOME_SM_AG_IF_ANS_V0;
|
||||
ans.ctrl_out.type = DAPP_AGENT_IF_CTRL_ANS_V0;
|
||||
return ans;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Read DAPP SM state from the agent (not implemented).
|
||||
*
|
||||
* Placeholder for a future read operation for the DAPP SM. Currently it
|
||||
* asserts unconditionally and returns true to satisfy the interface.
|
||||
*/
|
||||
bool read_dapp_sm(void* data)
|
||||
{
|
||||
assert(data != NULL);
|
||||
assert(0 != 0 && "Not implemented");
|
||||
|
||||
return true;
|
||||
}
|
||||
80
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp.h
Normal file
80
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp.h
Normal file
@@ -0,0 +1,80 @@
|
||||
#ifndef RAN_FUNC_SM_DAPP_READ_WRITE_AGENT_H
|
||||
#define RAN_FUNC_SM_DAPP_READ_WRITE_AGENT_H
|
||||
|
||||
#include "openair2/E2AP/flexric/src/agent/../sm/sm_io.h"
|
||||
#include "ran_func_dapp_subs.h"
|
||||
#include "ran_func_dapp_extern.h"
|
||||
#include "ran_e2sm_ue_id.h"
|
||||
#include "../../flexric/src/agent/e2_agent_api.h"
|
||||
|
||||
#include <stdio.h>
|
||||
#include <unistd.h>
|
||||
#include <string.h>
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#include "common/ran_context.h"
|
||||
|
||||
#if defined(E3_AGENT)
|
||||
#include "e3_agent.h"
|
||||
#include <endian.h>
|
||||
#include "openair2/LAYER2/NR_MAC_gNB/nr_mac_gNB.h"
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Populate the DAPP RAN function definition during E2 setup.
|
||||
*
|
||||
* Called by the E2 agent when building the E2 SETUP REQUEST.
|
||||
* Fills the RAN function definition with:
|
||||
* - RAN function name, OID, and description
|
||||
* - Three report styles:
|
||||
* Style 1 (DAPP-ALL): registers for both E3 data reports and subscription map
|
||||
* Style 2 (DAPP-E3-DATA-REPORT): E3 data reports only (ind format 1)
|
||||
* Style 3 (DAPP-E3-SUBSCRIPTION-MAP): subscription map only (ind format 2)
|
||||
* - One control style (format 1)
|
||||
* - Current dApp E3 subscription map (if any dApps are connected)
|
||||
* Also initializes the global DAPP subscription state (once).
|
||||
*
|
||||
* @param data Pointer to a dapp_e2_setup_t to be filled.
|
||||
*/
|
||||
void read_dapp_setup_sm(void* data);
|
||||
|
||||
/**
|
||||
* @brief Handle DAPP SM subscription requests at the agent.
|
||||
*
|
||||
* Routes the subscription into the appropriate indication lists based on
|
||||
* the report style type in the action definition:
|
||||
* - Style 1: registers for both Format 1 and Format 2 indications
|
||||
* - Style 2: registers for Format 1 indications only (E3 data reports)
|
||||
* - Style 3: registers for Format 2 indications only (subscription map)
|
||||
*
|
||||
* Returns an aperiodic subscription outcome.
|
||||
*
|
||||
* @param src Pointer to a wr_dapp_sub_data_t containing the subscription request.
|
||||
* @return Subscription outcome (APERIODIC_SUBSCRIPTION_FLRC).
|
||||
*/
|
||||
sm_ag_if_ans_t write_subs_dapp_sm(void const* src);
|
||||
|
||||
/**
|
||||
* @brief Handle DAPP SM control requests at the agent.
|
||||
*
|
||||
* For Format 1 control messages (when compiled with E3_AGENT):
|
||||
* - Extracts RAN function ID, dApp ID, and control payload
|
||||
* - Forwards the control to the E3 agent via e3_send_xapp_control()
|
||||
*
|
||||
* @param data Pointer to a dapp_ctrl_req_data_t containing the control request.
|
||||
* @return Control outcome of type DAPP_AGENT_IF_CTRL_ANS_V0.
|
||||
*/
|
||||
sm_ag_if_ans_t write_ctrl_dapp_sm(void const* data);
|
||||
|
||||
/**
|
||||
* @brief Read DAPP SM state from the agent (not implemented).
|
||||
*
|
||||
* Placeholder — asserts unconditionally if called.
|
||||
*
|
||||
* @param data Unused.
|
||||
* @return Never returns (asserts).
|
||||
*/
|
||||
bool read_dapp_sm(void*);
|
||||
|
||||
#endif
|
||||
18
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_extern.h
Normal file
18
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_extern.h
Normal file
@@ -0,0 +1,18 @@
|
||||
#ifndef RAN_FUNC_SM_DAPP_EXTERN_AGENT_H
|
||||
#define RAN_FUNC_SM_DAPP_EXTERN_AGENT_H
|
||||
|
||||
#include "openair2/RRC/NR/nr_rrc_defs.h"
|
||||
#if defined(E3_AGENT)
|
||||
#include "e3_agent.h"
|
||||
#include <endian.h>
|
||||
#include <string.h>
|
||||
#endif
|
||||
|
||||
void generate_e2_indication_from_e3_dapp_report(uint32_t ran_function_id,
|
||||
uint32_t dapp_id,
|
||||
size_t report_size,
|
||||
const uint8_t *report_data);
|
||||
|
||||
void notify_dapp_status_changed(void);
|
||||
|
||||
#endif
|
||||
241
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_subs.c
Normal file
241
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_subs.c
Normal file
@@ -0,0 +1,241 @@
|
||||
#include "ran_func_dapp_subs.h"
|
||||
#include "common/utils/assertions.h"
|
||||
#include "common/utils/alg/find.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <pthread.h>
|
||||
|
||||
/** @brief Mutex protecting all per-SM dapp_subs_data_t operations. */
|
||||
static pthread_mutex_t dapp_mutex = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/** @brief Mutex protecting the global Format 1 and Format 2 registries. */
|
||||
static pthread_mutex_t g_map_mtx = PTHREAD_MUTEX_INITIALIZER;
|
||||
|
||||
/** @brief Global list of RIC request IDs subscribed to Format 1 (E3 data reports). */
|
||||
static seq_arr_t g_all_frmt1_subs;
|
||||
|
||||
/** @brief Global list of RIC request IDs subscribed to Format 2 (subscription map). */
|
||||
static seq_arr_t g_all_frmt2_subs;
|
||||
|
||||
/** @brief Guard for one-time initialization of the global lists. */
|
||||
static bool g_map_inited = false;
|
||||
|
||||
/* ── Helpers ── */
|
||||
|
||||
/**
|
||||
* @brief Predicate: compare two uint32_t values for equality.
|
||||
*
|
||||
* Used with find_if() to locate a RIC request ID in a seq_arr_t.
|
||||
*/
|
||||
static bool eq_uint32(const void *value, const void *it)
|
||||
{
|
||||
return *(const uint32_t *)value == *(const uint32_t *)it;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Check whether a seq_arr_t contains a given uint32_t value.
|
||||
*
|
||||
* @param s The sequence to search.
|
||||
* @param v The value to look for.
|
||||
* @return true if found, false otherwise.
|
||||
*/
|
||||
static bool seq_contains(const seq_arr_t *s, uint32_t v)
|
||||
{
|
||||
for (void *it = seq_arr_front((seq_arr_t *)s); it != seq_arr_end((seq_arr_t *)s); it = seq_arr_next(s, it))
|
||||
if (*(uint32_t *)it == v)
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Append a uint32_t to a seq_arr_t if not already present.
|
||||
*
|
||||
* @param s The sequence to modify.
|
||||
* @param v The value to insert.
|
||||
*/
|
||||
static void seq_push_unique(seq_arr_t *s, uint32_t v)
|
||||
{
|
||||
if (!seq_contains(s, v))
|
||||
seq_arr_push_back(s, &v, sizeof(v));
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Remove the first occurrence of a uint32_t from a seq_arr_t.
|
||||
*
|
||||
* @param s The sequence to modify.
|
||||
* @param v The value to remove.
|
||||
* @return true if an element was removed, false if not found.
|
||||
*/
|
||||
static bool seq_remove_first(seq_arr_t *s, uint32_t v)
|
||||
{
|
||||
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it)) {
|
||||
if (*(uint32_t *)it == v) {
|
||||
seq_arr_erase(s, it);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Copy all uint32_t elements from a seq_arr_t into a heap-allocated array.
|
||||
*
|
||||
* Must be called while g_map_mtx is held. Sets *out to NULL and returns 0
|
||||
* if the sequence is empty or allocation fails.
|
||||
*
|
||||
* @param s The sequence to snapshot.
|
||||
* @param[out] out Set to the allocated array of IDs.
|
||||
* @return Number of elements copied.
|
||||
*/
|
||||
static size_t seq_snapshot_locked(seq_arr_t *s, uint32_t **out)
|
||||
{
|
||||
size_t n = 0;
|
||||
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it))
|
||||
++n;
|
||||
|
||||
if (n == 0) {
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t *ids = malloc(n * sizeof(*ids));
|
||||
if (ids == NULL) {
|
||||
*out = NULL;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
for (void *it = seq_arr_front(s); it != seq_arr_end(s); it = seq_arr_next(s, it))
|
||||
ids[i++] = *(uint32_t *)it;
|
||||
|
||||
*out = ids;
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Lazily initialize both global subscription lists.
|
||||
*
|
||||
* Called under g_map_mtx before any global list operation.
|
||||
*/
|
||||
static void subs_init_once(void)
|
||||
{
|
||||
if (!g_map_inited) {
|
||||
seq_arr_init(&g_all_frmt1_subs, sizeof(uint32_t));
|
||||
seq_arr_init(&g_all_frmt2_subs, sizeof(uint32_t));
|
||||
g_map_inited = true;
|
||||
}
|
||||
}
|
||||
|
||||
/* ── Per-SM init / remove ── */
|
||||
|
||||
void init_dapp_subs_data(dapp_subs_data_t *dapp_subs_data)
|
||||
{
|
||||
pthread_mutex_lock(&dapp_mutex);
|
||||
seq_arr_init(&dapp_subs_data->frmt_1_subs, sizeof(uint32_t));
|
||||
seq_arr_init(&dapp_subs_data->frmt_2_subs, sizeof(uint32_t));
|
||||
pthread_mutex_unlock(&dapp_mutex);
|
||||
}
|
||||
|
||||
void remove_dapp_subs_data(dapp_subs_data_t *dapp_subs_data, uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&dapp_mutex);
|
||||
|
||||
elm_arr_t elm_f1 = find_if(&dapp_subs_data->frmt_1_subs, &ric_req_id, eq_uint32);
|
||||
const bool had_f1 = (elm_f1.it != NULL);
|
||||
|
||||
elm_arr_t elm_f2 = find_if(&dapp_subs_data->frmt_2_subs, &ric_req_id, eq_uint32);
|
||||
const bool had_f2 = (elm_f2.it != NULL);
|
||||
|
||||
if (had_f1)
|
||||
seq_arr_erase(&dapp_subs_data->frmt_1_subs, elm_f1.it);
|
||||
if (had_f2)
|
||||
seq_arr_erase(&dapp_subs_data->frmt_2_subs, elm_f2.it);
|
||||
|
||||
if (!had_f1 && !had_f2)
|
||||
printf("[E2 AGENT][WARN] Tried to remove unknown RIC request ID: %u\n", ric_req_id);
|
||||
|
||||
pthread_mutex_unlock(&dapp_mutex);
|
||||
|
||||
if (had_f1)
|
||||
ric_subs_frmt1_remove(ric_req_id);
|
||||
if (had_f2)
|
||||
ric_subs_frmt2_remove(ric_req_id);
|
||||
}
|
||||
|
||||
/* ── Per-SM insert ── */
|
||||
|
||||
void insert_frmt_1_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&dapp_mutex);
|
||||
seq_arr_push_back(&d->frmt_1_subs, &ric_req_id, sizeof(ric_req_id));
|
||||
pthread_mutex_unlock(&dapp_mutex);
|
||||
}
|
||||
|
||||
void insert_frmt_2_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&dapp_mutex);
|
||||
seq_arr_push_back(&d->frmt_2_subs, &ric_req_id, sizeof(ric_req_id));
|
||||
pthread_mutex_unlock(&dapp_mutex);
|
||||
}
|
||||
|
||||
/* ── Global Format 1 ── */
|
||||
|
||||
void ric_subs_frmt1_add(uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
seq_push_unique(&g_all_frmt1_subs, ric_req_id);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
}
|
||||
|
||||
size_t ric_subs_frmt1_snapshot(uint32_t **out)
|
||||
{
|
||||
assert(out != NULL);
|
||||
*out = NULL;
|
||||
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
size_t n = seq_snapshot_locked(&g_all_frmt1_subs, out);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void ric_subs_frmt1_remove(uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
(void)seq_remove_first(&g_all_frmt1_subs, ric_req_id);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
}
|
||||
|
||||
/* ── Global Format 2 ── */
|
||||
|
||||
void ric_subs_frmt2_add(uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
seq_push_unique(&g_all_frmt2_subs, ric_req_id);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
}
|
||||
|
||||
size_t ric_subs_frmt2_snapshot(uint32_t **out)
|
||||
{
|
||||
assert(out != NULL);
|
||||
*out = NULL;
|
||||
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
size_t n = seq_snapshot_locked(&g_all_frmt2_subs, out);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
|
||||
return n;
|
||||
}
|
||||
|
||||
void ric_subs_frmt2_remove(uint32_t ric_req_id)
|
||||
{
|
||||
pthread_mutex_lock(&g_map_mtx);
|
||||
subs_init_once();
|
||||
(void)seq_remove_first(&g_all_frmt2_subs, ric_req_id);
|
||||
pthread_mutex_unlock(&g_map_mtx);
|
||||
}
|
||||
114
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_subs.h
Normal file
114
openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_subs.h
Normal file
@@ -0,0 +1,114 @@
|
||||
#ifndef RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
|
||||
#define RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
|
||||
|
||||
#include "openair2/E2AP/flexric/src/sm/dapp_sm/ie/dapp_data_ie.h"
|
||||
#include "common/utils/ds/seq_arr.h"
|
||||
|
||||
/**
|
||||
* @brief Per-SM subscription bookkeeping for the DAPP service model.
|
||||
*
|
||||
* Tracks which RIC request IDs are registered for each indication format.
|
||||
* Used by the DAPP agent to remove subscriptions when the RIC deletes them.
|
||||
*/
|
||||
typedef struct {
|
||||
seq_arr_t frmt_1_subs; /**< RIC request IDs subscribed to Format 1 (E3 data reports) */
|
||||
seq_arr_t frmt_2_subs; /**< RIC request IDs subscribed to Format 2 (E3 subscription map) */
|
||||
} dapp_subs_data_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize both per-SM subscription lists.
|
||||
*
|
||||
* Must be called once before any insert/remove operations.
|
||||
*
|
||||
* @param dapp_subs_data Pointer to the per-SM subscription state.
|
||||
*/
|
||||
void init_dapp_subs_data(dapp_subs_data_t *dapp_subs_data);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from all per-SM lists and global registries.
|
||||
*
|
||||
* Searches both frmt_1_subs and frmt_2_subs for @p ric_req_id.
|
||||
* For each list where it is found, erases it locally and calls the
|
||||
* corresponding global remove function. Logs a warning if the ID
|
||||
* is not found in either list.
|
||||
*
|
||||
* @param dapp_subs_data Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to remove.
|
||||
*/
|
||||
void remove_dapp_subs_data(dapp_subs_data_t *dapp_subs_data, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Record a RIC request ID in the per-SM Format 1 list.
|
||||
*
|
||||
* @param d Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to insert.
|
||||
*/
|
||||
void insert_frmt_1_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Add a RIC request ID to the global Format 1 registry.
|
||||
*
|
||||
* The global registry is used by generate_e2_indication_from_e3_dapp_report()
|
||||
* to find all subscribers that should receive E3 data report indications.
|
||||
* Duplicates are silently ignored.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to register.
|
||||
*/
|
||||
void ric_subs_frmt1_add(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from the global Format 1 registry.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to unregister.
|
||||
*/
|
||||
void ric_subs_frmt1_remove(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Snapshot all RIC request IDs in the global Format 1 registry.
|
||||
*
|
||||
* Allocates and returns a caller-owned array of RIC request IDs.
|
||||
* The caller must free() the returned array.
|
||||
*
|
||||
* @param[out] out Set to the allocated array, or NULL if empty.
|
||||
* @return Number of IDs in the snapshot (0 if none).
|
||||
*/
|
||||
size_t ric_subs_frmt1_snapshot(uint32_t **out);
|
||||
|
||||
/**
|
||||
* @brief Record a RIC request ID in the per-SM Format 2 list.
|
||||
*
|
||||
* @param d Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to insert.
|
||||
*/
|
||||
void insert_frmt_2_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Add a RIC request ID to the global Format 2 registry.
|
||||
*
|
||||
* The global registry is used by generate_e2_indication_dapp_e3_subscriptions()
|
||||
* to find all subscribers that should receive subscription map indications.
|
||||
* Duplicates are silently ignored.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to register.
|
||||
*/
|
||||
void ric_subs_frmt2_add(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from the global Format 2 registry.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to unregister.
|
||||
*/
|
||||
void ric_subs_frmt2_remove(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Snapshot all RIC request IDs in the global Format 2 registry.
|
||||
*
|
||||
* Allocates and returns a caller-owned array of RIC request IDs.
|
||||
* The caller must free() the returned array.
|
||||
*
|
||||
* @param[out] out Set to the allocated array, or NULL if empty.
|
||||
* @return Number of IDs in the snapshot (0 if none).
|
||||
*/
|
||||
size_t ric_subs_frmt2_snapshot(uint32_t **out);
|
||||
|
||||
#endif
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "O-RAN/ran_func_kpm.h"
|
||||
#include "O-RAN/ran_func_rc.h"
|
||||
|
||||
#ifdef E3_AGENT
|
||||
#include "O-RAN/ran_func_dapp.h"
|
||||
#endif
|
||||
|
||||
static
|
||||
void init_read_ind_tbl(read_ind_fp (*read_ind_tbl)[SM_AGENT_IF_READ_V0_END])
|
||||
{
|
||||
@@ -36,6 +40,9 @@ void init_read_ind_tbl(read_ind_fp (*read_ind_tbl)[SM_AGENT_IF_READ_V0_END])
|
||||
|
||||
(*read_ind_tbl)[KPM_STATS_V3_0] = read_kpm_sm ;
|
||||
(*read_ind_tbl)[RAN_CTRL_STATS_V1_03] = read_rc_sm;
|
||||
#ifdef E3_AGENT
|
||||
(*read_ind_tbl)[DAPP_STATS_V0] = read_dapp_sm;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
@@ -54,6 +61,9 @@ void init_read_setup_tbl(read_e2_setup_fp (*read_setup_tbl)[SM_AGENT_IF_E2_SETUP
|
||||
|
||||
(*read_setup_tbl)[KPM_V3_0_AGENT_IF_E2_SETUP_ANS_V0] = read_kpm_setup_sm ;
|
||||
(*read_setup_tbl)[RAN_CTRL_V1_3_AGENT_IF_E2_SETUP_ANS_V0] = read_rc_setup_sm;
|
||||
#ifdef E3_AGENT
|
||||
(*read_setup_tbl)[DAPP_AGENT_IF_E2_SETUP_ANS_V0] = read_dapp_setup_sm;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
@@ -71,6 +81,9 @@ void init_write_ctrl( write_ctrl_fp (*write_ctrl_tbl)[SM_AGENT_IF_WRITE_CTRL_V0_
|
||||
#endif
|
||||
|
||||
(*write_ctrl_tbl)[RAN_CONTROL_CTRL_V1_03] = write_ctrl_rc_sm;
|
||||
#ifdef E3_AGENT
|
||||
(*write_ctrl_tbl)[DAPP_CTRL_V0] = write_ctrl_dapp_sm;
|
||||
#endif
|
||||
}
|
||||
|
||||
static
|
||||
@@ -89,6 +102,9 @@ void init_write_subs(write_subs_fp (*write_subs_tbl)[SM_AGENT_IF_WRITE_SUBS_V0_E
|
||||
|
||||
(*write_subs_tbl)[KPM_SUBS_V3_0] = NULL;
|
||||
(*write_subs_tbl)[RAN_CTRL_SUBS_V1_03] = write_subs_rc_sm;
|
||||
#ifdef E3_AGENT
|
||||
(*write_subs_tbl)[DAPP_SUBS_V0] = write_subs_dapp_sm;
|
||||
#endif
|
||||
}
|
||||
|
||||
sm_io_ag_ran_t init_ran_func_ag(void)
|
||||
|
||||
49
openair2/E3AP/CMakeLists.txt
Normal file
49
openair2/E3AP/CMakeLists.txt
Normal file
@@ -0,0 +1,49 @@
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(CLIBE3 REQUIRED libe3)
|
||||
|
||||
if(NOT CLIBE3_FOUND)
|
||||
message(FATAL_ERROR "libe3 not found and required for E3 Agent")
|
||||
endif()
|
||||
|
||||
message(STATUS "Using for E3AP libe3 version: ${CLIBE3_VERSION}")
|
||||
|
||||
include_directories(${CLIBE3_INCLUDE_DIRS})
|
||||
|
||||
# Set default encoding format if not specified
|
||||
if(NOT DEFINED E3_ENCODING_FORMAT)
|
||||
set(E3_ENCODING_FORMAT "ASN1" CACHE STRING "E3AP encoding format (ASN1 or JSON)")
|
||||
endif()
|
||||
|
||||
# Validate encoding format
|
||||
if(NOT E3_ENCODING_FORMAT MATCHES "^(ASN1|JSON)$")
|
||||
message(FATAL_ERROR "Invalid E3_ENCODING_FORMAT: ${E3_ENCODING_FORMAT}. Must be ASN1 or JSON")
|
||||
endif()
|
||||
|
||||
message(STATUS "E3AP encoding format: ${E3_ENCODING_FORMAT}")
|
||||
|
||||
add_subdirectory(service_models)
|
||||
add_library(e3ap e3_agent.c config/e3_config.c)
|
||||
|
||||
# target_link_libraries(e3ap PUBLIC OCP_ITTI) # TODO
|
||||
|
||||
# Set encoding format compile definitions - both string value and format flags
|
||||
target_compile_definitions(e3ap PRIVATE E3_ENCODING_FORMAT="${E3_ENCODING_FORMAT}")
|
||||
|
||||
if(E3_ENCODING_FORMAT STREQUAL "ASN1")
|
||||
target_compile_definitions(e3ap PRIVATE E3_ASN1_FORMAT)
|
||||
target_link_libraries(e3ap PUBLIC ${CLIBE3_LIBRARIES} asn1_e3ap ${SERVICE_MODELS_LIBRARY})
|
||||
elseif(E3_ENCODING_FORMAT STREQUAL "JSON")
|
||||
target_compile_definitions(e3ap PRIVATE E3_JSON_FORMAT)
|
||||
pkg_check_modules(JSON_C REQUIRED json-c)
|
||||
if(NOT JSON_C_FOUND)
|
||||
message(FATAL_ERROR "JSON-C not found and required for JSON encoding, install it with apt-get install libjson-c-dev")
|
||||
endif()
|
||||
target_link_libraries(e3ap PUBLIC ${CLIBE3_LIBRARIES} ${JSON_C_LIBRARIES} ${SERVICE_MODELS_LIBRARY})
|
||||
target_include_directories(e3ap PUBLIC ${JSON_C_INCLUDE_DIRS})
|
||||
endif()
|
||||
|
||||
if(E2_AGENT)
|
||||
target_link_libraries(e3ap PUBLIC e2_ran_func_cuup e2_ran_func_du_cucp_cuup)
|
||||
target_compile_definitions(e3ap PRIVATE E2_AGENT)
|
||||
target_include_directories(e3ap PUBLIC ${OPENAIR_DIR}/openair2/E2AP/RAN_FUNCTION/O-RAN)
|
||||
endif()
|
||||
83
openair2/E3AP/config/e3_config.c
Normal file
83
openair2/E3AP/config/e3_config.c
Normal file
@@ -0,0 +1,83 @@
|
||||
#include "e3_config.h"
|
||||
#include "configuration.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "config.h"
|
||||
#include "common/config/config_paramdesc.h"
|
||||
#include "common/config/config_userapi.h"
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
#define E3CONFIG_SECTION "E3Configuration"
|
||||
|
||||
#define CONFIG_STRING_E3_LINK_PARAM "link"
|
||||
#define CONFIG_STRING_E3_TRANSPORT_PARAM "transport"
|
||||
#define CONFIG_STRING_E3_SAMPLING_PARAM "sampling"
|
||||
|
||||
// clang-format off
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
/* configuration parameters for the E3AP device */
|
||||
/* optname helpstr paramflags XXXptr defXXXval type numelt */
|
||||
/*-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------*/
|
||||
#define simOpt PARAMFLAG_NOFREE|PARAMFLAG_CMDLINE_NOPREFIXENABLED
|
||||
#define E3_PARAMS_DESC { \
|
||||
{"link", "Link layer for E3", simOpt, .strptr=&config->link, .defstrval="posix", TYPE_STRING, 0 },\
|
||||
{"transport", "Transport layer for E3", simOpt, .strptr=&config->transport, .defstrval="ipc", TYPE_STRING, 0 },\
|
||||
};
|
||||
// clang-format on
|
||||
|
||||
const char *E3_VALID_CONFIGURATIONS[][2] = {
|
||||
{"zmq", "ipc"},
|
||||
{"zmq", "tcp"},
|
||||
// {"zmq", "sctp"}, // implemented but not working because zeromq does not support it (yet)
|
||||
{"posix", "tcp"},
|
||||
{"posix", "sctp"},
|
||||
{"posix", "ipc"}};
|
||||
|
||||
void e3_readconfig(e3_cmdline_config_t *config)
|
||||
{
|
||||
paramdef_t e3_params[] = E3_PARAMS_DESC;
|
||||
|
||||
int ret = config_get(config_get_if(), e3_params, sizeof(e3_params) / sizeof(*(e3_params)), E3CONFIG_SECTION);
|
||||
AssertFatal(ret >= 0, "configuration couldn't be performed\n");
|
||||
|
||||
LOG_I(E3AP,
|
||||
"this is the configuration extracted: link %s transport %s\n",
|
||||
config->link,
|
||||
config->transport);
|
||||
}
|
||||
|
||||
void validate_configuration(e3_cmdline_config_t *config)
|
||||
{
|
||||
if (!config) {
|
||||
LOG_E(E3AP, "Configuration is null");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Check if link is "posix" or "zmq" using strncmp
|
||||
if (strncmp(config->link, "posix", 5) != 0 && strncmp(config->link, "zmq", 3) != 0) {
|
||||
LOG_E(E3AP, "Wrong link");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Check if transport is "tcp", "sctp", or "ipc" using strncmp
|
||||
if (strncmp(config->transport, "tcp", 3) != 0 && strncmp(config->transport, "sctp", 4) != 0
|
||||
&& strncmp(config->transport, "ipc", 3) != 0) {
|
||||
LOG_E(E3AP, "Wrong transport");
|
||||
abort();
|
||||
}
|
||||
|
||||
// Validate the combination of link and transport
|
||||
int combo_valid = 0;
|
||||
for (size_t i = 0; i < sizeof(E3_VALID_CONFIGURATIONS) / sizeof(E3_VALID_CONFIGURATIONS[0]); i++) {
|
||||
if (strncmp(config->link, E3_VALID_CONFIGURATIONS[i][0], strlen(E3_VALID_CONFIGURATIONS[i][0])) == 0
|
||||
&& strncmp(config->transport, E3_VALID_CONFIGURATIONS[i][1], strlen(E3_VALID_CONFIGURATIONS[i][1])) == 0) {
|
||||
combo_valid = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!combo_valid) {
|
||||
LOG_E(E3AP, "Wrong combination");
|
||||
abort();
|
||||
}
|
||||
}
|
||||
27
openair2/E3AP/config/e3_config.h
Normal file
27
openair2/E3AP/config/e3_config.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#ifndef E3_CONFIG_H
|
||||
#define E3_CONFIG_H
|
||||
|
||||
#include <stddef.h>
|
||||
|
||||
// Configuration structure for E3AP
|
||||
typedef struct {
|
||||
char *link; // Link layer (posix, zmq)
|
||||
char *transport; // Transport layer (tcp, sctp, ipc)
|
||||
} e3_cmdline_config_t;
|
||||
|
||||
// Valid configuration combinations
|
||||
extern const char *E3_VALID_CONFIGURATIONS[][2];
|
||||
|
||||
/**
|
||||
* @brief Read E3 configuration from the OAI config file
|
||||
* @param e3_configs Pointer to configuration structure to fill
|
||||
*/
|
||||
void e3_readconfig(e3_cmdline_config_t *config);
|
||||
|
||||
/**
|
||||
* @brief Validate E3AP configuration parameters
|
||||
* @param config Pointer to configuration structure to validate
|
||||
*/
|
||||
void validate_configuration(e3_cmdline_config_t *config);
|
||||
|
||||
#endif // E3_CONFIG_H
|
||||
267
openair2/E3AP/e3_agent.c
Normal file
267
openair2/E3AP/e3_agent.c
Normal file
@@ -0,0 +1,267 @@
|
||||
#include "e3_agent.h"
|
||||
#include "config/e3_config.h"
|
||||
#include "service_models/spectrum_sm/spectrum_sm.h"
|
||||
|
||||
// TODO replace pthreads with itti or use a faster way
|
||||
// #include "intertask_interface.h"
|
||||
// #include "create_tasks.h"
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <libe3/c_api.h>
|
||||
|
||||
#include "common/utils/system.h"
|
||||
#include "common/ran_context.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
e3_agent_global_t e3 = {0};
|
||||
|
||||
static void e2_e3_bridge(uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
const uint8_t *report_data,
|
||||
size_t report_size)
|
||||
{
|
||||
LOG_D(E3AP,
|
||||
"Received dApp report for RAN function %u from dApp %u (%zu bytes)\n",
|
||||
ran_function_id,
|
||||
dapp_id,
|
||||
report_size);
|
||||
#ifdef E2_AGENT
|
||||
if (!report_data && report_size > 0) {
|
||||
LOG_E(E3AP, "Invalid dApp report payload: report_data is NULL while report_size=%zu\n", report_size);
|
||||
return;
|
||||
}
|
||||
generate_e2_indication_from_e3_dapp_report(ran_function_id,
|
||||
dapp_id,
|
||||
report_size,
|
||||
report_data);
|
||||
#else
|
||||
(void)report_data;
|
||||
#endif
|
||||
}
|
||||
|
||||
void on_dapp_status_changed(void)
|
||||
{
|
||||
LOG_I(E3AP, "dApp status changed, triggering RIC Service Update\n");
|
||||
#ifdef E2_AGENT
|
||||
notify_dapp_status_changed();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
int e3_init()
|
||||
{
|
||||
LOG_D(E3AP, "Read configuration\n");
|
||||
e3_cmdline_config_t *e3_cmdline_configs =
|
||||
(e3_cmdline_config_t *)calloc(1, sizeof(e3_cmdline_config_t));
|
||||
if (!e3_cmdline_configs) {
|
||||
LOG_E(E3AP, "Failed to allocate E3 cmdline config\n");
|
||||
return -1;
|
||||
}
|
||||
e3_readconfig(e3_cmdline_configs);
|
||||
LOG_D(E3AP, "Validate configuration\n");
|
||||
validate_configuration(e3_cmdline_configs);
|
||||
|
||||
// Create e3_config_t from e3_cmdline_configs (only link and transport, rest defaults)
|
||||
e3_config_t config = {0};
|
||||
config.ran_identifier = "OAI DU";
|
||||
config.log_level = 3; // INFO
|
||||
|
||||
#ifdef E3_ASN1_FORMAT
|
||||
config.encoding = 0;
|
||||
#else
|
||||
config.encoding = 1;
|
||||
#endif
|
||||
|
||||
// Map string values to enum/int as needed for link_layer and transport_layer
|
||||
if (e3_cmdline_configs->link && strcmp(e3_cmdline_configs->link, "zmq") == 0) {
|
||||
config.link_layer = 0; // ZMQ
|
||||
} else if (e3_cmdline_configs->link && strcmp(e3_cmdline_configs->link, "posix") == 0) {
|
||||
config.link_layer = 1; // POSIX
|
||||
} else {
|
||||
config.link_layer = -1; // default
|
||||
}
|
||||
|
||||
if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "sctp") == 0) {
|
||||
config.transport_layer = 0; // SCTP
|
||||
} else if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "tcp") == 0) {
|
||||
config.transport_layer = 1; // TCP
|
||||
} else if (e3_cmdline_configs->transport && strcmp(e3_cmdline_configs->transport, "ipc") == 0) {
|
||||
config.transport_layer = 2; // IPC
|
||||
} else {
|
||||
config.transport_layer = -1; // default
|
||||
}
|
||||
|
||||
// All other fields left as default (0 or NULL)
|
||||
|
||||
// Create E3Agent with config
|
||||
e3.agent = e3_agent_create_with_config(&config);
|
||||
free(e3_cmdline_configs);
|
||||
e3_cmdline_configs = NULL;
|
||||
if (!e3.agent) {
|
||||
LOG_E(E3AP, "Failed to create E3Agent with config\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Initialize agent
|
||||
e3_error_t err = e3_agent_init(e3.agent);
|
||||
if (err != 0) {
|
||||
LOG_E(E3AP, "Failed to initialize E3Agent (err=%d)\n", err);
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Start agent
|
||||
err = e3_agent_start(e3.agent);
|
||||
if (err != 0) {
|
||||
LOG_E(E3AP, "Failed to start E3Agent (err=%d)\n", err);
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = e3_agent_set_dapp_report_handler(e3.agent, e2_e3_bridge);
|
||||
if (err != 0) {
|
||||
LOG_E(E3AP, "Failed to set dApp report handler (err=%d: %s)\n", err, e3_error_to_string(err));
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
err = e3_agent_set_dapp_status_changed_handler(e3.agent, on_dapp_status_changed);
|
||||
if (err != 0) {
|
||||
LOG_E(E3AP, "Failed to set dApp status changed handler (err=%d: %s)\n", err, e3_error_to_string(err));
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
// Register the SMs
|
||||
// SM Spectrum
|
||||
e3_c_service_model_desc_t *desc_sm_spectrum = create_spectrum_sm_model();
|
||||
if (!desc_sm_spectrum) {
|
||||
LOG_E(E3AP, "Failed to create Spectrum SM descriptor\n");
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
e3_service_model_handle_t *sm_spectrum = e3_service_model_create_from_c(desc_sm_spectrum);
|
||||
if (!sm_spectrum) {
|
||||
LOG_E(E3AP, "Failed to create Spectrum SM handle\n");
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
spectrum_sm_set_handle(sm_spectrum);
|
||||
|
||||
err = e3_agent_register_sm(e3.agent, sm_spectrum);
|
||||
if (err != 0) {
|
||||
LOG_E(E3AP, "Failed to register Spectrum SM (err=%d: %s)\n", err, e3_error_to_string(err));
|
||||
e3_service_model_destroy(sm_spectrum);
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e3_destroy()
|
||||
{
|
||||
// Stop and destroy the E3Agent if it exists
|
||||
if (e3.agent) {
|
||||
e3_agent_stop(e3.agent);
|
||||
e3_agent_destroy(e3.agent);
|
||||
e3.agent = NULL;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int e3_send_xapp_control(uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
const uint8_t *data,
|
||||
size_t len)
|
||||
{
|
||||
if (!e3.agent) {
|
||||
LOG_E(E3AP, "E3 agent not initialized: cannot send xApp control\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (data == NULL && len > 0) {
|
||||
LOG_E(E3AP, "data is not initialized, but len > 0\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
e3_error_t err = e3_agent_send_xapp_control(e3.agent, dapp_id, ran_function_id, data, len);
|
||||
if (err != E3_SUCCESS) {
|
||||
LOG_E(E3AP,
|
||||
"Failed to send xApp control to dApp %u for RAN function %u (err=%d)\n",
|
||||
dapp_id,
|
||||
ran_function_id,
|
||||
err);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
e3_dapp_subscription_map_t e3_get_dapp_subscription_map(void)
|
||||
{
|
||||
e3_dapp_subscription_map_t map = {0};
|
||||
|
||||
if (!e3.agent) {
|
||||
LOG_W(E3AP, "E3 agent not initialized: cannot query dApp subscriptions\n");
|
||||
return map;
|
||||
}
|
||||
|
||||
size_t num_dapps = 0;
|
||||
uint32_t *dapp_ids = e3_agent_get_registered_dapps(e3.agent, &num_dapps);
|
||||
if (!dapp_ids || num_dapps == 0) {
|
||||
e3_agent_free_uint32_array(dapp_ids);
|
||||
return map;
|
||||
}
|
||||
|
||||
map.dapps = (e3_dapp_info_t *)calloc(num_dapps, sizeof(e3_dapp_info_t));
|
||||
if (!map.dapps) {
|
||||
LOG_E(E3AP, "Failed to allocate dApp subscription map\n");
|
||||
e3_agent_free_uint32_array(dapp_ids);
|
||||
return map;
|
||||
}
|
||||
map.num_dapps = num_dapps;
|
||||
|
||||
for (size_t i = 0; i < num_dapps; i++) {
|
||||
map.dapps[i].dapp_id = dapp_ids[i];
|
||||
|
||||
size_t num_subs = 0;
|
||||
uint32_t *subs = e3_agent_get_dapp_subscriptions(e3.agent, dapp_ids[i], &num_subs);
|
||||
|
||||
if (subs && num_subs > 0) {
|
||||
map.dapps[i].e3_ran_func_ids = subs; // transfer ownership from libe3 malloc
|
||||
map.dapps[i].num_e3_ran_funcs = num_subs;
|
||||
} else {
|
||||
map.dapps[i].e3_ran_func_ids = NULL;
|
||||
map.dapps[i].num_e3_ran_funcs = 0;
|
||||
e3_agent_free_uint32_array(subs);
|
||||
}
|
||||
}
|
||||
|
||||
e3_agent_free_uint32_array(dapp_ids);
|
||||
return map;
|
||||
}
|
||||
|
||||
void e3_free_dapp_subscription_map(e3_dapp_subscription_map_t *map)
|
||||
{
|
||||
if (!map || !map->dapps)
|
||||
return;
|
||||
|
||||
for (size_t i = 0; i < map->num_dapps; i++) {
|
||||
e3_agent_free_uint32_array(map->dapps[i].e3_ran_func_ids);
|
||||
}
|
||||
free(map->dapps);
|
||||
|
||||
map->dapps = NULL;
|
||||
map->num_dapps = 0;
|
||||
}
|
||||
59
openair2/E3AP/e3_agent.h
Normal file
59
openair2/E3AP/e3_agent.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#ifndef E3_AGENT_H
|
||||
#define E3_AGENT_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <pthread.h>
|
||||
|
||||
#include <unistd.h>
|
||||
|
||||
#include <libe3/c_api.h>
|
||||
|
||||
#include "config/e3_config.h"
|
||||
|
||||
#ifdef E2_AGENT
|
||||
#include "ran_func_dapp_extern.h"
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
e3_agent_handle_t *agent;
|
||||
e3_service_model_handle_t **service_models;
|
||||
size_t num_service_models;
|
||||
} e3_agent_global_t;
|
||||
|
||||
typedef struct {
|
||||
uint32_t dapp_id;
|
||||
uint32_t *e3_ran_func_ids;
|
||||
size_t num_e3_ran_funcs;
|
||||
} e3_dapp_info_t;
|
||||
|
||||
typedef struct {
|
||||
e3_dapp_info_t *dapps;
|
||||
size_t num_dapps;
|
||||
} e3_dapp_subscription_map_t;
|
||||
|
||||
extern e3_agent_global_t e3;
|
||||
|
||||
int e3_init();
|
||||
int e3_destroy();
|
||||
|
||||
int e3_send_xapp_control(uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
const uint8_t *data,
|
||||
size_t len);
|
||||
|
||||
/**
|
||||
* @brief Get all connected dApps and their RAN function subscriptions.
|
||||
*
|
||||
* Caller must free the result with e3_free_dapp_subscription_map().
|
||||
*
|
||||
* @return populated map on success, {NULL, 0} on error or if no dApps
|
||||
*/
|
||||
e3_dapp_subscription_map_t e3_get_dapp_subscription_map(void);
|
||||
|
||||
/**
|
||||
* @brief Free a subscription map returned by e3_get_dapp_subscription_map().
|
||||
*/
|
||||
void e3_free_dapp_subscription_map(e3_dapp_subscription_map_t *map);
|
||||
|
||||
#endif // E3_AGENT_H
|
||||
18
openair2/E3AP/service_models/CMakeLists.txt
Normal file
18
openair2/E3AP/service_models/CMakeLists.txt
Normal file
@@ -0,0 +1,18 @@
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
add_subdirectory(spectrum_sm)
|
||||
|
||||
add_library(service_models INTERFACE)
|
||||
|
||||
target_include_directories(service_models INTERFACE
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_SOURCE_DIR}/common/utils/LOG
|
||||
${CMAKE_SOURCE_DIR}/openair2/E3AP
|
||||
${CLIBE3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_link_libraries(service_models INTERFACE
|
||||
${SPECTRUM_SM_LIBRARY}
|
||||
)
|
||||
|
||||
set(SERVICE_MODELS_LIBRARY service_models PARENT_SCOPE)
|
||||
44
openair2/E3AP/service_models/spectrum_sm/CMakeLists.txt
Normal file
44
openair2/E3AP/service_models/spectrum_sm/CMakeLists.txt
Normal file
@@ -0,0 +1,44 @@
|
||||
# Spectrum Service Model CMakeLists.txt
|
||||
|
||||
cmake_minimum_required(VERSION 3.10)
|
||||
|
||||
add_subdirectory(MESSAGES)
|
||||
|
||||
# Spectrum SM source files
|
||||
set(SPECTRUM_SM_SOURCES
|
||||
spectrum_sm.c
|
||||
spectrum_enc.c
|
||||
spectrum_dec.c
|
||||
)
|
||||
|
||||
# Create spectrum SM library
|
||||
add_library(spectrum_sm STATIC ${SPECTRUM_SM_SOURCES})
|
||||
|
||||
# Include directories
|
||||
target_include_directories(spectrum_sm PUBLIC
|
||||
${CMAKE_CURRENT_SOURCE_DIR}
|
||||
${CMAKE_CURRENT_SOURCE_DIR}/..
|
||||
${CMAKE_CURRENT_BINARY_DIR}/MESSAGES # ASN.1 generated headers
|
||||
${CMAKE_SOURCE_DIR}/common/utils/LOG
|
||||
${CMAKE_SOURCE_DIR}/openair2/E3AP
|
||||
${CMAKE_SOURCE_DIR}/common/utils/T/tracer # T-tracer headers
|
||||
${CLIBE3_INCLUDE_DIRS}
|
||||
)
|
||||
|
||||
target_compile_definitions(spectrum_sm PRIVATE T_MESSAGES_PATH="${OPENAIR_DIR}/common/utils/T/T_messages.txt")
|
||||
|
||||
target_link_libraries(spectrum_sm spectrum_sm_asn1 tracer_utils)
|
||||
add_dependencies(spectrum_sm spectrum_sm_asn1)
|
||||
|
||||
# Compile-time format selection (inherit from E3AP)
|
||||
if(E3_ENCODING_FORMAT STREQUAL "ASN1")
|
||||
target_compile_definitions(spectrum_sm PRIVATE SPECTRUM_SM_ASN1_FORMAT)
|
||||
message(STATUS "Spectrum SM: Using ASN.1 encoding")
|
||||
else()
|
||||
target_compile_definitions(spectrum_sm PRIVATE SPECTRUM_SM_JSON_FORMAT)
|
||||
target_link_libraries(spectrum_sm json-c)
|
||||
message(STATUS "Spectrum SM: Using JSON encoding")
|
||||
endif()
|
||||
|
||||
# Export spectrum SM library
|
||||
set(SPECTRUM_SM_LIBRARY spectrum_sm PARENT_SCOPE)
|
||||
@@ -0,0 +1,65 @@
|
||||
-- Spectrum Service Model ASN.1 Definitions
|
||||
-- This service model extends E3 messages for spectrum sharing applications
|
||||
--
|
||||
-- Usage with E3 Protocol:
|
||||
-- - Spectrum-IndicationData structures are encoded into the protocolData field of E3-IndicationMessage
|
||||
-- - Spectrum-ControlData structures are encoded into the actionData field of E3-ControlAction
|
||||
-- - Use E3-PDU as the top-level message structure
|
||||
|
||||
SPECTRUM-SM DEFINITIONS AUTOMATIC TAGS ::= BEGIN
|
||||
|
||||
-- ============================================================================
|
||||
-- SM SPECTRUM RAN FUNCTION DATA STRUCTURES
|
||||
-- ============================================================================
|
||||
|
||||
-- Spectrum RAN function data
|
||||
Spectrum-RanFunctionData ::= SEQUENCE {
|
||||
name OCTET STRING (SIZE (1..32)),
|
||||
version INTEGER (0..65535),
|
||||
description OCTET STRING (SIZE (1..128))
|
||||
}
|
||||
|
||||
-- ============================================================================
|
||||
-- INDICATION MESSAGE STRUCTURES
|
||||
-- ============================================================================
|
||||
|
||||
-- Spectrum IQ Data Indication (raw I/Q samples from RAN)
|
||||
Spectrum-IQDataIndication ::= SEQUENCE {
|
||||
iqSamples OCTET STRING (SIZE (1..16384)), -- Raw I/Q data as int16 array
|
||||
sampleCount INTEGER (0..32768), -- Number of I/Q samples
|
||||
timestamp INTEGER (0..2147483647) OPTIONAL -- Unix timestamp when samples were captured
|
||||
}
|
||||
|
||||
-- ============================================================================
|
||||
-- CONTROL ACTION STRUCTURES
|
||||
-- ============================================================================
|
||||
|
||||
-- PRB Blacklist Control (main control message used by spectrum dApp)
|
||||
Spectrum-PRBBlacklistControl ::= SEQUENCE {
|
||||
blacklistedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex), -- List of PRB indices
|
||||
samplingThreshold INTEGER (0..100) OPTIONAL, -- Sampling ratio for the IQ captures, one delivery each samplingThreshold
|
||||
validityPeriod INTEGER (1..3600) OPTIONAL -- How long this blacklist is valid (seconds)
|
||||
}
|
||||
|
||||
-- ============================================================================
|
||||
-- xAPP CONTROL ACTION STRUCTURES
|
||||
-- ============================================================================
|
||||
|
||||
|
||||
-- Spectrum Configuration Control
|
||||
Spectrum-ConfigControl ::= SEQUENCE {
|
||||
noiseFloorThreshold INTEGER (-100..100) OPTIONAL, -- Update noise floor threshold
|
||||
averagingFrames INTEGER (1..255) OPTIONAL, -- Update averaging window
|
||||
enable BOOLEAN OPTIONAL -- Enable/disable spectrum monitoring
|
||||
}
|
||||
|
||||
|
||||
-- ============================================================================
|
||||
-- CONSTANTS AND CONSTRAINTS
|
||||
-- ============================================================================
|
||||
|
||||
-- Maximum values based on 5G NR specifications
|
||||
maxPRBs INTEGER ::= 273 -- Maximum PRBs in 5G NR
|
||||
maxPRBIndex INTEGER ::= 272 -- maxPRBs - 1
|
||||
|
||||
END
|
||||
@@ -0,0 +1,102 @@
|
||||
set(SPECTRUM_SM_GRAMMAR ASN1/V1/e3sm_spectrum.asn)
|
||||
|
||||
set(spectrum_sm_source
|
||||
ANY_aper.c
|
||||
ANY.c
|
||||
ANY_uper.c
|
||||
ANY_xer.c
|
||||
aper_decoder.c
|
||||
aper_encoder.c
|
||||
aper_opentype.c
|
||||
aper_support.c
|
||||
asn_application.c
|
||||
asn_bit_data.c
|
||||
asn_codecs_prim.c
|
||||
asn_codecs_prim_xer.c
|
||||
asn_internal.c
|
||||
asn_random_fill.c
|
||||
ber_tlv_length.c
|
||||
ber_tlv_tag.c
|
||||
constr_CHOICE.c
|
||||
constr_SEQUENCE.c
|
||||
constr_TYPE.c
|
||||
constraints.c
|
||||
OCTET_STRING_aper.c
|
||||
OCTET_STRING.c
|
||||
OCTET_STRING_uper.c
|
||||
OCTET_STRING_xer.c
|
||||
per_decoder.c
|
||||
per_encoder.c
|
||||
per_opentype.c
|
||||
per_support.c
|
||||
BOOLEAN.c
|
||||
BOOLEAN_aper.c
|
||||
BOOLEAN_uper.c
|
||||
BOOLEAN_xer.c
|
||||
INTEGER.c
|
||||
INTEGER_aper.c
|
||||
INTEGER_uper.c
|
||||
INTEGER_xer.c
|
||||
uper_decoder.c
|
||||
uper_encoder.c
|
||||
uper_opentype.c
|
||||
uper_support.c
|
||||
xer_decoder.c
|
||||
xer_encoder.c
|
||||
xer_support.c
|
||||
# Spectrum SM specific generated files
|
||||
Spectrum-IQDataIndication.c
|
||||
Spectrum-PRBBlacklistControl.c
|
||||
Spectrum-ConfigControl.c
|
||||
Spectrum-RanFunctionData.c
|
||||
)
|
||||
|
||||
set(spectrum_sm_headers
|
||||
ANY_aper.h
|
||||
ANY.h
|
||||
ANY_uper.h
|
||||
ANY_xer.h
|
||||
aper_decoder.h
|
||||
aper_encoder.h
|
||||
aper_opentype.h
|
||||
aper_support.h
|
||||
asn_application.h
|
||||
asn_bit_data.h
|
||||
asn_codecs_prim.h
|
||||
asn_codecs_prim_xer.h
|
||||
asn_internal.h
|
||||
asn_random_fill.h
|
||||
ber_tlv_length.h
|
||||
ber_tlv_tag.h
|
||||
constr_CHOICE.h
|
||||
constr_SEQUENCE.h
|
||||
constr_TYPE.h
|
||||
constraints.h
|
||||
OCTET_STRING_aper.h
|
||||
OCTET_STRING.h
|
||||
OCTET_STRING_uper.h
|
||||
OCTET_STRING_xer.h
|
||||
per_decoder.h
|
||||
per_encoder.h
|
||||
per_opentype.h
|
||||
per_support.h
|
||||
BOOLEAN.h
|
||||
BOOLEAN_aper.h
|
||||
BOOLEAN_uper.h
|
||||
BOOLEAN_xer.h
|
||||
INTEGER.h
|
||||
INTEGER_aper.h
|
||||
INTEGER_uper.h
|
||||
INTEGER_xer.h
|
||||
uper_decoder.h
|
||||
uper_encoder.h
|
||||
uper_opentype.h
|
||||
uper_support.h
|
||||
xer_decoder.h
|
||||
xer_encoder.h
|
||||
xer_support.h
|
||||
Spectrum-IQDataIndication.h
|
||||
Spectrum-PRBBlacklistControl.h
|
||||
Spectrum-ConfigControl.h
|
||||
Spectrum-RanFunctionData.h
|
||||
)
|
||||
@@ -0,0 +1,26 @@
|
||||
set(SPECTRUM_SM_VERSION 1 0 0)
|
||||
make_version(SPECTRUM_SM_cc ${SPECTRUM_SM_VERSION})
|
||||
string(REPLACE ";" "." SPECTRUM_SM_RELEASE "${SPECTRUM_SM_VERSION}")
|
||||
|
||||
if(SPECTRUM_SM_RELEASE VERSION_EQUAL "1.0.0")
|
||||
include(ASN1/V1/e3sm_spectrum.cmake)
|
||||
else()
|
||||
message(FATAL_ERROR "unknown SPECTRUM_SM_RELEASE ${SPECTRUM_SM_RELEASE}")
|
||||
endif()
|
||||
|
||||
message(STATUS "Selected SPECTRUM_SM_VERSION: ${SPECTRUM_SM_RELEASE}")
|
||||
run_asn1c(${CMAKE_CURRENT_SOURCE_DIR}/${SPECTRUM_SM_GRAMMAR}
|
||||
""
|
||||
OUTPUT ${spectrum_sm_source} ${spectrum_sm_headers}
|
||||
OPTIONS -pdu=all -gen-APER -gen-UPER -no-gen-JER -no-gen-BER -no-gen-OER -fno-include-deps -fcompound-names -findirect-choice -no-gen-example
|
||||
COMMENT "Spectrum SM source files"
|
||||
)
|
||||
|
||||
|
||||
add_library(spectrum_sm_asn1 ${spectrum_sm_source})
|
||||
target_include_directories(spectrum_sm_asn1 PUBLIC "${CMAKE_CURRENT_BINARY_DIR}")
|
||||
target_compile_options(spectrum_sm_asn1 PRIVATE -DASN_DISABLE_OER_SUPPORT -w)
|
||||
|
||||
# Export variables to parent scope
|
||||
set(SPECTRUM_SM_ASN1_LIB spectrum_sm_asn1 PARENT_SCOPE)
|
||||
set(SPECTRUM_SM_ASN1_INCLUDE_DIR "${CMAKE_CURRENT_BINARY_DIR}" PARENT_SCOPE)
|
||||
@@ -0,0 +1,375 @@
|
||||
# Spectrum Service Model (SM) Architecture
|
||||
|
||||
This document describes the E3 Service Model Spectrum implementation in OpenAirInterface, detailing the IQ data collection mechanism, the PRB control loop, and the xApp-dApp coordination path.
|
||||
|
||||
## Overview
|
||||
|
||||
The Spectrum SM provides three key functionalities:
|
||||
1. **Indication (Sensing)**: Collection and forwarding of frequency-domain IQ samples for spectrum monitoring
|
||||
2. **Control**: Dynamic PRB (Physical Resource Block) blacklisting based on external dApp decisions
|
||||
3. **xApp-dApp Interaction**: Bidirectional PRB-level information exchange between xApp (via E2) and dApp (via E3), enabling coordinated RAN control from both interfaces
|
||||
|
||||
---
|
||||
|
||||
## 1. IQ Data Collection (Indication Path)
|
||||
|
||||
### 1.1 Data Source Location
|
||||
|
||||
The IQ samples are collected in the PHY layer at:
|
||||
- **File**: `openair1/SCHED_NR/phy_procedures_nr_gNB.c`
|
||||
- **Function**: `phy_procedures_gNB_uespec_RX()`
|
||||
- **Data Variable**: `gNB->common_vars.rxdataF[0]`
|
||||
|
||||
### 1.2 Signal Processing Stage
|
||||
|
||||
The collected IQs are **PRE-EQUALIZATION** samples in the **frequency domain (post-FFT)**.
|
||||
|
||||
```
|
||||
┌─────────────┐ ┌─────────┐ ┌──────────────────────────────────┐ ┌─────────────────┐ ┌─────────────┐
|
||||
│ RF Frontend │ → │ ADC │ → │ Time Domain Processing + FFT │ → │ rxdataF │ → │ Equalization │
|
||||
│ │ │ │ │ (CP Removal, Synchronization) │ │ ◄── E3 COLLECTS │ │ │
|
||||
└─────────────┘ └─────────┘ └──────────────────────────────────┘ └─────────────────┘ └─────────────┘
|
||||
│
|
||||
▼
|
||||
┌─────────────────┐
|
||||
│ Channel Est. │
|
||||
│ rxdataF_comp │
|
||||
│ (post-equal.) │
|
||||
└─────────────────┘
|
||||
```
|
||||
|
||||
### 1.3 Position in 5G NR PHY Uplink Processing Chain
|
||||
|
||||
| Stage | Description | E3 Agent |
|
||||
|-------|-------------|----------|
|
||||
| ADC | Analog-to-Digital Conversion | ✗ Before |
|
||||
| RF Frontend | Filtering, amplification | ✗ Before |
|
||||
| Time-domain RX | Sample buffering | ✗ Before |
|
||||
| CP Removal | Cyclic prefix removal | ✗ Before |
|
||||
| **FFT** | Time → Frequency transform | ✗ Before |
|
||||
| **rxdataF** | **Frequency-domain samples** | **✓ COLLECTED HERE** |
|
||||
| Channel Estimation | Estimate channel response | ✗ After |
|
||||
| Equalization | Compensate channel effects | ✗ After |
|
||||
| rxdataF_comp | Equalized samples | ✗ After |
|
||||
| Demodulation | Symbol → bits | ✗ After |
|
||||
| LDPC Decoding | Error correction | ✗ After |
|
||||
|
||||
### 1.4 Collection Parameters
|
||||
|
||||
| Parameter | Value | Description |
|
||||
|-----------|-------|-------------|
|
||||
| **Domain** | Frequency | Post-FFT samples |
|
||||
| **Stage** | Pre-equalization | Raw channel + signal + noise |
|
||||
| **Slot filter** | Slot 8 only | `slot_rx == 8` |
|
||||
| **Symbol** | 12th OFDM symbol | Fixed symbol within slot |
|
||||
| **Size** | `ofdm_symbol_size` samples | One full OFDM symbol |
|
||||
| **Antenna** | First antenna (index 0) | Single antenna capture |
|
||||
| **Data type** | `c16_t` (complex 16-bit) | I/Q interleaved |
|
||||
| **Sampling control** | `sampling_threshold` | Decimation factor (default: 5) |
|
||||
|
||||
### 1.5 Code Reference
|
||||
|
||||
```c
|
||||
// From phy_procedures_nr_gNB.c (lines ~1137-1161)
|
||||
#ifdef E3_AGENT
|
||||
c16_t **rxdataF_sen = gNB->common_vars.rxdataF[0];
|
||||
if (nr_slot_select(&gNB->gNB_config, frame_rx, slot_rx) == NR_UPLINK_SLOT && slot_rx == 8) {
|
||||
e3_sm_spectrum_control->sampling_counter++;
|
||||
if (e3_sm_spectrum_control->sampling_counter > e3_sm_spectrum_control->sampling_threshold) {
|
||||
const uint16_t n_symbols = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot;
|
||||
uint64_t symbol_offset = (n_symbols)*gNB->frame_parms.ofdm_symbol_size + (12) * gNB->frame_parms.ofdm_symbol_size;
|
||||
int32_t *rx_signal = (int32_t *)&rxdataF_sen[0][symbol_offset];
|
||||
|
||||
T(T_GNB_PHY_UL_FREQ_SENSING_SYMBOL, ...);
|
||||
e3_sm_spectrum_control->sampling_counter = 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
```
|
||||
|
||||
### 1.6 Implications
|
||||
|
||||
Since the samples are **pre-equalization**:
|
||||
- They contain the **combined effect** of: transmitted signal + channel response + noise + interference
|
||||
- They are **NOT** compensated for channel distortion
|
||||
- Ideal for **spectrum sensing/monitoring** applications
|
||||
- Can be used to analyze: interference levels, spectral occupancy, raw signal power per subcarrier
|
||||
|
||||
---
|
||||
|
||||
## 2. PRB Control (Control Path)
|
||||
|
||||
### 2.1 Control Flow Overview
|
||||
|
||||
```
|
||||
┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────────┐
|
||||
│ dApp/xApp │ → │ E3 Agent │ → │ spectrum_sm_process_ │ → │ e3_sm_spectrum_ │
|
||||
│ (external) │ │ Interface │ │ dapp_control_action() │ │ control struct │
|
||||
└──────────────┘ └──────────────┘ └─────────────────────────┘ └──────────────────┘
|
||||
│
|
||||
▼
|
||||
┌──────────────┐ ┌──────────────┐ ┌─────────────────────────┐ ┌──────────────────┐
|
||||
│ Scheduling │ ← │ gNB-> │ ← │ nr_update_prb_policy() │ ← │ dyn_prbbl[] │
|
||||
│ Decisions │ │ ulprbbl[] │ │ (every 128 frames) │ │ (blacklist) │
|
||||
└──────────────┘ └──────────────┘ └─────────────────────────┘ └──────────────────┘
|
||||
```
|
||||
|
||||
### 2.2 Control Data Structure
|
||||
|
||||
```c
|
||||
typedef struct e3_sm_spectrum_control {
|
||||
uint16_t* action_list; // Temporary buffer (array) for incoming PRB indices
|
||||
int action_size; // Number of PRBs in action_list
|
||||
uint16_t dyn_prbbl[MAX_BWP_SIZE]; // Dynamic PRB blacklist bitmap (per-PRB marker)
|
||||
int ready; // Flag: new policy available
|
||||
uint32_t sampling_threshold; // IQ sampling decimation factor
|
||||
uint32_t sampling_counter; // Current sample count
|
||||
pthread_mutex_t mutex; // Thread synchronization
|
||||
} e3_sm_spectrum_control_t;
|
||||
```
|
||||
|
||||
### 2.3 Control Processing
|
||||
|
||||
When a control message arrives from the dApp:
|
||||
|
||||
1. **Decode**: `spectrum_sm_process_control()` decodes the PRB control message (E3 SM control handler)
|
||||
2. **Parse**: Extract blacklisted PRB indices from the message
|
||||
3. **Update**: Set `dyn_prbbl[prb_index] = 0x3FFF` for each blacklisted PRB
|
||||
4. **Signal**: Set `ready = 1` to indicate new policy is available
|
||||
|
||||
```c
|
||||
// From spectrum_sm.c (actual control handler: `spectrum_sm_process_control()`)
|
||||
for (size_t j = 0; j < elems; ++j) {
|
||||
uint16_t prb = alist16[j];
|
||||
if (prb < MAX_BWP_SIZE) {
|
||||
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
|
||||
}
|
||||
}
|
||||
e3_sm_spectrum_control->ready = 1;
|
||||
```
|
||||
|
||||
### 2.4 Slot 8 Reservation for Spectrum Sensing
|
||||
|
||||
To ensure clean IQ samples for spectrum sensing (without UE transmissions), slot 8 is reserved by modifying the ULSCH TDA selection in `gNB_scheduler_ulsch.c`:
|
||||
|
||||
```c
|
||||
// From gNB_scheduler_ulsch.c - get_ul_tda()
|
||||
#ifdef E3_AGENT
|
||||
// Empty symbol in slot 8 for spectrum sensing. Symbol 12.
|
||||
// TODO handle when SRS is present
|
||||
if (slot == 8) {
|
||||
return 1; // Use TDA index 1 which avoids symbol 12
|
||||
}
|
||||
#endif // E3_AGENT
|
||||
```
|
||||
|
||||
This forces the scheduler to use **TDA index 1** for slot 8, which results in:
|
||||
- **No UE PUSCH transmission** on symbol 12 of slot 8
|
||||
- The 12th OFDM symbol remains **free of scheduled UE traffic**
|
||||
- IQ samples captured are purely **noise + external interference** (no OAI-scheduled signals)
|
||||
|
||||
### 2.5 PRB Blacklist Application in MAC Scheduler
|
||||
|
||||
The PRB blacklist is applied at multiple points in the MAC scheduler:
|
||||
|
||||
#### 2.5.1 Policy Update (Periodic)
|
||||
|
||||
```c
|
||||
// Called every 128 frames (slot 0, frame & 127 == 0) in gNB_scheduler.c
|
||||
#ifdef E3_AGENT
|
||||
nr_update_prb_policy(module_idP, frame, slot);
|
||||
#endif
|
||||
```
|
||||
|
||||
The `nr_update_prb_policy()` function (in `gNB_scheduler_dlsch.c`):
|
||||
- Acquires the mutex lock
|
||||
- Copies `dyn_prbbl` to `gNB->ulprbbl` if `ready == 1`
|
||||
- Resets `ready = 0`
|
||||
- Logs the barred PRBs
|
||||
|
||||
#### 2.5.2 VRB Map Application (Every Slot)
|
||||
|
||||
```c
|
||||
// Applied every slot during vrb_map clearing in gNB_scheduler.c
|
||||
#ifdef E3_AGENT
|
||||
uint16_t *vrb_map = cc[CC_id].vrb_map[i];
|
||||
memcpy(vrb_map, &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
|
||||
#endif
|
||||
```
|
||||
|
||||
This ensures:
|
||||
- **Uplink**: `vrb_map_UL` is initialized with the blacklist
|
||||
- **Downlink**: `vrb_map` is initialized with the blacklist (when E3_AGENT enabled)
|
||||
|
||||
### 2.6 Effect on Scheduling
|
||||
|
||||
When a PRB is blacklisted (`ulprbbl[prb] = 0x3FFF`):
|
||||
- The scheduler treats those PRBs as **already occupied**
|
||||
- No UE transmissions (PUSCH/PUCCH) will be scheduled on those PRBs
|
||||
- No gNB transmissions (PDSCH/PDCCH) will be scheduled on those PRBs
|
||||
|
||||
### 2.7 Control Timing
|
||||
|
||||
| Event | Timing |
|
||||
|-------|--------|
|
||||
| Control message reception | Asynchronous (from dApp) |
|
||||
| Policy update check | Every 128 frames (~1.28s at 10ms frame) |
|
||||
| VRB map application | Every slot |
|
||||
|
||||
---
|
||||
|
||||
## 3. Complete Closed-Loop Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ dApp │
|
||||
│ (Spectrum analysis, ML inference, etc.)│
|
||||
└────────────────┬────────────────────────┘
|
||||
│
|
||||
┌───────────┴───────────┐
|
||||
│ E3 Interface │
|
||||
│ (encode/decode) │
|
||||
└───────────┬───────────┘
|
||||
│
|
||||
┌────────────────────────────────┼────────────────────────────────┐
|
||||
│ │ │
|
||||
▼ │ ▼
|
||||
┌───────────────────────────┐ │ ┌───────────────────────────┐
|
||||
│ INDICATION PATH │ │ │ CONTROL PATH │
|
||||
│ (PHY Layer) │ │ │ (MAC Layer) │
|
||||
│ │ │ │ │
|
||||
│ rxdataF (freq domain) │ │ │ dyn_prbbl[] → ulprbbl[] │
|
||||
│ │ │ │ │ │ │
|
||||
│ ▼ │ │ │ ▼ │
|
||||
│ T-Tracer event │ │ │ vrb_map initialization │
|
||||
│ │ │ │ │ │ │
|
||||
│ ▼ │ │ │ ▼ │
|
||||
│ spectrum_sm encode │───────────────────┘ │ Scheduler decisions │
|
||||
│ │ │ │ │
|
||||
│ ▼ │ └───────────────────────────┘
|
||||
│ sm_indication_data_set │
|
||||
└───────────────────────────┘
|
||||
```
|
||||
|
||||
| Parameter | Default | Location | Description |
|
||||
|-----------|---------|----------|-------------|
|
||||
| `sampling_threshold` | 5 | spectrum_sm.c | IQ capture every N×10ms |
|
||||
| `slot_rx` filter | 8 | phy_procedures_nr_gNB.c | Only capture from slot 8 |
|
||||
| Symbol index | 12 | phy_procedures_nr_gNB.c | 12th symbol of the slot |
|
||||
| Policy update period | 128 frames | gNB_scheduler.c | ~1.28s between updates |
|
||||
| `MAX_BWP_SIZE` | 275 | nr_common.h | Maximum PRBs supported |
|
||||
|
||||
**Exported functions (SM entry points)**
|
||||
|
||||
- `create_spectrum_sm_model()` : Build and return the `e3_c_service_model_desc_t` descriptor for registration with the E3 agent. Encodes RAN function metadata (ASN.1 or JSON) via the SM encoder.
|
||||
- `spectrum_sm_set_handle()` : Provide the SM with the `e3_service_model_handle_t` used to emit indications and acknowledgements.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 4. xApp-dApp Interaction (E2-E3 Bridge)
|
||||
|
||||
When the E3 Agent is compiled with `E2_AGENT` support, the Spectrum SM participates in a closed loop between an external xApp (via E2) and the local dApp (via E3). Two additional ASN.1 structures support this interaction.
|
||||
|
||||
### 4.1 xApp → dApp Control (`Spectrum-PRBBlockedControl`)
|
||||
|
||||
The xApp sends a `Spectrum-PRBBlockedControl` message to instruct the dApp about which PRBs should be blocked. This message travels through the E2 interface into the E3 Agent, which forwards it to the connected dApp.
|
||||
|
||||
```asn1
|
||||
Spectrum-PRBBlockedControl ::= SEQUENCE {
|
||||
blockedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex)
|
||||
}
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────┐ ┌─────────────────────────┐ ┌──────────┐
|
||||
│ xApp │ → │ E2 Agent │ → │ E3 Agent │ → │ dApp │
|
||||
│ │ │ │ │ (direct E2→E3 handoff) │ │ │
|
||||
└──────────┘ └──────────┘ └─────────────────────────┘ └──────────┘
|
||||
```
|
||||
|
||||
- The E2 Agent delivers xApp control actions to the E3 Agent using the E3 library APIs.
|
||||
- The E3 Agent registers an internal dApp-report handler (`e2_e3_bridge()`) using `e3_agent_set_dapp_report_handler()` which is invoked when a dApp report arrives.
|
||||
- `e2_e3_bridge()` forwards the received report to the E2-side translation function `generate_e2_indication_from_e3_dapp_report(ran_function_id, dapp_id, report_size, report_data)`, which emits the E2 indication to subscribed xApps.
|
||||
|
||||
### 4.2 dApp → xApp Report (`Spectrum-PRBBlacklistReport`)
|
||||
|
||||
The dApp sends a `Spectrum-PRBBlacklistReport` message back to the xApp to communicate the current PRB blacklist state. This message arrives at the E3 Agent as a `E3AP_PDU_TYPE_DAPP_REPORT` and is forwarded to the E2 interface.
|
||||
|
||||
```asn1
|
||||
Spectrum-PRBBlacklistReport ::= SEQUENCE {
|
||||
blacklistedPRBs SEQUENCE (SIZE (0..maxPRBs)) OF INTEGER (0..maxPRBIndex)
|
||||
}
|
||||
```
|
||||
|
||||
**Flow**:
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────────────────────────────────┐ ┌──────────┐ ┌──────────┐
|
||||
│ dApp │ → │ E3 Agent (dApp-report handler) │ → │ E2 Agent │ → │ xApp │
|
||||
│ │ │ generate_e2_indication_from_e3_dapp │ │ │ │ │
|
||||
│ │ │ report() │ │ │ │ │
|
||||
└──────────┘ └──────────────────────────────────────┘ └──────────┘ └──────────┘
|
||||
```
|
||||
|
||||
- The E3 Agent receives the dApp report via the registered report handler (`e3_agent.c`) and forwards it to the E2 translation function.
|
||||
- When `E2_AGENT` is enabled, `generate_e2_indication_from_e3_dapp_report()` translates the E3 dApp report into an E2 indication which the E2 Agent delivers to subscribed xApps.
|
||||
|
||||
### 4.3 Complete xApp-dApp Closed Loop
|
||||
|
||||
```
|
||||
┌──────────┐ ┌──────────┐
|
||||
│ │ ──► Spectrum-PRBBlockedControl ── E2 Agent ──► E3 ──►│ │
|
||||
│ xApp │ │ dApp │
|
||||
│ │ ◄── Spectrum-PRBBlacklistReport ── E2 Agent ◄── E3 ◄──│ │
|
||||
└──────────┘ └──────────┘
|
||||
│ │
|
||||
│ E2 Control │ E3 Control
|
||||
▼ ▼
|
||||
┌─────────────────────────────────────────────────────────────────────────────────┐
|
||||
│ gNB (RAN) │
|
||||
└─────────────────────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
Both the xApp and the dApp can independently issue control actions to the RAN. The xApp sends controls via the E2 interface, while the dApp sends controls via the E3 interface. The bidirectional xApp-dApp channel (`Spectrum-PRBBlockedControl` / `Spectrum-PRBBlacklistReport`) allows them to exchange PRB-level information, enabling coordinated decision-making before either entity acts on the RAN.
|
||||
|
||||
---
|
||||
|
||||
## 5. Files Reference
|
||||
|
||||
RAN
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `openair1/SCHED_NR/phy_procedures_nr_gNB.c` | IQ sample collection point |
|
||||
| `openair2/E3AP/service_models/spectrum_sm/spectrum_sm.c` | SM main logic, T-tracer interface |
|
||||
| `openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h` | Data structures, function declarations |
|
||||
| `openair2/E3AP/service_models/spectrum_sm/spectrum_enc.c` | Indication message encoding |
|
||||
| `openair2/E3AP/service_models/spectrum_sm/spectrum_dec.c` | Control message decoding |
|
||||
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler.c` | Policy application in scheduler |
|
||||
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_dlsch.c` | `nr_update_prb_policy()` implementation |
|
||||
| `openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_ulsch.c` | Slot 8 TDA reservation for sensing |
|
||||
|
||||
|
||||
xApp-dApp Bridge
|
||||
|
||||
| File | Purpose |
|
||||
|------|---------|
|
||||
| `openair2/E3AP/e3_agent.h` | E3 agent global state and initialization (`e3_init()`, `e3_destroy()`); E3 agent registers a dApp-report handler in `e3_init()`.
|
||||
| `openair2/E3AP/e3_agent.c` | Implements the E3 agent lifecycle; installs `e2_e3_bridge()` as the dApp-report handler and forwards reports to the E2 translation function.
|
||||
| `openair2/E2AP/RAN_FUNCTION/O-RAN/ran_func_dapp_extern.h` | Declares `generate_e2_indication_from_e3_dapp_report()` used to translate E3 dApp reports into E2 indications.
|
||||
|
||||
|
||||
---
|
||||
|
||||
## Key functions
|
||||
|
||||
- `spectrum_encode_indication()` — encode raw IQ/buffer data into the SM indication payload (used in the worker loop to prepare dApp indications).
|
||||
- `spectrum_decode_prb_control()` — decode incoming PRB control messages from dApps into `spectrum_prb_control_t` (used by the SM control handler).
|
||||
- `spectrum_encode_ran_function_data()` — encode the RAN function metadata (ASN.1 or JSON) returned by `create_spectrum_sm_model()`.
|
||||
- `spectrum_free_decoded_control()` — free the decoded control payload returned by `spectrum_decode_prb_control()`.
|
||||
- `spectrum_sm_process_control()` — SM control handler invoked by the E3 framework to apply incoming control messages.
|
||||
- `create_spectrum_sm_model()` and `spectrum_sm_set_handle()` — SM registration and handle plumbing functions.
|
||||
|
||||
130
openair2/E3AP/service_models/spectrum_sm/spectrum_dec.c
Normal file
130
openair2/E3AP/service_models/spectrum_sm/spectrum_dec.c
Normal file
@@ -0,0 +1,130 @@
|
||||
#include "spectrum_dec.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
#include "Spectrum-PRBBlacklistControl.h"
|
||||
#else
|
||||
#include <json-c/json.h>
|
||||
#endif
|
||||
|
||||
spectrum_prb_control_t* spectrum_decode_prb_control(uint8_t *encoded_data, size_t encoded_size) {
|
||||
if (!encoded_data || encoded_size == 0) {
|
||||
LOG_E(E3AP, "[SPECTRUM] tried to decode an empty buffer\n");
|
||||
return NULL;
|
||||
}
|
||||
spectrum_prb_control_t *prb_control;
|
||||
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
// ASN.1 decoding implementation
|
||||
|
||||
// Decode ASN.1 structure
|
||||
Spectrum_PRBBlacklistControl_t *asn_control = NULL;
|
||||
asn_dec_rval_t dec_ret = aper_decode(0, &asn_DEF_Spectrum_PRBBlacklistControl,
|
||||
(void **)&asn_control, encoded_data, encoded_size, 0, 0);
|
||||
|
||||
if (dec_ret.code != RC_OK || !asn_control) {
|
||||
LOG_E(E3AP, "Failed to decode PRB blacklist control\n");
|
||||
if (asn_control) {
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// LOG_E(E3AP, "----------------- ASN1 DECODER PRINT START----------------- \n");
|
||||
// xer_fprint(stdout, &asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
// LOG_E(E3AP, "----------------- ASN1 DECODER PRINT END ----------------- \n");
|
||||
|
||||
// Allocate decoded control structure
|
||||
prb_control = malloc(sizeof(spectrum_prb_control_t));
|
||||
if (!prb_control) {
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
LOG_E(E3AP, "Failed to malloc structure\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prb_control->blacklisted_prbs = NULL;
|
||||
prb_control->prb_count = 0;
|
||||
|
||||
const int n = asn_control->blacklistedPRBs.list.count;
|
||||
if (n < 0 || n > 273) {
|
||||
LOG_E(E3AP, "Invalid PRB list count: %d\n", n);
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
free(prb_control);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prb_control->prb_count = (uint32_t)n;
|
||||
|
||||
if (prb_control->prb_count > 0) {
|
||||
prb_control->blacklisted_prbs = malloc((size_t)prb_control->prb_count * sizeof(uint16_t));
|
||||
if (!prb_control->blacklisted_prbs) {
|
||||
LOG_E(E3AP, "Failed to malloc PRB list\n");
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
free(prb_control);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
for (int i = 0; i < n; ++i) {
|
||||
if (asn_control->blacklistedPRBs.list.array[i] == NULL) {
|
||||
prb_control->blacklisted_prbs[i] = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
long v = *(asn_control->blacklistedPRBs.list.array[i]);
|
||||
|
||||
if (v < 0 || v > 272) {
|
||||
LOG_E(E3AP, "Invalid PRB value at index %d: %ld\n", i, v);
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
free(prb_control->blacklisted_prbs);
|
||||
free(prb_control);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
prb_control->blacklisted_prbs[i] = (uint16_t)v;
|
||||
}
|
||||
}
|
||||
|
||||
// Extract optional sampling threshold
|
||||
prb_control->sampling_threshold = 0;
|
||||
if (asn_control->samplingThreshold) {
|
||||
prb_control->sampling_threshold = *(asn_control->samplingThreshold);
|
||||
}
|
||||
|
||||
// Extract optional validity period
|
||||
prb_control->validity_period = 0;
|
||||
if (asn_control->validityPeriod) {
|
||||
prb_control->validity_period = *(asn_control->validityPeriod);
|
||||
}
|
||||
|
||||
// Cleanup ASN.1 structure
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_PRBBlacklistControl, asn_control);
|
||||
|
||||
LOG_D(E3AP, "Decoded PRB blacklist control: %u PRBs, sampling threshold %u, validity %u seconds\n",
|
||||
prb_control->prb_count, prb_control->sampling_threshold, prb_control->validity_period);
|
||||
|
||||
return prb_control;
|
||||
|
||||
#else
|
||||
// JSON decoding implementation TBD
|
||||
return NULL;
|
||||
#endif
|
||||
}
|
||||
|
||||
void spectrum_free_decoded_control(spectrum_prb_control_t *prb_control) {
|
||||
if (!prb_control) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Free the dynamically allocated blacklisted_prbs array
|
||||
if (prb_control->blacklisted_prbs) {
|
||||
free(prb_control->blacklisted_prbs);
|
||||
prb_control->blacklisted_prbs = NULL;
|
||||
}
|
||||
|
||||
// Free the structure itself
|
||||
free(prb_control);
|
||||
}
|
||||
43
openair2/E3AP/service_models/spectrum_sm/spectrum_dec.h
Normal file
43
openair2/E3AP/service_models/spectrum_sm/spectrum_dec.h
Normal file
@@ -0,0 +1,43 @@
|
||||
#ifndef SPECTRUM_DEC_H
|
||||
#define SPECTRUM_DEC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
/**
|
||||
* Spectrum SM Decoding Functions
|
||||
*
|
||||
* Provides decoding for spectrum control messages
|
||||
* with compile-time format selection (ASN.1 or JSON)
|
||||
*/
|
||||
|
||||
// Forward declarations for decoded structures
|
||||
typedef struct spectrum_prb_control_s spectrum_prb_control_t;
|
||||
|
||||
/**
|
||||
* Decoded PRB blacklist control structure
|
||||
*/
|
||||
typedef struct spectrum_prb_control_s {
|
||||
uint16_t *blacklisted_prbs;
|
||||
uint32_t prb_count;
|
||||
uint32_t sampling_threshold; // 0 if not specified
|
||||
uint32_t validity_period; // 0 if not specified
|
||||
} spectrum_prb_control_t;
|
||||
|
||||
/**
|
||||
* Decode PRB blacklist control specifically
|
||||
*
|
||||
* @param encoded_data Input encoded data
|
||||
* @param encoded_size Size of encoded data
|
||||
* @return spectrum_decode_prb_control on success, NULL on error
|
||||
*/
|
||||
spectrum_prb_control_t* spectrum_decode_prb_control(uint8_t *encoded_data, size_t encoded_size);
|
||||
|
||||
/**
|
||||
* Free a decoded PRB control structure
|
||||
*
|
||||
* @param prb_control Pointer to the structure to free
|
||||
*/
|
||||
void spectrum_free_decoded_control(spectrum_prb_control_t *prb_control);
|
||||
|
||||
#endif // SPECTRUM_DEC_H
|
||||
291
openair2/E3AP/service_models/spectrum_sm/spectrum_enc.c
Normal file
291
openair2/E3AP/service_models/spectrum_sm/spectrum_enc.c
Normal file
@@ -0,0 +1,291 @@
|
||||
#include "spectrum_enc.h"
|
||||
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#include <libe3/error_codes.h>
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#define ENCODE_BUFFER_SIZE 32768 // 32KB - allows 16KB IQ data + ASN.1 overhead
|
||||
#define RAN_FUNCTION_BUFFER_SIZE 512
|
||||
#define SPECTRUM_RAN_FUNCTION_NAME "spectrum_sm"
|
||||
#define SPECTRUM_RAN_FUNCTION_DESCRIPTION "Spectrum service model for IQ indication and PRB blacklist control"
|
||||
#define SPECTRUM_RAN_FUNCTION_VERSION 1
|
||||
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
/**
|
||||
* ASN.1 encoding implementation
|
||||
*/
|
||||
|
||||
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
|
||||
uint8_t **encoded_data, size_t *encoded_size) {
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Starting encoding: iq_size=%zu, timestamp=%u\n", iq_size, timestamp);
|
||||
|
||||
if (!iq_data || iq_size == 0 || !encoded_data || !encoded_size) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Invalid parameters: iq_data=%p, iq_size=%zu, encoded_data=%p, encoded_size=%p\n",
|
||||
iq_data, iq_size, encoded_data, encoded_size);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Create ASN.1 structure
|
||||
Spectrum_IQDataIndication_t *indication = calloc(1, sizeof(Spectrum_IQDataIndication_t));
|
||||
if (!indication) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate indication structure\n");
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Allocated indication structure at %p\n", indication);
|
||||
|
||||
// Set IQ samples
|
||||
indication->iqSamples.buf = malloc(iq_size);
|
||||
if (!indication->iqSamples.buf) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate IQ buffer of size %zu\n", iq_size);
|
||||
free(indication);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
memcpy(indication->iqSamples.buf, iq_data, iq_size);
|
||||
indication->iqSamples.size = iq_size;
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Set IQ samples: buf=%p, size=%zu\n",
|
||||
indication->iqSamples.buf, indication->iqSamples.size);
|
||||
|
||||
// Set sample count (assuming int32_t samples)
|
||||
indication->sampleCount = iq_size / sizeof(int32_t);
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Sample count: %ld (iq_size=%zu / sizeof(int32_t)=%zu)\n",
|
||||
indication->sampleCount, iq_size, sizeof(int32_t));
|
||||
|
||||
// Set optional timestamp
|
||||
if (timestamp > 0) {
|
||||
indication->timestamp = malloc(sizeof(long));
|
||||
if (indication->timestamp) {
|
||||
*(indication->timestamp) = (long)timestamp;
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Set timestamp: %ld\n", *(indication->timestamp));
|
||||
} else {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate timestamp\n");
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
} else {
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] No timestamp set (timestamp=0)\n");
|
||||
}
|
||||
|
||||
// Validate structure before encoding
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Pre-encode validation:\n");
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] - iqSamples.buf: %p\n", indication->iqSamples.buf);
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] - iqSamples.size: %zu\n", indication->iqSamples.size);
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] - sampleCount: %ld\n", indication->sampleCount);
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] - timestamp: %p\n", indication->timestamp);
|
||||
|
||||
// Allocate buffer for encoding
|
||||
uint8_t *buffer = malloc(ENCODE_BUFFER_SIZE);
|
||||
if (!buffer) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate encoding buffer of size %d\n", ENCODE_BUFFER_SIZE);
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Allocated encoding buffer: %p, size: %d\n", buffer, ENCODE_BUFFER_SIZE);
|
||||
|
||||
// Encode to buffer
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Calling aper_encode_to_buffer...\n");
|
||||
asn_enc_rval_t enc_ret = aper_encode_to_buffer(&asn_DEF_Spectrum_IQDataIndication,
|
||||
NULL, indication, buffer, ENCODE_BUFFER_SIZE);
|
||||
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Encoding result: encoded=%ld, failed_type=%p, structure_ptr=%p\n",
|
||||
enc_ret.encoded, enc_ret.failed_type, enc_ret.structure_ptr);
|
||||
|
||||
if (enc_ret.encoded == -1) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to encode Spectrum IQ indication\n");
|
||||
if (enc_ret.failed_type) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed type: %s\n", enc_ret.failed_type->name);
|
||||
}
|
||||
if (enc_ret.structure_ptr) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed at structure: %p\n", enc_ret.structure_ptr);
|
||||
}
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Data size may exceed buffer (iq_size=%zu, buffer=%d)\n",
|
||||
iq_size, ENCODE_BUFFER_SIZE);
|
||||
free(buffer);
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
*encoded_size = (enc_ret.encoded + 7) / 8; // Convert bits to bytes
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Encoded bits: %ld, bytes: %zu\n", enc_ret.encoded, *encoded_size);
|
||||
|
||||
*encoded_data = malloc(*encoded_size);
|
||||
if (!(*encoded_data)) {
|
||||
LOG_E(E3AP, "[SPECTRUM_ENC] Failed to allocate output buffer of size %zu\n", *encoded_size);
|
||||
free(buffer);
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
memcpy(*encoded_data, buffer, *encoded_size);
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Copied %zu bytes to output buffer at %p\n", *encoded_size, *encoded_data);
|
||||
free(buffer);
|
||||
|
||||
// Cleanup
|
||||
ASN_STRUCT_FREE(asn_DEF_Spectrum_IQDataIndication, indication);
|
||||
|
||||
LOG_D(E3AP, "[SPECTRUM_ENC] Successfully encoded spectrum indication: %zu bytes\n", *encoded_size);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size)
|
||||
{
|
||||
if (!encoded_data || !encoded_size) {
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
*encoded_data = NULL;
|
||||
*encoded_size = 0;
|
||||
|
||||
Spectrum_RanFunctionData_t rf_data;
|
||||
memset(&rf_data, 0, sizeof(rf_data));
|
||||
|
||||
const size_t name_len = strlen(SPECTRUM_RAN_FUNCTION_NAME);
|
||||
const size_t desc_len = strlen(SPECTRUM_RAN_FUNCTION_DESCRIPTION);
|
||||
|
||||
rf_data.name.buf = malloc(name_len);
|
||||
rf_data.description.buf = malloc(desc_len);
|
||||
if (!rf_data.name.buf || !rf_data.description.buf) {
|
||||
free(rf_data.name.buf);
|
||||
free(rf_data.description.buf);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(rf_data.name.buf, SPECTRUM_RAN_FUNCTION_NAME, name_len);
|
||||
rf_data.name.size = name_len;
|
||||
rf_data.version = SPECTRUM_RAN_FUNCTION_VERSION;
|
||||
memcpy(rf_data.description.buf, SPECTRUM_RAN_FUNCTION_DESCRIPTION, desc_len);
|
||||
rf_data.description.size = desc_len;
|
||||
|
||||
uint8_t buffer[RAN_FUNCTION_BUFFER_SIZE];
|
||||
asn_enc_rval_t enc_ret = aper_encode_to_buffer(&asn_DEF_Spectrum_RanFunctionData,
|
||||
NULL,
|
||||
&rf_data,
|
||||
buffer,
|
||||
sizeof(buffer));
|
||||
|
||||
free(rf_data.name.buf);
|
||||
free(rf_data.description.buf);
|
||||
|
||||
if (enc_ret.encoded == -1) {
|
||||
return E3_ENCODE_FAILED;
|
||||
}
|
||||
|
||||
*encoded_size = (enc_ret.encoded + 7) / 8;
|
||||
*encoded_data = malloc(*encoded_size);
|
||||
if (!(*encoded_data)) {
|
||||
*encoded_size = 0;
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(*encoded_data, buffer, *encoded_size);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
#else
|
||||
/**
|
||||
* JSON encoding implementation
|
||||
*/
|
||||
|
||||
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
|
||||
uint8_t **encoded_data, size_t *encoded_size) {
|
||||
if (!iq_data || iq_size == 0 || !encoded_data || !encoded_size) {
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Create JSON object
|
||||
json_object *indication = json_object_new_object();
|
||||
if (!indication) {
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
// Convert IQ data to base64 or hex string for JSON
|
||||
// For simplicity, we'll use hex encoding
|
||||
size_t hex_size = iq_size * 2 + 1;
|
||||
char *hex_string = malloc(hex_size);
|
||||
if (!hex_string) {
|
||||
json_object_put(indication);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
uint8_t *data_bytes = (uint8_t *)iq_data;
|
||||
for (size_t i = 0; i < iq_size; i++) {
|
||||
sprintf(hex_string + (i * 2), "%02x", data_bytes[i]);
|
||||
}
|
||||
hex_string[hex_size - 1] = '\0';
|
||||
|
||||
// Add fields to JSON
|
||||
json_object_object_add(indication, "iqSamples", json_object_new_string(hex_string));
|
||||
json_object_object_add(indication, "sampleCount",
|
||||
json_object_new_int64(iq_size / sizeof(int32_t)));
|
||||
|
||||
if (timestamp > 0) {
|
||||
json_object_object_add(indication, "timestamp", json_object_new_int64(timestamp));
|
||||
}
|
||||
|
||||
// Convert JSON to string
|
||||
const char *json_string = json_object_to_json_string(indication);
|
||||
if (!json_string) {
|
||||
free(hex_string);
|
||||
json_object_put(indication);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
// Allocate output buffer
|
||||
*encoded_size = strlen(json_string);
|
||||
*encoded_data = malloc(*encoded_size);
|
||||
if (!*encoded_data) {
|
||||
free(hex_string);
|
||||
json_object_put(indication);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(*encoded_data, json_string, *encoded_size);
|
||||
|
||||
// Cleanup
|
||||
free(hex_string);
|
||||
json_object_put(indication);
|
||||
|
||||
LOG_D(E3AP, "Encoded spectrum indication (JSON): %zu bytes\n", *encoded_size);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size)
|
||||
{
|
||||
if (!encoded_data || !encoded_size) {
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
*encoded_data = NULL;
|
||||
*encoded_size = 0;
|
||||
|
||||
json_object *rf_data = json_object_new_object();
|
||||
if (!rf_data) {
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
json_object_object_add(rf_data, "name", json_object_new_string(SPECTRUM_RAN_FUNCTION_NAME));
|
||||
json_object_object_add(rf_data, "version", json_object_new_int(SPECTRUM_RAN_FUNCTION_VERSION));
|
||||
json_object_object_add(rf_data, "description",
|
||||
json_object_new_string(SPECTRUM_RAN_FUNCTION_DESCRIPTION));
|
||||
|
||||
const char *json_string = json_object_to_json_string(rf_data);
|
||||
if (!json_string) {
|
||||
json_object_put(rf_data);
|
||||
return E3_ENCODE_FAILED;
|
||||
}
|
||||
|
||||
*encoded_size = strlen(json_string);
|
||||
*encoded_data = malloc(*encoded_size);
|
||||
if (!(*encoded_data)) {
|
||||
json_object_put(rf_data);
|
||||
*encoded_size = 0;
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
memcpy(*encoded_data, json_string, *encoded_size);
|
||||
json_object_put(rf_data);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
#endif
|
||||
46
openair2/E3AP/service_models/spectrum_sm/spectrum_enc.h
Normal file
46
openair2/E3AP/service_models/spectrum_sm/spectrum_enc.h
Normal file
@@ -0,0 +1,46 @@
|
||||
#ifndef SPECTRUM_ENC_H
|
||||
#define SPECTRUM_ENC_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
/**
|
||||
* Spectrum SM Encoding Functions
|
||||
*
|
||||
* Provides encoding for spectrum indication and control messages
|
||||
* with compile-time format selection (ASN.1 or JSON)
|
||||
*/
|
||||
|
||||
// Compile-time format selection
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
#include "Spectrum-IQDataIndication.h"
|
||||
#include "Spectrum-PRBBlacklistControl.h"
|
||||
#include "Spectrum-ConfigControl.h"
|
||||
#include "Spectrum-RanFunctionData.h"
|
||||
#endif
|
||||
|
||||
#ifdef SPECTRUM_SM_JSON_FORMAT
|
||||
#include <json-c/json.h>
|
||||
#endif
|
||||
|
||||
/**
|
||||
* Encode spectrum IQ data indication message
|
||||
*
|
||||
* @param iq_data Raw IQ data buffer
|
||||
* @param iq_size Size of IQ data
|
||||
* @param timestamp Timestamp when data was captured
|
||||
* @param encoded_data Output buffer (allocated by function)
|
||||
* @param encoded_size Output size
|
||||
* @return 0 on success, negative on error
|
||||
*/
|
||||
int spectrum_encode_indication(void *iq_data, size_t iq_size, uint32_t timestamp,
|
||||
uint8_t **encoded_data, size_t *encoded_size);
|
||||
|
||||
/**
|
||||
* Encode Spectrum RAN Function data for setup/registration.
|
||||
*/
|
||||
int spectrum_encode_ran_function_data(uint8_t **encoded_data, size_t *encoded_size);
|
||||
|
||||
|
||||
#endif // SPECTRUM_ENC_H
|
||||
607
openair2/E3AP/service_models/spectrum_sm/spectrum_sm.c
Normal file
607
openair2/E3AP/service_models/spectrum_sm/spectrum_sm.c
Normal file
@@ -0,0 +1,607 @@
|
||||
#include "spectrum_sm.h"
|
||||
#include "spectrum_enc.h"
|
||||
#include "spectrum_dec.h"
|
||||
|
||||
#include "../../e3_agent.h"
|
||||
|
||||
#include "database.h"
|
||||
#include "event.h"
|
||||
#include "handler.h"
|
||||
#include "utils.h"
|
||||
#include "event_selector.h"
|
||||
#include "configuration.h"
|
||||
#include "logger/logger.h"
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// T-tracer constants
|
||||
#define DEFAULT_REMOTE_IP "127.0.0.1"
|
||||
#define DEFAULT_REMOTE_PORT 2021
|
||||
|
||||
// SM default constants
|
||||
#define DEFAULT_SM_SAMPLING_THRESHOLD 5
|
||||
|
||||
e3_sm_spectrum_control_t *e3_sm_spectrum_control = NULL;
|
||||
static spectrum_sm_context_t spectrum_ctx = {0};
|
||||
static uint8_t *spectrum_ran_function_data = NULL;
|
||||
static size_t spectrum_ran_function_data_len = 0;
|
||||
static int spectrum_ran_function_data_ready = 0;
|
||||
|
||||
|
||||
// Spectrum SM RAN function IDs
|
||||
#define SPECTRUM_SM_RAN_FUNCTION_ID 1
|
||||
|
||||
static uint32_t spectrum_telemetry_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
|
||||
static uint32_t spectrum_control_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
|
||||
|
||||
/* this macro looks for a particular element and checks its type */
|
||||
#define GET_DATA_FROM_TRACER(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) { \
|
||||
LOG_E(E3AP, "bad type for %s\n", var_name); \
|
||||
return NULL; \
|
||||
} \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_init(void *sm_context);
|
||||
static void spectrum_sm_destroy(void *sm_context);
|
||||
static e3_error_t spectrum_sm_start(void *sm_context);
|
||||
static void spectrum_sm_stop(void *sm_context);
|
||||
static int spectrum_sm_is_running(void *sm_context);
|
||||
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
|
||||
void* sm_context,
|
||||
uint32_t request_message_id,
|
||||
uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
uint32_t control_id,
|
||||
const uint8_t* data,
|
||||
size_t data_len);
|
||||
|
||||
static e3_c_service_model_desc_t spectrum_sm_desc = {
|
||||
.name = "spectrum_sm",
|
||||
.version = 1,
|
||||
.ran_function_id = SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
.telemetry_ids = spectrum_telemetry_ids,
|
||||
.telemetry_ids_len = sizeof(spectrum_telemetry_ids) / sizeof(spectrum_telemetry_ids[0]),
|
||||
.control_ids = spectrum_control_ids,
|
||||
.control_ids_len = sizeof(spectrum_control_ids) / sizeof(spectrum_control_ids[0]),
|
||||
.ran_function_data = NULL,
|
||||
.ran_function_data_len = 0,
|
||||
.sm_init = spectrum_sm_init,
|
||||
.sm_destroy = spectrum_sm_destroy,
|
||||
.sm_start = spectrum_sm_start,
|
||||
.sm_stop = spectrum_sm_stop,
|
||||
.sm_is_running = spectrum_sm_is_running,
|
||||
.sm_process_control = spectrum_sm_process_control,
|
||||
.sm_context = &spectrum_ctx,
|
||||
};
|
||||
|
||||
static int is_running_locked(const spectrum_sm_context_t *ctx)
|
||||
{
|
||||
return ctx->running ? 1 : 0;
|
||||
}
|
||||
|
||||
void spectrum_sm_set_handle(e3_service_model_handle_t *sm_handle)
|
||||
{
|
||||
pthread_mutex_lock(&spectrum_ctx.lock);
|
||||
spectrum_ctx.sm_handle = sm_handle;
|
||||
pthread_mutex_unlock(&spectrum_ctx.lock);
|
||||
}
|
||||
|
||||
e3_c_service_model_desc_t *create_spectrum_sm_model(void)
|
||||
{
|
||||
if (!spectrum_ran_function_data_ready) {
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with ASN.1 encoder\n");
|
||||
#else
|
||||
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with JSON encoder\n");
|
||||
#endif
|
||||
int rc = spectrum_encode_ran_function_data(&spectrum_ran_function_data,
|
||||
&spectrum_ran_function_data_len);
|
||||
if (rc != E3_SUCCESS) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to encode RAN function data (%d)\n", rc);
|
||||
spectrum_ran_function_data = NULL;
|
||||
spectrum_ran_function_data_len = 0;
|
||||
}
|
||||
spectrum_ran_function_data_ready = 1;
|
||||
}
|
||||
|
||||
spectrum_sm_desc.ran_function_data = spectrum_ran_function_data;
|
||||
spectrum_sm_desc.ran_function_data_len = spectrum_ran_function_data_len;
|
||||
return &spectrum_sm_desc;
|
||||
}
|
||||
|
||||
static void activate_traces(int socket, int number_of_events, int *is_on)
|
||||
{
|
||||
char t = 1;
|
||||
if (socket_send(socket, &t, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to activate T-tracer events\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I(E3AP, "[SPECTRUM] T-tracer events activated\n");
|
||||
}
|
||||
|
||||
static void *t_tracer_connector_thread_main(void *context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int keep_running = ctx->connector_running ? 1 : 0;
|
||||
int has_socket = (ctx->t_tracer_socket >= 0) ? 1 : 0;
|
||||
int prepared = (ctx->database != NULL && ctx->event_flags != NULL) ? 1 : 0;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!keep_running) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!prepared) {
|
||||
char *database_filename = T_MESSAGES_PATH;
|
||||
LOG_D(E3AP, "[SPECTRUM] Loading T-tracer database\n");
|
||||
void *db = parse_database(database_filename);
|
||||
if (!db) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to parse T-tracer database\n");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
load_config_file(database_filename);
|
||||
|
||||
int n_events = number_of_ids(db);
|
||||
int *flags = calloc(n_events, sizeof(int));
|
||||
if (!flags) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to allocate event flags array\n");
|
||||
free_database(db);
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (!ctx->database && !ctx->event_flags) {
|
||||
ctx->database = db;
|
||||
ctx->number_of_events = n_events;
|
||||
ctx->event_flags = flags;
|
||||
} else {
|
||||
free(flags);
|
||||
free_database(db);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (has_socket) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int s = try_connect_to((char *)DEFAULT_REMOTE_IP, DEFAULT_REMOTE_PORT);
|
||||
if (s < 0) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int should_activate = 0;
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->t_tracer_socket < 0) {
|
||||
ctx->t_tracer_socket = s;
|
||||
ctx->tracer_ready = true;
|
||||
should_activate = 1;
|
||||
LOG_I(E3AP, "[SPECTRUM] Connected to T-tracer at %s:%d\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT);
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (should_activate) {
|
||||
/* Keep old init behavior: activate traces right after successful connect. */
|
||||
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int wait_for_t_tracer_ready(spectrum_sm_context_t *ctx)
|
||||
{
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
int sock = ctx->t_tracer_socket;
|
||||
int ready = ctx->tracer_ready ? 1 : 0;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!running) {
|
||||
return -1;
|
||||
}
|
||||
if (ready && sock >= 0) {
|
||||
return sock;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void *spectrum_sm_thread_main(void *context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
|
||||
OBUF ebuf = {osize : 0, omaxsize : 0, obuf : NULL};
|
||||
|
||||
if (!ctx || !ctx->initialized) {
|
||||
LOG_E(E3AP, "[SPECTRUM] SM not initialized, exiting worker\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to set SIGPIPE handler\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wait_for_t_tracer_ready(ctx) < 0) {
|
||||
LOG_E(E3AP, "[SPECTRUM] T-tracer not ready, exiting worker\n");
|
||||
return NULL;
|
||||
}
|
||||
on_off(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL", ctx->event_flags, 1);
|
||||
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
|
||||
|
||||
ctx->iq_data_event_id = event_id_from_name(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL");
|
||||
database_event_format f = get_format(ctx->database, ctx->iq_data_event_id);
|
||||
|
||||
int data_index = -1;
|
||||
for (int i = 0; i < f.count; i++) {
|
||||
GET_DATA_FROM_TRACER("rxdata", "buffer", data_index);
|
||||
}
|
||||
|
||||
if (data_index == -1) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to find rxdata in event format\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_I(E3AP, "[SPECTRUM] Start main extraction loop\n");
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
e3_service_model_handle_t *sm_handle = ctx->sm_handle;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!running) {
|
||||
LOG_D(E3AP, "[SPECTRUM] Worker loop stopping (running=0)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->t_tracer_socket < 0 && wait_for_t_tracer_ready(ctx) < 0) {
|
||||
LOG_E(E3AP, "[SPECTRUM] T-tracer became unavailable, stopping worker loop\n");
|
||||
break;
|
||||
}
|
||||
|
||||
event e = get_event(ctx->t_tracer_socket, &ebuf, ctx->database);
|
||||
if (e.type == -1) {
|
||||
LOG_W(E3AP, "[SPECTRUM] T-tracer connection lost\n");
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->t_tracer_socket >= 0) {
|
||||
close(ctx->t_tracer_socket);
|
||||
ctx->t_tracer_socket = -1;
|
||||
ctx->tracer_ready = false;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type != ctx->iq_data_event_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sm_handle) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Missing SM handle, cannot emit indication\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_D(E3AP,
|
||||
"[SPECTRUM] IQ event received (sampling handled in PHY producer, threshold=%u)\n",
|
||||
e3_sm_spectrum_control->sampling_threshold);
|
||||
|
||||
uint8_t *encoded_data = NULL;
|
||||
size_t encoded_size = 0;
|
||||
LOG_D(E3AP, "[SPECTRUM] Encoding indication from rxdata buffer (%d bytes)\n",
|
||||
e.e[data_index].bsize);
|
||||
int rc = spectrum_encode_indication(e.e[data_index].b,
|
||||
e.e[data_index].bsize,
|
||||
0,
|
||||
&encoded_data,
|
||||
&encoded_size);
|
||||
if (rc != E3_SUCCESS || !encoded_data) {
|
||||
LOG_E(E3AP,
|
||||
"[SPECTRUM] Failed to encode indication (rc=%d, rxdata_size=%d)\n",
|
||||
rc,
|
||||
e.e[data_index].bsize);
|
||||
continue;
|
||||
}
|
||||
LOG_D(E3AP, "[SPECTRUM] Encoded indication payload (%zu bytes)\n", encoded_size);
|
||||
|
||||
size_t dapp_len = 0;
|
||||
uint32_t *dapp_ids = e3_agent_get_ran_function_subscribers(e3.agent,
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
&dapp_len);
|
||||
LOG_D(E3AP, "[SPECTRUM] Found %zu subscribers for ran_function_id=%u\n",
|
||||
dapp_len,
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID);
|
||||
|
||||
if (dapp_ids) {
|
||||
if (dapp_len > 0) {
|
||||
for (size_t i = 0; i < dapp_len; ++i) {
|
||||
e3_error_t emit_rc =
|
||||
e3_service_model_emit_indication(sm_handle, dapp_ids[i], SPECTRUM_SM_RAN_FUNCTION_ID, encoded_data, encoded_size);
|
||||
if (emit_rc != E3_SUCCESS) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to emit indication for dApp %u (err=%d)\n", dapp_ids[i], emit_rc);
|
||||
} else {
|
||||
LOG_D(E3AP, "[SPECTRUM] Emitted indication to dApp %u (%zu bytes)\n", dapp_ids[i], encoded_size);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t total_subscriptions = e3_agent_subscription_count(e3.agent);
|
||||
size_t registered_dapp_len = 0;
|
||||
uint32_t *registered_dapps = e3_agent_get_registered_dapps(e3.agent, ®istered_dapp_len);
|
||||
LOG_E(E3AP,
|
||||
"[SPECTRUM] No subscribers for ran_function_id=%u "
|
||||
"(total_subscriptions=%zu, registered_dapps=%zu)\n",
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
total_subscriptions,
|
||||
registered_dapp_len);
|
||||
if (registered_dapps) {
|
||||
e3_agent_free_uint32_array(registered_dapps);
|
||||
}
|
||||
}
|
||||
}
|
||||
e3_agent_free_uint32_array(dapp_ids);
|
||||
|
||||
free(encoded_data);
|
||||
}
|
||||
|
||||
if (ebuf.obuf) {
|
||||
free(ebuf.obuf);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_init(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Preserve handle set before SM registration; memset below would clear it. */
|
||||
e3_service_model_handle_t *preset_sm_handle = ctx->sm_handle;
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->t_tracer_socket = -1;
|
||||
ctx->sm_handle = preset_sm_handle;
|
||||
pthread_mutex_init(&ctx->lock, NULL);
|
||||
ctx->initialized = true;
|
||||
LOG_D(E3AP, "[SPECTRUM] SM init complete (preset_sm_handle=%p)\n", (void *)ctx->sm_handle);
|
||||
|
||||
ctx->connector_running = true;
|
||||
if (pthread_create(&ctx->connector_thread, NULL, t_tracer_connector_thread_main, ctx) != 0) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_THREAD_FAILED;
|
||||
}
|
||||
ctx->connector_started = true;
|
||||
|
||||
e3_sm_spectrum_control = calloc(1, sizeof(*e3_sm_spectrum_control));
|
||||
if (!e3_sm_spectrum_control) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
e3_sm_spectrum_control->action_list = calloc(MAX_BWP_SIZE, sizeof(uint16_t));
|
||||
if (!e3_sm_spectrum_control->action_list) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&e3_sm_spectrum_control->mutex, NULL);
|
||||
e3_sm_spectrum_control->sampling_threshold = DEFAULT_SM_SAMPLING_THRESHOLD;
|
||||
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_start(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx || !ctx->initialized) {
|
||||
return E3_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->running) {
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
ctx->running = true;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (pthread_create(&ctx->worker_thread, NULL, spectrum_sm_thread_main, ctx) != 0) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return E3_SM_ERROR_THREAD_FAILED;
|
||||
}
|
||||
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
static void spectrum_sm_stop(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int was_running = ctx->running;
|
||||
ctx->running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (was_running) {
|
||||
pthread_cancel(ctx->worker_thread);
|
||||
pthread_join(ctx->worker_thread, NULL);
|
||||
}
|
||||
|
||||
if (ctx->connector_started) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->connector_running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
pthread_join(ctx->connector_thread, NULL);
|
||||
ctx->connector_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int spectrum_sm_is_running(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return 0;
|
||||
}
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return running;
|
||||
}
|
||||
|
||||
static void spectrum_sm_destroy(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
spectrum_sm_stop(sm_context);
|
||||
|
||||
if (ctx->event_flags) {
|
||||
free(ctx->event_flags);
|
||||
ctx->event_flags = NULL;
|
||||
}
|
||||
|
||||
if (ctx->database) {
|
||||
free_database(ctx->database);
|
||||
ctx->database = NULL;
|
||||
}
|
||||
|
||||
if (ctx->t_tracer_socket >= 0) {
|
||||
close(ctx->t_tracer_socket);
|
||||
ctx->t_tracer_socket = -1;
|
||||
}
|
||||
|
||||
if (e3_sm_spectrum_control) {
|
||||
if (e3_sm_spectrum_control->action_list) {
|
||||
free(e3_sm_spectrum_control->action_list);
|
||||
e3_sm_spectrum_control->action_list = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&e3_sm_spectrum_control->mutex);
|
||||
free(e3_sm_spectrum_control);
|
||||
e3_sm_spectrum_control = NULL;
|
||||
}
|
||||
|
||||
if (ctx->initialized) {
|
||||
pthread_mutex_destroy(&ctx->lock);
|
||||
}
|
||||
|
||||
if (spectrum_ran_function_data) {
|
||||
free(spectrum_ran_function_data);
|
||||
spectrum_ran_function_data = NULL;
|
||||
spectrum_ran_function_data_len = 0;
|
||||
spectrum_ran_function_data_ready = 0;
|
||||
spectrum_sm_desc.ran_function_data = NULL;
|
||||
spectrum_sm_desc.ran_function_data_len = 0;
|
||||
}
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->t_tracer_socket = -1;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
|
||||
void* sm_context,
|
||||
uint32_t request_message_id,
|
||||
uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
uint32_t control_id,
|
||||
const uint8_t* data,
|
||||
size_t data_len)
|
||||
{
|
||||
(void)dapp_id;
|
||||
int response_code = 0; // Positive
|
||||
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx || !sm_handle || ran_function_id != SPECTRUM_SM_RAN_FUNCTION_ID ||
|
||||
control_id != SPECTRUM_SM_RAN_FUNCTION_ID) {
|
||||
if (sm_handle) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
}
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->sm_handle = sm_handle;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!data || data_len == 0 || !e3_sm_spectrum_control) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
spectrum_prb_control_t* control_payload =
|
||||
spectrum_decode_prb_control((uint8_t *)data, data_len);
|
||||
if (!control_payload) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const size_t max_elems = MAX_BWP_SIZE;
|
||||
size_t elems = control_payload->prb_count;
|
||||
if (elems > max_elems) {
|
||||
elems = max_elems;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
uint16_t *alist16 = e3_sm_spectrum_control->action_list;
|
||||
memcpy(alist16, control_payload->blacklisted_prbs, elems * sizeof(uint16_t));
|
||||
e3_sm_spectrum_control->action_size = (int)elems;
|
||||
|
||||
memset(e3_sm_spectrum_control->dyn_prbbl, 0, max_elems * sizeof(uint16_t));
|
||||
for (size_t j = 0; j < elems; ++j) {
|
||||
uint16_t prb = alist16[j];
|
||||
if (prb < MAX_BWP_SIZE) {
|
||||
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
|
||||
}
|
||||
}
|
||||
|
||||
memset(alist16, 0, max_elems * sizeof(uint16_t));
|
||||
if (control_payload->sampling_threshold > 0) {
|
||||
e3_sm_spectrum_control->sampling_threshold = control_payload->sampling_threshold;
|
||||
}
|
||||
|
||||
e3_sm_spectrum_control->ready = 1;
|
||||
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
spectrum_free_decoded_control(control_payload);
|
||||
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
52
openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h
Normal file
52
openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h
Normal file
@@ -0,0 +1,52 @@
|
||||
#ifndef SPECTRUM_SM_H
|
||||
#define SPECTRUM_SM_H
|
||||
|
||||
#include <libe3/c_api.h>
|
||||
|
||||
#include "common/utils/nr/nr_common.h"
|
||||
#include <stdbool.h>
|
||||
#include <pthread.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
e3_c_service_model_desc_t* create_spectrum_sm_model(void);
|
||||
void spectrum_sm_set_handle(e3_service_model_handle_t *sm_handle);
|
||||
|
||||
/**
|
||||
* @brief E3 agent control variables
|
||||
* This struct is responsible of handling all the shared variables to enable intercommunication between the E3 agent and the rest of
|
||||
* the codebase
|
||||
*
|
||||
*/
|
||||
typedef struct e3_sm_spectrum_control {
|
||||
uint16_t* action_list;
|
||||
int action_size;
|
||||
uint16_t dyn_prbbl[MAX_BWP_SIZE];
|
||||
int ready;
|
||||
uint32_t sampling_threshold;
|
||||
uint32_t sampling_counter;
|
||||
pthread_mutex_t mutex;
|
||||
} e3_sm_spectrum_control_t;
|
||||
|
||||
extern e3_sm_spectrum_control_t* e3_sm_spectrum_control;
|
||||
|
||||
// Spectrum SM specific structures
|
||||
typedef struct {
|
||||
int t_tracer_socket;
|
||||
void *database;
|
||||
int *event_flags;
|
||||
int number_of_events;
|
||||
int iq_data_event_id;
|
||||
|
||||
pthread_t connector_thread;
|
||||
pthread_t worker_thread;
|
||||
pthread_mutex_t lock;
|
||||
bool connector_started;
|
||||
bool connector_running;
|
||||
bool tracer_ready;
|
||||
bool running;
|
||||
bool initialized;
|
||||
e3_service_model_handle_t *sm_handle;
|
||||
} spectrum_sm_context_t;
|
||||
|
||||
#endif // SPECTRUM_SM_H
|
||||
@@ -144,6 +144,11 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
for (int i = 0; i < num_beams; i++) {
|
||||
uint16_t *vrb_map_UL = cc[CC_id].vrb_map_UL[i];
|
||||
memcpy(&vrb_map_UL[prev_slot % size * MAX_BWP_SIZE], &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
|
||||
#ifdef E3_AGENT
|
||||
// This for downlink PRBs
|
||||
uint16_t *vrb_map = cc[CC_id].vrb_map[i];
|
||||
memcpy(vrb_map, &gNB->ulprbbl, sizeof(uint16_t) * MAX_BWP_SIZE);
|
||||
#endif // E3_AGENT
|
||||
}
|
||||
clear_nr_nfapi_information(gNB, CC_id, frame, slot);
|
||||
}
|
||||
@@ -166,6 +171,9 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
"periodical UE stats deactivated after reaching %d UEs, please check nrMAC_stats.log, or increase MACRLCs.[0].stats_max_ue\n",
|
||||
gNB->stats_max_ue);
|
||||
}
|
||||
#ifdef E3_AGENT
|
||||
nr_update_prb_policy(module_idP, frame, slot);
|
||||
#endif // E3_AGENT
|
||||
}
|
||||
|
||||
nr_measgap_scheduling(gNB, frame, slot);
|
||||
|
||||
@@ -20,6 +20,10 @@
|
||||
#include "executables/softmodem-common.h"
|
||||
#include "../../../nfapi/oai_integration/vendor_ext.h"
|
||||
|
||||
#ifdef E3_AGENT
|
||||
#include <openair2/E3AP/service_models/spectrum_sm/spectrum_sm.h>
|
||||
#endif // E3_AGENT
|
||||
|
||||
////////////////////////////////////////////////////////
|
||||
/////* DLSCH MAC PDU generation (6.1.2 TS 38.321) */////
|
||||
////////////////////////////////////////////////////////
|
||||
@@ -49,6 +53,30 @@ int get_dl_tda(const gNB_MAC_INST *nrmac, int slot)
|
||||
return 0; // if FDD or not mixed slot in TDD, for now use default TDA
|
||||
}
|
||||
|
||||
#ifdef E3_AGENT
|
||||
void nr_update_prb_policy(module_id_t module_idP, frame_t frame, sub_frame_t slot)
|
||||
{
|
||||
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
|
||||
|
||||
NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock);
|
||||
LOG_W(NR_MAC, "Barred_PRBs ");
|
||||
|
||||
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
|
||||
if (e3_sm_spectrum_control->ready) {
|
||||
memcpy(gNB->ulprbbl, e3_sm_spectrum_control->dyn_prbbl, MAX_BWP_SIZE * sizeof(uint16_t));
|
||||
e3_sm_spectrum_control->ready = 0; // Reset ready flag for next production
|
||||
}
|
||||
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
for (int i = 0; i < MAX_BWP_SIZE; i++) {
|
||||
if (gNB->ulprbbl[i] == 0x3FFF)
|
||||
printf("%d ", i);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
#endif // E3_AGENT
|
||||
|
||||
// Compute and write all MAC CEs and subheaders, and return number of written bytes
|
||||
int nr_write_ce_dlsch_pdu(module_id_t module_idP,
|
||||
const NR_UE_sched_ctrl_t *ue_sched_ctl,
|
||||
|
||||
@@ -110,6 +110,15 @@ const NR_tda_info_t *get_best_ul_tda(const gNB_MAC_INST *nrmac, int beam, const
|
||||
int start = check_rb_start;
|
||||
int len = check_rb_len;
|
||||
uint16_t tda_mask = SL_to_bitmap(tdas->startSymbolIndex, tdas->nrOfSymbols);
|
||||
|
||||
#ifdef E3_AGENT
|
||||
// Skip TDAs that use symbol 12 in slot 8 (reserved for spectrum sensing)
|
||||
if (slot == 8 && (tda_mask & (1 << 12))) {
|
||||
LOG_D(NR_MAC, "%4d.%2d skipping TDA (mask 0x%04x) overlapping spectrum sensing symbol\n", frame, slot, tda_mask);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
get_max_rb_range(vrb_map_UL, nrmac->ulprbbl, tda_mask, &start, &len);
|
||||
uint64_t s = (uint64_t)tdas->nrOfSymbols * len;
|
||||
if (s > score) {
|
||||
|
||||
@@ -49,6 +49,10 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, s
|
||||
|
||||
void nr_mac_update_timers(module_id_t module_id);
|
||||
|
||||
#ifdef E3_AGENT
|
||||
void nr_update_prb_policy(module_id_t module_id, frame_t frame, sub_frame_t slot);
|
||||
#endif // E3_AGENT
|
||||
|
||||
void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, slot_t slot_rxP, NR_Sched_Rsp_t *sched_info);
|
||||
|
||||
/* \brief main DL scheduler function. Calls a preprocessor to decide on
|
||||
|
||||
@@ -1099,7 +1099,13 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay, i
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
|
||||
// UL TDA index 1 in case of SRS
|
||||
if (do_SRS) {
|
||||
bool need_no_sym12_tda = do_SRS;
|
||||
#ifdef E3_AGENT
|
||||
// E3 spectrum sensing reads symbol 12 of slot 8, so a TDA without symbol 12
|
||||
// must always be available for get_best_ul_tda() to pick in slot 8.
|
||||
need_no_sym12_tda = true;
|
||||
#endif
|
||||
if (need_no_sym12_tda) {
|
||||
tda = set_TimeDomainResourceAllocation(k2, get_SLIV(0, 12));
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
}
|
||||
@@ -1153,7 +1159,7 @@ void nr_rrc_config_ul_tda(NR_ServingCellConfigCommon_t *scc, int min_fb_delay, i
|
||||
for (int i = k2 + 1; i <= N_ul; ++i) {
|
||||
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 13));
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
if (do_SRS) {
|
||||
if (need_no_sym12_tda) {
|
||||
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 12));
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
}
|
||||
|
||||
@@ -0,0 +1,295 @@
|
||||
Active_gNBs = ( "oai-gnb");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
sa = 1;
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
gNB_name = "oai-gnb";
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({
|
||||
mcc = 001;
|
||||
mnc = 01;
|
||||
mnc_length = 2;
|
||||
snssaiList = (
|
||||
{
|
||||
sst = 1, sd=0x0;
|
||||
},
|
||||
{
|
||||
sst = 1, sd=0xffffff;
|
||||
}
|
||||
);
|
||||
});
|
||||
nr_cellid = 1;
|
||||
|
||||
////////// Physical parameters:
|
||||
pdsch_AntennaPorts_XP = 2;
|
||||
pusch_AntennaPorts = 2;
|
||||
pdsch_AntennaPorts_N1 = 1;
|
||||
maxMIMO_layers = 2;
|
||||
do_CSIRS = 1;
|
||||
do_SRS = 0;
|
||||
do_SINR = 1;
|
||||
# sib1_tda = 15;
|
||||
|
||||
pdcch_ConfigSIB1 = (
|
||||
{
|
||||
controlResourceSetZero = 10;
|
||||
searchSpaceZero = 0;
|
||||
}
|
||||
);
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
physCellId = 0;
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741)
|
||||
absoluteFrequencySSB = 649920;
|
||||
dl_frequencyBand = 78;
|
||||
# this is 3300.24 MHz
|
||||
dl_absoluteFrequencyPointA = 648728;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
|
||||
initialDLBWPlocationAndBandwidth = 28875;
|
||||
#
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 10;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 28875;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 151;# testMAC 158; OAI gNB 98
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 12;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ra_ReponseWindow
|
||||
#1,2,4,8,10,20,40,80
|
||||
ra_ResponseWindow = 5;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
|
||||
#oneHalf (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
msg3_DeltaPreamble = 2;
|
||||
p0_NominalWithGrant = -90;
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
# ssb_PositionsInBurs_BitmapPR
|
||||
# 1=short, 2=medium, 3=long
|
||||
ssb_PositionsInBurst_PR = 2;
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6; #6;
|
||||
nrofDownlinkSlots = 7; #7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2; #2;
|
||||
nrofUplinkSymbols = 4;
|
||||
ssPBCH_BlockPower = 0;
|
||||
}
|
||||
);
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ({ ipv4 = "172.30.134.111"; });
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.130.0.20";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "10.130.0.20";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
}
|
||||
);
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 120;
|
||||
pucch_TargetSNRx10 = 260;
|
||||
ul_bler_target_upper=.15;
|
||||
ul_bler_target_lower=.05;
|
||||
pusch_FailureThres = 100;
|
||||
ul_max_mcs = 28;
|
||||
ulsch_max_frame_inactivity = 1;
|
||||
#pusch_RSSI_Threshold = -500;
|
||||
#pucch_RSSI_Threshold = -600;
|
||||
}
|
||||
);
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 180;
|
||||
pucch0_dtx_threshold = 80;
|
||||
pusch_dtx_threshold = 10;
|
||||
max_ldpc_iterations = 15;
|
||||
tx_amp_backoff_dB = 24; # needs to match O-RU configuration
|
||||
L1_rx_thread_core = 47;
|
||||
L1_tx_thread_core = 49; # relevant after merge of l1_tx_thread
|
||||
phase_compensation = 0; # needs to match O-RU configuration
|
||||
}
|
||||
);
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "no";
|
||||
nb_tx = 2;
|
||||
nb_rx = 2;
|
||||
att_tx = 0
|
||||
att_rx = 0;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 75;
|
||||
sf_extension = 0;
|
||||
eNB_instances = [0];
|
||||
ru_thread_core = 51;
|
||||
sl_ahead = 5;
|
||||
##beamforming 1x2 matrix: 1 layer x 2 antennas
|
||||
bf_weights = [0x00007fff, 0x0000,0x00007fff, 0x0000];
|
||||
tr_preference = "raw_if4p5"; # important: activate FHI7.2
|
||||
do_precoding = 0; # needs to match O-RU configuration
|
||||
}
|
||||
);
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="info";
|
||||
f1ap_log_level ="info";
|
||||
e3ap_log_level ="info";
|
||||
};
|
||||
|
||||
E3Configuration : {
|
||||
link = "zmq";
|
||||
transport = "tcp";
|
||||
};
|
||||
|
||||
|
||||
fhi_72 = {
|
||||
dpdk_devices = ("0000:a0:00.7", "0000:a0:01.7");
|
||||
system_core = 53;
|
||||
io_core = 55;
|
||||
worker_cores = (57);
|
||||
du_addr = ("56:89:79:bc:cd:3c", "56:89:79:bc:cd:3c");
|
||||
# ru_addr = ("b8:ce:f6:5e:6b:4a", "b8:ce:f6:5e:6b:4a");
|
||||
ru_addr = ("6c:ad:ad:00:04:de", "6c:ad:ad:00:04:de");
|
||||
mtu = 8000;
|
||||
file_prefix = "fhi_72";
|
||||
fh_config = ({
|
||||
Tadv_cp_dl = 25;
|
||||
T2a_cp_dl = (285, 429); # (min, max)
|
||||
T2a_cp_ul = (285, 429); # (min, max)
|
||||
T2a_up = (71, 428); # (min, max)
|
||||
Ta3 = (20, 32); # (min, max)
|
||||
T1a_cp_dl = (285, 470); # (min, max)
|
||||
T1a_cp_ul = (285, 429); # (min, max)
|
||||
T1a_up = (96, 196); # (min, max) #96
|
||||
Ta4 = (0, 75); # (min, max)
|
||||
ru_config = {
|
||||
iq_width = 9;
|
||||
iq_width_prach = 9;
|
||||
fft_size = 12;
|
||||
};
|
||||
prach_config = {
|
||||
eAxC_offset = 2; # 4 if you want to use 4x4
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -0,0 +1,290 @@
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe01;
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1 }) });
|
||||
|
||||
nr_cellid = 12345678L;
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
pdsch_AntennaPorts = 1;
|
||||
pusch_AntennaPorts = 1;
|
||||
do_CSIRS = 1;
|
||||
|
||||
pdcch_ConfigSIB1 = (
|
||||
{
|
||||
controlResourceSetZero = 12;
|
||||
searchSpaceZero = 0;
|
||||
}
|
||||
);
|
||||
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
|
||||
physCellId = 0;
|
||||
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3300.60 MHz + 81*12*30e-3 MHz = 3329.76
|
||||
absoluteFrequencySSB = 641280;
|
||||
# this is 3300.60 MHz
|
||||
dl_absoluteFrequencyPointA = 640008;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
|
||||
initialDLBWPlocationAndBandwidth = 28875;
|
||||
#
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 28875;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 98;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 12;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ra_ReponseWindow
|
||||
#1,2,4,8,10,20,40,80
|
||||
ra_ResponseWindow = 4;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
|
||||
#oneHalf (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
|
||||
msg3_DeltaPreamble = 1;
|
||||
p0_NominalWithGrant =-90;
|
||||
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -70;
|
||||
# ssb_PositionsInBurs_BitmapPR
|
||||
# 1=short, 2=medium, 3=long
|
||||
ssb_PositionsInBurst_PR = 2;
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
|
||||
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
|
||||
ssPBCH_BlockPower = -25;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ( { ipv4 = "10.188.63.150";
|
||||
ipv6 = "192:168:30::17";
|
||||
active = "yes";
|
||||
preference = "ipv4";
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_INTERFACE_NAME_FOR_NG_AMF = "eno1";
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.188.63.150/24";
|
||||
GNB_INTERFACE_NAME_FOR_NGU = "eno1";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "10.188.63.150/24";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 300;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
pusch_FailureThres = 1000;
|
||||
ulsch_max_frame_inactivity = 0;
|
||||
ul_max_mcs = 28;
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 120;
|
||||
pucch0_dtx_threshold = 80;
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1;
|
||||
nb_rx = 1;
|
||||
att_tx = 0;
|
||||
att_rx = 0;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 85;
|
||||
eNB_instances = [0];
|
||||
##beamforming 1x2 matrix: 1 layer x 2 antennas
|
||||
bf_weights = [0x00007fff, 0x0000];
|
||||
# Comment these two lines below for colosseum
|
||||
clock_src = "internal";
|
||||
time_source = "internal";
|
||||
# sdr_addrs = "addr=192.168.40.2"; # colosseum USRP IP
|
||||
sdr_addrs = "addr=192.168.40.34"; # local setup
|
||||
}
|
||||
);
|
||||
|
||||
e2_agent = {
|
||||
near_ric_ip_addr = "127.0.0.1";
|
||||
sm_dir = "/usr/local/lib/flexric/"
|
||||
}
|
||||
|
||||
E3Configuration : {
|
||||
link = "zmq";
|
||||
transport = "ipc";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
parallel_config = "PARALLEL_SINGLE_THREAD";
|
||||
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
|
||||
worker_config = "WORKER_ENABLE";
|
||||
}
|
||||
);
|
||||
|
||||
rfsimulator :
|
||||
{
|
||||
serveraddr = "server";
|
||||
serverport = "4043";
|
||||
options = (); #("saviq"); or/and "chanmod"
|
||||
modelname = "AWGN";
|
||||
IQfile = "/tmp/rfsimulator.iqs";
|
||||
};
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
e3ap_log_level ="info";
|
||||
}
|
||||
@@ -0,0 +1,281 @@
|
||||
Active_gNBs = ( "gNB-OAI");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe01;
|
||||
gNB_name = "gNB-OAI";
|
||||
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({ mcc = 001; mnc = 01; mnc_length = 2; snssaiList = ({ sst = 1 }) });
|
||||
|
||||
nr_cellid = 12345678L;
|
||||
|
||||
////////// Physical parameters:
|
||||
|
||||
pdsch_AntennaPorts = 1;
|
||||
pusch_AntennaPorts = 1;
|
||||
do_CSIRS = 1;
|
||||
|
||||
pdcch_ConfigSIB1 = (
|
||||
{
|
||||
controlResourceSetZero = 12;
|
||||
searchSpaceZero = 0;
|
||||
}
|
||||
);
|
||||
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
|
||||
physCellId = 0;
|
||||
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3300.60 MHz + 81*12*30e-3 MHz = 3329.76
|
||||
absoluteFrequencySSB = 641280;
|
||||
# this is 3300.60 MHz
|
||||
dl_absoluteFrequencyPointA = 640008;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 106;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
|
||||
initialDLBWPlocationAndBandwidth = 28875;
|
||||
#
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 106;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 28875;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 98;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 12;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ra_ReponseWindow
|
||||
#1,2,4,8,10,20,40,80
|
||||
ra_ResponseWindow = 4;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
|
||||
#oneHalf (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
|
||||
msg3_DeltaPreamble = 1;
|
||||
p0_NominalWithGrant =-90;
|
||||
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -70;
|
||||
# ssb_PositionsInBurs_BitmapPR
|
||||
# 1=short, 2=medium, 3=long
|
||||
ssb_PositionsInBurst_PR = 2;
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
|
||||
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
|
||||
ssPBCH_BlockPower = -25;
|
||||
}
|
||||
|
||||
);
|
||||
|
||||
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ( { ipv4 = "10.188.63.150";
|
||||
ipv6 = "192:168:30::17";
|
||||
active = "yes";
|
||||
preference = "ipv4";
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_INTERFACE_NAME_FOR_NG_AMF = "eno1";
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.188.63.150/24";
|
||||
GNB_INTERFACE_NAME_FOR_NGU = "eno1";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "10.188.63.150/24";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
|
||||
}
|
||||
);
|
||||
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 300;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
pusch_FailureThres = 1000;
|
||||
ulsch_max_frame_inactivity = 0;
|
||||
ul_max_mcs = 28;
|
||||
}
|
||||
);
|
||||
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 120;
|
||||
pucch0_dtx_threshold = 80;
|
||||
}
|
||||
);
|
||||
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "yes"
|
||||
nb_tx = 1;
|
||||
nb_rx = 1;
|
||||
att_tx = 0;
|
||||
att_rx = 0;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 60; # max gain is 60 for x410
|
||||
eNB_instances = [0];
|
||||
##beamforming 1x2 matrix: 1 layer x 2 antennas
|
||||
bf_weights = [0x00007fff, 0x0000];
|
||||
sdr_addrs = "mgmt_addr=192.168.40.18,addr=192.168.40.18,clock_source=internal,time_source=internal"
|
||||
}
|
||||
);
|
||||
|
||||
E3Configuration : {
|
||||
link = "zmq";
|
||||
transport = "ipc";
|
||||
};
|
||||
|
||||
THREAD_STRUCT = (
|
||||
{
|
||||
#three config for level of parallelism "PARALLEL_SINGLE_THREAD", "PARALLEL_RU_L1_SPLIT", or "PARALLEL_RU_L1_TRX_SPLIT"
|
||||
parallel_config = "PARALLEL_SINGLE_THREAD";
|
||||
#two option for worker "WORKER_DISABLE" or "WORKER_ENABLE"
|
||||
worker_config = "WORKER_ENABLE";
|
||||
}
|
||||
);
|
||||
|
||||
rfsimulator :
|
||||
{
|
||||
serveraddr = "server";
|
||||
serverport = "4043";
|
||||
options = (); #("saviq"); or/and "chanmod"
|
||||
modelname = "AWGN";
|
||||
IQfile = "/tmp/rfsimulator.iqs";
|
||||
};
|
||||
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
e3ap_log_level ="info";
|
||||
}
|
||||
@@ -0,0 +1,295 @@
|
||||
Active_gNBs = ( "oai-gnb");
|
||||
# Asn1_verbosity, choice in: none, info, annoying
|
||||
Asn1_verbosity = "none";
|
||||
sa = 1;
|
||||
gNBs =
|
||||
(
|
||||
{
|
||||
////////// Identification parameters:
|
||||
gNB_ID = 0xe00;
|
||||
gNB_name = "oai-gnb";
|
||||
// Tracking area code, 0x0000 and 0xfffe are reserved values
|
||||
tracking_area_code = 1;
|
||||
plmn_list = ({
|
||||
mcc = 001;
|
||||
mnc = 01;
|
||||
mnc_length = 2;
|
||||
snssaiList = (
|
||||
{
|
||||
sst = 1, sd=0x0;
|
||||
},
|
||||
{
|
||||
sst = 1, sd=0xffffff;
|
||||
}
|
||||
);
|
||||
});
|
||||
nr_cellid = 1;
|
||||
|
||||
////////// Physical parameters:
|
||||
pdsch_AntennaPorts_XP = 2;
|
||||
pusch_AntennaPorts = 2;
|
||||
pdsch_AntennaPorts_N1 = 1;
|
||||
maxMIMO_layers = 2;
|
||||
do_CSIRS = 1;
|
||||
do_SRS = 0;
|
||||
do_SINR = 1;
|
||||
# sib1_tda = 15;
|
||||
|
||||
pdcch_ConfigSIB1 = (
|
||||
{
|
||||
controlResourceSetZero = 10;
|
||||
searchSpaceZero = 0;
|
||||
}
|
||||
);
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
#spCellConfigCommon
|
||||
physCellId = 0;
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# this is 3300.24 + 134*12*30e3 = 3348.48 MHz (5G NR GSCN: 7741)
|
||||
absoluteFrequencySSB = 649920;
|
||||
dl_frequencyBand = 78;
|
||||
# this is 3300.24 MHz
|
||||
dl_absoluteFrequencyPointA = 646724;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 1;
|
||||
dl_carrierBandwidth = 273;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
# this is RBstart=0,L=162 (275*(275-L+1))+(274-RBstart))
|
||||
initialDLBWPlocationAndBandwidth = 1099;
|
||||
#
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 1;
|
||||
#pdcch-ConfigCommon
|
||||
initialDLBWPcontrolResourceSetZero = 12;
|
||||
initialDLBWPsearchSpaceZero = 0;
|
||||
#uplinkConfigCommon
|
||||
#frequencyInfoUL
|
||||
ul_frequencyBand = 78;
|
||||
#scs-SpecificCarrierList
|
||||
ul_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 1;
|
||||
ul_carrierBandwidth = 273;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 1099;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 1;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 151;# testMAC 158; OAI gNB 98
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
prach_msg1_FrequencyStart = 0;
|
||||
zeroCorrelationZoneConfig = 12;
|
||||
preambleReceivedTargetPower = -96;
|
||||
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
|
||||
preambleTransMax = 6;
|
||||
#powerRampingStep
|
||||
# 0=dB0,1=dB2,2=dB4,3=dB6
|
||||
powerRampingStep = 1;
|
||||
#ra_ReponseWindow
|
||||
#1,2,4,8,10,20,40,80
|
||||
ra_ResponseWindow = 5;
|
||||
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
|
||||
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 3;
|
||||
#oneHalf (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
#(0..7) 8,16,24,32,40,48,56,64
|
||||
ra_ContentionResolutionTimer = 7;
|
||||
rsrp_ThresholdSSB = 19;
|
||||
#prach-RootSequenceIndex_PR
|
||||
#1 = 839, 2 = 139
|
||||
prach_RootSequenceIndex_PR = 2;
|
||||
prach_RootSequenceIndex = 1;
|
||||
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
|
||||
#
|
||||
msg1_SubcarrierSpacing = 1,
|
||||
# restrictedSetConfig
|
||||
# 0=unrestricted, 1=restricted type A, 2=restricted type B
|
||||
restrictedSetConfig = 0,
|
||||
msg3_DeltaPreamble = 2;
|
||||
p0_NominalWithGrant = -90;
|
||||
# pucch-ConfigCommon setup :
|
||||
# pucchGroupHopping
|
||||
# 0 = neither, 1= group hopping, 2=sequence hopping
|
||||
pucchGroupHopping = 0;
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
# ssb_PositionsInBurs_BitmapPR
|
||||
# 1=short, 2=medium, 3=long
|
||||
ssb_PositionsInBurst_PR = 2;
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 2;
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
dmrs_TypeA_Position = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
subcarrierSpacing = 1;
|
||||
#tdd-UL-DL-ConfigurationCommon
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
referenceSubcarrierSpacing = 1;
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 6; #6;
|
||||
nrofDownlinkSlots = 7; #7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2; #2;
|
||||
nrofUplinkSymbols = 4;
|
||||
ssPBCH_BlockPower = 0;
|
||||
}
|
||||
);
|
||||
# ------- SCTP definitions
|
||||
SCTP :
|
||||
{
|
||||
# Number of streams to use in input/output
|
||||
SCTP_INSTREAMS = 2;
|
||||
SCTP_OUTSTREAMS = 2;
|
||||
};
|
||||
|
||||
////////// AMF parameters:
|
||||
amf_ip_address = ({ ipv4 = "172.30.134.111"; });
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "10.130.0.20";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "10.130.0.20";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
}
|
||||
);
|
||||
MACRLCs = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 120;
|
||||
pucch_TargetSNRx10 = 260;
|
||||
ul_bler_target_upper=.15;
|
||||
ul_bler_target_lower=.05;
|
||||
pusch_FailureThres = 100;
|
||||
ul_max_mcs = 28;
|
||||
ulsch_max_frame_inactivity = 1;
|
||||
#pusch_RSSI_Threshold = -500;
|
||||
#pucch_RSSI_Threshold = -600;
|
||||
}
|
||||
);
|
||||
L1s = (
|
||||
{
|
||||
num_cc = 1;
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 180;
|
||||
pucch0_dtx_threshold = 80;
|
||||
pusch_dtx_threshold = 10;
|
||||
max_ldpc_iterations = 15;
|
||||
tx_amp_backoff_dB = 24; # needs to match O-RU configuration
|
||||
L1_rx_thread_core = 47;
|
||||
L1_tx_thread_core = 49; # relevant after merge of l1_tx_thread
|
||||
phase_compensation = 0; # needs to match O-RU configuration
|
||||
}
|
||||
);
|
||||
RUs = (
|
||||
{
|
||||
local_rf = "no";
|
||||
nb_tx = 2;
|
||||
nb_rx = 2;
|
||||
att_tx = 0
|
||||
att_rx = 0;
|
||||
bands = [78];
|
||||
max_pdschReferenceSignalPower = -27;
|
||||
max_rxgain = 75;
|
||||
sf_extension = 0;
|
||||
eNB_instances = [0];
|
||||
ru_thread_core = 51;
|
||||
sl_ahead = 5;
|
||||
##beamforming 1x2 matrix: 1 layer x 2 antennas
|
||||
bf_weights = [0x00007fff, 0x0000,0x00007fff, 0x0000];
|
||||
tr_preference = "raw_if4p5"; # important: activate FHI7.2
|
||||
do_precoding = 0; # needs to match O-RU configuration
|
||||
}
|
||||
);
|
||||
security = {
|
||||
# preferred ciphering algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nea0, nea1, nea2, nea3
|
||||
ciphering_algorithms = ( "nea0" );
|
||||
|
||||
# preferred integrity algorithms
|
||||
# the first one of the list that an UE supports in chosen
|
||||
# valid values: nia0, nia1, nia2, nia3
|
||||
integrity_algorithms = ( "nia2", "nia0" );
|
||||
|
||||
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
|
||||
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
|
||||
drb_ciphering = "yes";
|
||||
drb_integrity = "no";
|
||||
};
|
||||
|
||||
log_config :
|
||||
{
|
||||
global_log_level ="info";
|
||||
hw_log_level ="info";
|
||||
phy_log_level ="info";
|
||||
mac_log_level ="info";
|
||||
rlc_log_level ="info";
|
||||
pdcp_log_level ="info";
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="info";
|
||||
f1ap_log_level ="info";
|
||||
e3ap_log_level ="info";
|
||||
};
|
||||
|
||||
E3Configuration : {
|
||||
link = "zmq";
|
||||
transport = "tcp";
|
||||
};
|
||||
|
||||
|
||||
fhi_72 = {
|
||||
dpdk_devices = ("0000:a0:00.7", "0000:a0:01.7");
|
||||
system_core = 53;
|
||||
io_core = 55;
|
||||
worker_cores = (57);
|
||||
du_addr = ("56:89:79:bc:cd:3c", "56:89:79:bc:cd:3c");
|
||||
# ru_addr = ("b8:ce:f6:5e:6b:4a", "b8:ce:f6:5e:6b:4a");
|
||||
ru_addr = ("6c:ad:ad:00:04:de", "6c:ad:ad:00:04:de");
|
||||
mtu = 8000;
|
||||
file_prefix = "fhi_72";
|
||||
fh_config = ({
|
||||
Tadv_cp_dl = 25;
|
||||
T2a_cp_dl = (285, 429); # (min, max)
|
||||
T2a_cp_ul = (285, 429); # (min, max)
|
||||
T2a_up = (71, 428); # (min, max)
|
||||
Ta3 = (20, 32); # (min, max)
|
||||
T1a_cp_dl = (285, 470); # (min, max)
|
||||
T1a_cp_ul = (285, 429); # (min, max)
|
||||
T1a_up = (96, 196); # (min, max) #96
|
||||
Ta4 = (0, 75); # (min, max)
|
||||
ru_config = {
|
||||
iq_width = 9;
|
||||
iq_width_prach = 9;
|
||||
fft_size = 12;
|
||||
};
|
||||
prach_config = {
|
||||
eAxC_offset = 2; # 4 if you want to use 4x4
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -241,6 +241,7 @@ log_config :
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
e3ap_log_level ="debug";
|
||||
};
|
||||
|
||||
e2_agent = {
|
||||
|
||||
Reference in New Issue
Block a user