Compare commits

...

59 Commits

Author SHA1 Message Date
Filippo Olimpieri
7d3ef0140b Modified nr_radio_config.c to always include SLIV(0,12) UL TDA when E3_AGENT is enabled 2026-05-01 20:13:18 +00:00
Filippo Olimpieri
ecc6c9ec3e modified CMakeLists of E3 to compile asn files the same way as in the other parts of the repository 2026-04-30 18:22:56 -04:00
Filippo Olimpieri
ce211dc2ef removed duplicated definition and modified skipping of TDS symbol to match the new code structure 2026-04-30 17:19:13 -04:00
Filippo Olimpieri
c3971d524f E2SM DAPP revision
* updated ran functiond for dapp to remove action def and renamed formats

* updated e3_agent code and ran_func_dapp to expose dapp ids and e3 ran func to which each dapp is subscribed

* implemented triggering of RIC service update at every status change of dApp

* modified ran function dapp to handle action definition to specify the kind of report to send in return

* removed report style 1 that was used to geth both types of indications

* updated ran func dapp to populate the new ran func struct following the new structure definition

* fixed missing branchets in if, restored some comments and exception messages, and added macros for report styles

* clang-formatted files in ran func def
2026-04-14 13:31:44 -04:00
Andrea Lacava
5f3ea814e5 Implementation of LIBE3 inside OAI
* implemented spectrum SM

* fix t tracer connection

* Enhance E3 Agent integration and logging in Spectrum Service Model

* Refactor E3 service model: update data types and improve memory handling

* Update documentation
2026-03-05 15:02:21 -05:00
Andrea Lacava
c2fdea2f01 Add documentation
* Add documentation for Spectrum Service Model architecture and functionalities

* Add xApp-dApp bridge

* updated documentation

* Update documentation for xApp-dApp interaction and files

---------

Co-authored-by: Andrea Lacava <a.lacava@northeastern.edu>

---------

Co-authored-by: Filippo Olimpieri <126257005+Ninjabippo1205@users.noreply.github.com>
2026-02-19 12:44:18 -05:00
Filippo Olimpieri
df5ddd2ea4 solve ASN E3AP PDU duplication 2026-01-26 16:11:01 -05:00
Filippo Olimpieri
e5c922feba E2SM-DAPP for xApp-dApp synchronization
* updated flexric submodule

* added support in E3AP for report messages and xAppControlMessages

* improved consistecy

* added conditional compilation guard to ensure generate_e2_indication_from_e3_dapp_report is called only when E2_AGENT is defined

* updated ran func dapp to handle the new version of the e3 agent, and modified also e3 agent to support xAppControlAction

* addressed comments of PR by removing code in CMakeLists and renamed shared queue, marked as untested the code not tested

* removed unused code and  implemented read_dapp_setup_sm

* updated flexric

* removed unused code

* removed unused parameters and updated code to work with new implementation of flexric dapp sm

* updated dapp_e3_obj with e3sm_obj

* updated flexric

* build ran functions of e2sm_dapp ony if E3_AGENT is defined

* fixed decoding of dapp control to enable read of all 273 prbs, currently it supports max 137

* update asn and encoding/decoding to consider prbblacklist and whitelist as sequence of integers instead of octet string

* fix issue that cap the number of xapps to which the indication message could be sent

* added include guard for void* e3_agent_dapp_task(void* args_p);

* modified PRB_Index in e3sm_spectrum.asn

* updated flexric

* resolved confict with develop-dapp branch

* resolved confict with develop-dapp branch

* updated flexric to new branch

* restored initial .gitmodule and flexric submodule

* updated .gitmodules to add missing newline

* added compilation guards to avoid to use of  e2sm-dapp when e3 agent is not defined during copilation

* update DAPP service model description for clarity in synchronization context

---------

Co-authored-by: Andrea Lacava <a.lacava@northeastern.edu>
2026-01-23 16:49:04 -05:00
Andrea Lacava
65df4e2afd enable restart of the main thread for SM 2025-12-17 15:18:44 -05:00
Andrea Lacava
af8d1daf36 Increase indication for the foxconn
* Enhance spectrum encoding with improved error logging and validation checks

* Increase encode buffer size to 32KB for improved IQ data handling
2025-12-09 10:00:07 -05:00
Andrea Lacava
e4ea2170b0 increase buffer size for foxconn 2025-12-05 18:26:29 -05:00
Andrea Lacava
4bb6c48a2f Enhance subscription management by tracking affected RAN functions during dApp unregistration and managing SM lifecycle accordingly 2025-12-05 14:38:59 -05:00
Andrea Lacava
4e6caca5a7 Include dApp report and xApp control action to ensure compatibility and fix ordering 2025-12-04 14:56:38 -05:00
Andrea Lacava
aa01359088 Management of subscribe request to allow clean disconnection
* Refactor subscription management: streamline add/remove logic and enhance error handling

* Enhance dApp unregistration: collect affected RAN function IDs and stop SMs if necessary

* Allow connection from different hosts

* Add management of already existing subscription in E3 Agent

* Change log level for existing subscription warning

* Refactor subscription management: now the RAN functions in the SMs are initialized and run from the beginning. the dApp subscriptions are not going to affect their behaviors, only the E3AP delivery

---------

Co-authored-by: absentio <20925479+absentio@users.noreply.github.com>
2025-12-01 15:48:59 -05:00
Andrea Lacava
d92a51d468 Introducing structured E3SM for different use cases support (#39)
Ex cathedra: squash-merge of E3AP integration and Spectrum SM refactor

This PR consolidates multiple commits that implement, refactor, and stabilize the E3AP integration into the OAI Layer 2 stack with support for Spectrum Service Models and full subscription management.
Thanks to this, now the codebase features a fully working E3AP/E3SM-based control loop in OAI, with subscription and indication message flow, Spectrum SM integration, dynamic sensing thresholds, and compile-time encoding format selection — establishing a solid foundation for scalable multi-RU support.
	•	E3AP Integration & Infrastructure
• Moved E3AP handling into the OAI Layer 2 to support multiple RUs.
• Defined generic data types and introduced compile-time encoding format selection for E3AP messages.
• Refactored socket handling with permission settings for IPC endpoints; added e3_response_queue and new thread support for SMs.
• Reorganized configuration handling, removing deprecated structs, obsolete control logic, and T-tracer functions.
	•	Subscription Management
• Added E3 Subscription Manager (e3_subscription_manager.h/.c) to manage dApp registrations and RAN function subscriptions.
• Implemented add/remove/check subscription functions, integrated with e3_agent.c for handling subscription requests and indications.
• Enhanced agent lifecycle (init/destroy) to incorporate subscription manager cleanup.
• Added subscription request/response handling, with logging improvements for traceability.
	•	Control Loop & Spectrum SM Enhancements
• Implemented setup response with RAN function ID list.
• Subscription and indication messages working end-to-end; completed full control loop.
• Enabled dynamic sampling threshold changes; renamed e3_agent_control to e3_sm_spectrum_control.
• Refined spectrum encoding functions to use uint32_t timestamps and updated ASN.1 definitions accordingly.
• Added ranFunctionIdentifier to control actions for consistency.
	•	Cleanup & Utilities
• Reduced verbosity and removed unused code, buffers, and logs.
• Commented unused timestamp functions to remove warnings.
• Updated CMakeLists and generator scripts.
2025-10-09 15:35:03 -04:00
Noemi Giustini
876696494f Add support for 100MHz over the E3AP and add additional configurations for RUs (#30)
* increase protocolData size to support 100MHz

---------

Co-authored-by: Andrea Lacava <lacava.a@northeastern.edu>
2025-07-02 16:53:25 -04:00
Andrea Lacava
7c6dd3b563 remove all helpers 2025-04-04 18:56:58 +00:00
Andrea Lacava
3ef37bfb37 formatting and cleanup of unnecessary code 2025-04-04 14:05:52 -04:00
Andrea Lacava
a0a0f1dbcd add RIC capabilities and E2AP on the dApp (#18)
* Optimize PHY actions, remove redundant code.
Ensure correct access to the gNB MAC parameters using locking.

* remove magic numbers

* improve memory management of the control action list

* fix direct memory leaks

* change scripts

* add size check

* Successful near-rt, xApp and dApps coexistence

* improve feedback logic of the PRB blocked

* fix index in NR PRB update policy
2025-03-14 17:30:51 -04:00
Andrea Lacava
7bdf1edf8a add include guards and reduce changes only to the necessary ones 2025-02-21 00:03:12 +00:00
Andrea Lacava
3c45d35972 fix data extraction thanks @RajeevGa 2025-02-11 23:12:53 +00:00
Andrea Lacava
4b14d6609b code formatting 2025-02-03 20:39:34 +00:00
Andrea Lacava
b3c9ef630a add back a space 2025-02-03 20:31:28 +00:00
Andrea Lacava
29d2586b8d remove references to GNB_PHY_INPUT_SIGNAL 2025-02-03 20:29:17 +00:00
Andrea Lacava
51ca8ed10d add UTIL back 2025-01-31 23:40:15 +00:00
Andrea Lacava
c48e3e21e4 Update mirror.yml 2025-01-31 16:50:07 -05:00
Andrea Lacava
a9d8f6edb4 Update mirror.yml 2025-01-31 16:43:40 -05:00
Andrea Lacava
ea95f93427 Update mirror.yml to remove conditional push 2025-01-31 16:40:39 -05:00
Andrea Lacava
7ccc29aed3 fix branch 2025-01-27 15:28:26 -05:00
Andrea Lacava
ac90a535f7 Implementation of ZeroMQ, IPC, SCTP, TCP to serve ASN1 PDU for the E3AP (#12)
* work with x310

* E3AP ASN inside

* fix the logs and remove dead code

* add unix domain socket

* add zmq handlers

* Add parameters for E3

* add e3connector to include all the logic related to the message exchange

* add mutex to the contol actions

* list of integers 84% of overhead

* loop apparently works

* indication working

* Action control working

* zmq tcp working

* start work ipc

* simplyfy uds as ipc and remove sctp zmq from valid configurations

* zmq and ipc almost working

* control working better

* LOG_I on the endpoints

* sampling done before the use of the t-tracer

* apply @gabri94 patch to extract modulation order

* remove topic for the inbound connection

* improve the ASN to reduce the PDU size

* improve sampling logic to support no sampling

* code to support data taking commented

* comment on the save of MAC stats

* create stats files

* fix taskset on dapp

* remove OP reference : (

* the best open source projects have references in their code

* add mirror workflow
2025-01-27 15:23:56 -05:00
Andrea Lacava
c5f3bae132 add local core and fix cmakelists for L2_NR 2024-10-19 21:41:03 +00:00
Andrea Lacava
b6d928d7a1 fix cmakelists.txt files for enabling and disabling the e3ap over builds and give the possibility to select whether to use raw tcp sockets or unix domain sockets 2024-10-18 20:35:47 +00:00
Andrea Lacava
21de22e0a3 sampling 5 symbols 2024-10-17 20:02:35 +00:00
Andrea Lacava
ec5f6e7e57 enabling TCP sockets along with UDS 2024-10-17 19:21:01 +00:00
Andrea Lacava
7999a88ea0 revert path of unix domain socket 2024-10-17 18:20:03 +00:00
Andrea Lacava
3ea1ad3a54 test speed colosseum 2024-10-17 17:52:49 +00:00
Andrea Lacava
2213ea1b31 Change of the configurations after the call (#11)
* add sampling counter and sampling_threshold for IQs, reduced RX gain for 410 conf

* removed sampling add configurations

* enable x410
2024-10-17 13:50:01 -04:00
Andrea Lacava
54e0287330 add x410 conf 2024-10-16 20:26:26 +00:00
Andrea Lacava
aa4481ac08 change TCP sockets to UDS (#10)
* change TCP sockets to UDS

* Save IQ samples to file and work OTA (#9)

* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

* change address to .62

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>

* add unix domain sockets

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-10-09 17:46:08 -04:00
Andrea Lacava
74374975ca Save IQ samples to file and work OTA (#9)
* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

* change address to .62

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-10-07 15:20:16 -04:00
Andrea Lacava
69efebb153 Add PRB masking and save data through dApp (#7)
* IQ logging into a file

* Logs aligned with timestamp

* remove python code

* change relative path

* OTA working (no control)

---------

Co-authored-by: RajeevGa <gangula@eurecom.fr>
Co-authored-by: RajeevGa <r.gangula@northeastern.edu>
2024-09-27 16:57:15 -04:00
Andrea Lacava
0f1bd004d3 change configuration for OTA 2024-09-27 18:34:05 +00:00
Andrea Lacava
efdf65d763 indentation 2024-09-27 16:00:47 +00:00
Andrea Lacava
707b031007 fix relative paths 2024-06-13 12:49:10 +00:00
Andrea Lacava
8b59a83ab6 move python code in the dApp repo 2024-06-13 12:21:28 +00:00
RajeevGa
b33261e10c Adding readme file 2024-06-09 15:47:41 -04:00
RajeevGa
2048ba532f adding gNB config file 2024-05-28 15:28:11 -04:00
RajeevGa
ac5212053f qtr working demo 2024-05-28 15:26:26 -04:00
Andrea Lacava
82a60c6d7b t tracer working internally 2024-04-25 20:14:53 +00:00
Andrea Lacava
1043bf4a6c moved initialization of E3 agent before the T tracer 2024-04-22 15:13:47 -04:00
Andrea Lacava
ad3158887c Add --nrUE option to build script 2024-03-25 19:39:40 -04:00
Andrea Lacava
70a7d63888 add stub e3 agent 2024-03-22 15:38:25 -04:00
Andrea Lacava
a455ae1436 Update build_oai.sh and e3_agent.c***
***Add run_oai.sh and gnb.band78.sa.fr1.106PRB.usrpx300.conf***

***Fix T_MESSAGES_PATH in e3_agent.h
2024-03-18 14:31:48 -04:00
Andrea Lacava
f71a0fd372 fix typo 2024-03-14 18:34:18 -04:00
RajeevGa
8aeb08d5e2 Added tracer for getting time domain UL iq samples 2024-03-14 15:38:17 -04:00
Andrea Lacava
3ae1b6fc7c Update E3AP CMakeLists.txt and e3_agent.c 2024-03-07 23:36:48 -05:00
Andrea Lacava
7d95e58ad8 Add E3AP capabilities and enable E3_AGENT in build 2024-03-07 22:33:10 -05:00
Andrea Lacava
79645dd554 fix error on including directories 2024-03-07 16:20:05 -05:00
Andrea Lacava
02a95300cd reproduced error on the develop version 2024-03-05 16:32:39 -05:00
45 changed files with 4701 additions and 4 deletions

2
.gitignore vendored
View File

@@ -34,3 +34,5 @@ nfapi_nr_interface_scf
*.log
*.out
CMakeUserPresets.json
build/*

View File

@@ -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()

View File

@@ -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"

View File

@@ -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 |
|:---------|:---------------|:---------------|----------------:|

View File

@@ -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,

View File

@@ -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

View File

@@ -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 */
/****************************************************************************/

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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)

View File

@@ -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

View File

@@ -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

View 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;
}

View 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

View 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

View 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);
}

View 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

View File

@@ -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)

View 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()

View 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();
}
}

View 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
View 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
View 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

View 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)

View 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)

View File

@@ -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

View File

@@ -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
)

View File

@@ -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)

View File

@@ -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.

View 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);
}

View 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

View 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

View 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

View 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, &registered_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;
}

View 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

View File

@@ -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);

View File

@@ -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,

View File

@@ -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) {

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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
};
});
};

View File

@@ -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";
}

View File

@@ -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";
}

View File

@@ -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
};
});
};

View File

@@ -241,6 +241,7 @@ log_config :
rrc_log_level ="info";
ngap_log_level ="debug";
f1ap_log_level ="debug";
e3ap_log_level ="debug";
};
e2_agent = {