mirror of
https://gitlab.eurecom.fr/oai/openairinterface5g.git
synced 2026-07-18 07:00:30 +00:00
Compare commits
29 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
1617c32e3c | ||
|
|
ae780bae57 | ||
|
|
2834088ea0 | ||
|
|
0b320c8dce | ||
|
|
9fc066394e | ||
|
|
04ce714f37 | ||
|
|
d420f8c123 | ||
|
|
e92dfe2f93 | ||
|
|
35965a1418 | ||
|
|
103c1d48e3 | ||
|
|
0755086cc2 | ||
|
|
c8ff0819f8 | ||
|
|
ee4018b5d0 | ||
|
|
49e6575910 | ||
|
|
79ebf23a80 | ||
|
|
3cbf0927be | ||
|
|
6c534c3b76 | ||
|
|
b5e0cf6a2b | ||
|
|
4d67a6e513 | ||
|
|
ced388a0f4 | ||
|
|
134d750a88 | ||
|
|
13963ba70d | ||
|
|
28c1634fdd | ||
|
|
ec171483e9 | ||
|
|
d75b8963ab | ||
|
|
3329877914 | ||
|
|
0538ea4937 | ||
|
|
b5aa2d87fb | ||
|
|
58f00921c8 |
2
.gitignore
vendored
2
.gitignore
vendored
@@ -34,5 +34,3 @@ nfapi_nr_interface_scf
|
||||
*.log
|
||||
*.out
|
||||
CMakeUserPresets.json
|
||||
|
||||
build/*
|
||||
|
||||
@@ -377,12 +377,6 @@ 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 #
|
||||
##################################################
|
||||
@@ -735,9 +729,6 @@ 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
|
||||
@@ -1348,7 +1339,6 @@ 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)
|
||||
|
||||
@@ -1379,10 +1369,6 @@ 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
|
||||
@@ -1903,11 +1889,6 @@ 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
|
||||
@@ -1930,10 +1911,6 @@ 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()
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# Contributing to OpenAirInterface #
|
||||
# Contributing to OpenAirInterface
|
||||
|
||||
We want to make contributing to this project as easy and transparent as possible.
|
||||
|
||||
@@ -11,10 +11,10 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
|
||||
if you do not have any.
|
||||
- We recommend that you register with a professional or student email address.
|
||||
- If your email domain (`@domain.com`) is not whitelisted, please contact us
|
||||
(mailto:contact@openairinterface.org).
|
||||
(mailto:oaicicdteam@openairinterface.org).
|
||||
- Eurecom GitLab does NOT accept public email domains.
|
||||
3. Provide the OAI team with the **username** of this account to
|
||||
(mailto:contact@openairinterface.org) ; we will give you the developer
|
||||
(mailto:oaicicdteam@openairinterface.org) ; we will give you the developer
|
||||
rights on this repository.
|
||||
4. The contributing policies are described in the [corresponding documentation
|
||||
page](doc/code-style-contrib.md).
|
||||
@@ -24,8 +24,43 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
|
||||
request from a forked repository.
|
||||
* This decision was made for the license reasons.
|
||||
* The Continuous Integration will reject your merge request.
|
||||
5. Mandatory signing of all the commits using the email address used for CLA.
|
||||
|
||||
# License #
|
||||
## Commit Guidelines
|
||||
|
||||
### Signing Commits
|
||||
|
||||
To sign commits:
|
||||
|
||||
You can also get the verified label
|
||||
on your commits via using [SSH KEYS or GPG KEYS](https://docs.gitlab.com/user/project/repository/signed_commits/)
|
||||
|
||||
```
|
||||
# Edit .git/config in the git repository you are working on
|
||||
# Add the user section
|
||||
[user]
|
||||
name = YOUR NAME
|
||||
email = YOUR EMAIL ADDRESS
|
||||
|
||||
# If you use a signing key, use the below configuration instead
|
||||
[user]
|
||||
name = YOUR NAME
|
||||
email = YOUR EMAIL ADDRESS
|
||||
signingkey = LOCATION OF SSH KEYS or GPG KEY
|
||||
|
||||
[gpg]
|
||||
format = ssh
|
||||
|
||||
[commit]
|
||||
gpgsign = true
|
||||
```
|
||||
|
||||
> **NOTE:** If your commits are not signed the CI framework will not accept the MR.
|
||||
|
||||
For more information regarding contribution guidelines
|
||||
please check [this document](doc/code-style-contrib.md)
|
||||
|
||||
## License
|
||||
|
||||
By contributing to OpenAirInterface, you agree that your contributions will be
|
||||
licensed under
|
||||
|
||||
@@ -89,6 +89,19 @@ pipeline {
|
||||
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
|
||||
// GitLab-Jenkins plugin integration is lacking to perform the merge by itself
|
||||
// Doing it manually --> it may have merge conflicts
|
||||
|
||||
// Validate MR commits: checks for missing Signed-off-by and merge commits
|
||||
def mrValidationLog = "signedCommit_${env.BUILD_NUMBER}.log"
|
||||
def mrValidationExitCode = sh(
|
||||
script: "bash ./ci-scripts/pre-ci-check.sh -s origin/${env.gitlabSourceBranch} -t origin/${env.gitlabTargetBranch} > ${mrValidationLog} 2>&1",
|
||||
returnStatus: true
|
||||
)
|
||||
def mrValidationMessage = readFile(mrValidationLog).trim()
|
||||
sh "rm -f ${mrValidationLog}"
|
||||
addGitLabMRComment comment: "${mrValidationMessage}"
|
||||
if (mrValidationExitCode >= 2) {
|
||||
error("${mrValidationMessage}")
|
||||
}
|
||||
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
|
||||
} else {
|
||||
echo "Git Branch is ${GIT_BRANCH}"
|
||||
|
||||
@@ -70,7 +70,7 @@ gNBs =
|
||||
initialULBWPsubcarrierSpacing = 3;
|
||||
#rach-ConfigCommon
|
||||
#rach-ConfigGeneric
|
||||
prach_ConfigurationIndex = 52;
|
||||
prach_ConfigurationIndex = 70;
|
||||
#prach_msg1_FDM
|
||||
#0 = one, 1=two, 2=four, 3=eight
|
||||
prach_msg1_FDM = 0;
|
||||
@@ -112,7 +112,7 @@ gNBs =
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
ssb_PositionsInBurst_Bitmap = 85;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
@@ -179,7 +179,7 @@ MACRLCs = ({
|
||||
set_analog_beamforming = "lophy";
|
||||
beam_duration = 1;
|
||||
beams_per_period = 1;
|
||||
beam_weights = [0]; // single SSB -> one analog beam
|
||||
beam_weights = [0, 1, 2, 3]; // 4 SSBs -> 4 analog beams
|
||||
});
|
||||
|
||||
L1s = (
|
||||
|
||||
94
ci-scripts/pre-ci-check.sh
Executable file
94
ci-scripts/pre-ci-check.sh
Executable file
@@ -0,0 +1,94 @@
|
||||
#!/bin/bash
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
function usage {
|
||||
echo "OAI GitLab MR validation script (Signed-off-by and merge commits)"
|
||||
echo ""
|
||||
echo "Usage:"
|
||||
echo "------"
|
||||
echo " $0 -s <source-branch> -t <target-branch>"
|
||||
echo ""
|
||||
echo "Options:"
|
||||
echo "--------"
|
||||
echo " -s"
|
||||
echo " The source branch of the merge request. Default value is current Git Branch (HEAD)"
|
||||
echo ""
|
||||
echo " -t"
|
||||
echo " The target branch of the merge request. Default value is develop"
|
||||
echo ""
|
||||
echo " -h"
|
||||
echo " Print this help message."
|
||||
echo ""
|
||||
}
|
||||
|
||||
# Parse arguments properly
|
||||
SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
|
||||
TARGET_BRANCH="origin/develop"
|
||||
|
||||
git fetch --quiet
|
||||
|
||||
while getopts ":s:t:h" opt; do
|
||||
case "$opt" in
|
||||
s)
|
||||
SOURCE_BRANCH="$OPTARG"
|
||||
;;
|
||||
t)
|
||||
TARGET_BRANCH="$OPTARG"
|
||||
;;
|
||||
h)
|
||||
usage
|
||||
exit 0
|
||||
;;
|
||||
:)
|
||||
echo "Error: Option -$OPTARG requires a value."
|
||||
echo ""
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
\?)
|
||||
echo "Error: Invalid option -$OPTARG"
|
||||
echo ""
|
||||
usage
|
||||
exit 2
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
# ----------------------------
|
||||
# Merged commits
|
||||
# ----------------------------
|
||||
mergeCommits=$(git rev-list --merges --abbrev-commit "$TARGET_BRANCH".."$SOURCE_BRANCH")
|
||||
if [[ -n "$mergeCommits" ]]; then
|
||||
message="> ERROR: Following merge commits are found in the source branch history. Please rebase your branch.\n>\n"
|
||||
message+="> $(echo "$mergeCommits" | paste -sd ',' -)\n"
|
||||
echo -e "$message"
|
||||
exit 3
|
||||
fi
|
||||
|
||||
# ----------------------------
|
||||
# Check unsigned commits
|
||||
# ----------------------------
|
||||
unsignedCommits=$(
|
||||
for c in $(git rev-list "$TARGET_BRANCH".."$SOURCE_BRANCH"); do
|
||||
if ! git log -1 --format=%B "$c" | grep -q "Signed-off-by:"; then
|
||||
git log -1 --format='%h' "$c"
|
||||
fi
|
||||
done | paste -sd ","
|
||||
)
|
||||
|
||||
# ----------------------------
|
||||
# Report unsigned commits
|
||||
# ----------------------------
|
||||
message=""
|
||||
|
||||
if [ -n "$unsignedCommits" ]; then
|
||||
message="> WARNING: The following commit(s) are missing a Signed-off-by:\n>\n> $unsignedCommits\n>\n"
|
||||
message+="> Please use 'git commit -s' to sign your commits.\n>\n"
|
||||
message+="> For detailed instructions, refer to the CONTRIBUTING file at the root of this repository."
|
||||
echo -e "$message"
|
||||
exit 1
|
||||
else
|
||||
message="> All commits are signed off using 'git commit -s'."
|
||||
echo -e "$message"
|
||||
exit 0
|
||||
fi
|
||||
@@ -99,6 +99,7 @@ services:
|
||||
- ALL
|
||||
environment:
|
||||
USE_ADDITIONAL_OPTIONS: --rfsim --log_config.global_log_options level,nocolor,time
|
||||
--rfsimulator.[0].enable_beams --rfsimulator.[0].beam_gains 0,-20,-30,-40
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
networks:
|
||||
public_net:
|
||||
@@ -125,6 +126,7 @@ services:
|
||||
USE_ADDITIONAL_OPTIONS: --rfsim
|
||||
--rfsimulator.[0].serveraddr 192.168.71.140 --log_config.global_log_options level,nocolor,time
|
||||
-C 27975360000 -r 66 --numerology 3 --ssb 48 --band 257
|
||||
--rfsimulator.[0].enable_beams --rfsimulator.[0].beam_gains 0,-20,-30,-40 --rfsimulator.[0].beam_ids 2
|
||||
ASAN_OPTIONS: detect_leaks=0
|
||||
devices:
|
||||
- /dev/net/tun:/dev/net/tun
|
||||
|
||||
@@ -18,7 +18,7 @@ HWs=""
|
||||
BUILD_DIR=ran_build
|
||||
CMAKE_BUILD_TYPE="RelWithDebInfo"
|
||||
CMAKE_CMD="$CMAKE"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
|
||||
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal websrv oai_iqplayer imscope imscope_record"
|
||||
TARGET_LIST=""
|
||||
BUILD_TOOL_OPT="-j$(nproc)"
|
||||
|
||||
@@ -43,8 +43,6 @@ 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
|
||||
@@ -145,10 +143,6 @@ function main() {
|
||||
CMAKE_CMD="$CMAKE_CMD -DE2_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
--build-e3 )
|
||||
CMAKE_CMD="$CMAKE_CMD -DE3_AGENT=ON"
|
||||
shift
|
||||
;;
|
||||
-I | --install-external-packages)
|
||||
INSTALL_EXTERNAL=1
|
||||
echo_info "Will install external packages"
|
||||
|
||||
@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
|
||||
LOG_T(<component>,<format>,<argument>,...)
|
||||
)
|
||||
```
|
||||
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the 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.
|
||||
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.
|
||||
|
||||
| macro | level letter | level value | level name |
|
||||
|:---------|:---------------|:---------------|----------------:|
|
||||
|
||||
@@ -155,7 +155,6 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""};
|
||||
COMP_DEF(NFAPI_PNF, log) \
|
||||
COMP_DEF(ITTI, log) \
|
||||
COMP_DEF(UTIL, log) \
|
||||
COMP_DEF(E3AP, log) \
|
||||
COMP_DEF(MAX_LOG_PREDEF_COMPONENTS, )
|
||||
|
||||
#define COMP_ENUM(comp, file_extension) comp,
|
||||
|
||||
@@ -159,10 +159,6 @@ 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
|
||||
@@ -430,27 +426,6 @@ ID = LEGACY_MAC_TRACE
|
||||
GROUP = ALL:LEGACY_MAC:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_E3AP_INFO
|
||||
DESC = E3AP legacy logs - info level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_INFO:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_ERROR
|
||||
DESC = E3AP legacy logs - error level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_ERROR:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_WARNING
|
||||
DESC = E3AP legacy logs - warning level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_WARNING:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_DEBUG
|
||||
DESC = E3AP legacy logs - debug level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_DEBUG:LEGACY
|
||||
FORMAT = string,log
|
||||
ID = LEGACY_E3AP_TRACE
|
||||
DESC = E3AP legacy logs - trace level
|
||||
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_TRACE:LEGACY
|
||||
FORMAT = string,log
|
||||
|
||||
ID = LEGACY_NR_MAC_INFO
|
||||
DESC = NR_MAC legacy logs - info level
|
||||
GROUP = ALL:LEGACY_NR_MAC:LEGACY_GROUP_INFO:LEGACY
|
||||
|
||||
@@ -12,8 +12,6 @@
|
||||
#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;
|
||||
@@ -235,117 +233,6 @@ 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 */
|
||||
/****************************************************************************/
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
|
||||
#include "spsc_q.h"
|
||||
|
||||
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
|
||||
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz)
|
||||
{
|
||||
/* internally, use one element more: the ringbuffer is full if
|
||||
* (write_idx + 1) % cnt == read_idx, so it's full if one element is still
|
||||
@@ -17,7 +17,8 @@ spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
|
||||
cnt += 1;
|
||||
spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz};
|
||||
rb.buf = calloc(cnt, elsiz);
|
||||
return rb;
|
||||
*rbn = rb;
|
||||
return rb.buf != NULL;
|
||||
}
|
||||
|
||||
void spsc_q_free(spsc_q_t *rb)
|
||||
|
||||
@@ -25,7 +25,7 @@ typedef struct spsc_q {
|
||||
atomic_size_t read_idx;
|
||||
} spsc_q_t;
|
||||
|
||||
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz);
|
||||
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz);
|
||||
void spsc_q_free(spsc_q_t *rb);
|
||||
|
||||
bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz);
|
||||
|
||||
@@ -8,7 +8,8 @@ extern "C" {
|
||||
}
|
||||
|
||||
TEST(spsc_q, basic_test) {
|
||||
spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
|
||||
spsc_q_t rb;
|
||||
EXPECT_TRUE(spsc_q_alloc(&rb, 2, sizeof(int)));
|
||||
|
||||
int a = 1;
|
||||
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
|
||||
@@ -26,7 +27,8 @@ TEST(spsc_q, basic_test) {
|
||||
}
|
||||
|
||||
TEST(spsc_q, cont_write) {
|
||||
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
|
||||
spsc_q_t rb;
|
||||
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
|
||||
|
||||
int a = 1;
|
||||
for (int i = 0; i < 1111; ++i) {
|
||||
@@ -57,7 +59,8 @@ bool count(const void *data, void *user)
|
||||
return true; /* count all */
|
||||
}
|
||||
TEST(spsc_q, iterator) {
|
||||
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
|
||||
spsc_q_t rb;
|
||||
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
|
||||
int n = 8;
|
||||
for (int i = 0; i < n; ++i)
|
||||
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
|
||||
@@ -85,7 +88,8 @@ bool drop(const void *data, void *user)
|
||||
return true; /* drop all */
|
||||
}
|
||||
TEST(spsc_q, drop) {
|
||||
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
|
||||
spsc_q_t rb;
|
||||
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
|
||||
int n = 8;
|
||||
for (int i = 0; i < n; ++i)
|
||||
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
|
||||
@@ -104,7 +108,8 @@ TEST(spsc_q, drop) {
|
||||
|
||||
TEST(spsc_q, full) {
|
||||
int n = 3;
|
||||
spsc_q_t rb = spsc_q_alloc(n, sizeof(int));
|
||||
spsc_q_t rb;
|
||||
EXPECT_TRUE(spsc_q_alloc(&rb, n, sizeof(int)));
|
||||
int a = 1;
|
||||
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
|
||||
a++;
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
|
||||
|
||||
# LDPC offload with the XDMA driver
|
||||
|
||||
[TOC]
|
||||
|
||||
This documentation aims to provide a tutorial for using Xilinx PCIe-XDMA based FPGA LDPC decoding within OAI. LDPC decoding is offloaded to FPGA.
|
||||
|
||||
## XDMA Driver Build & Install
|
||||
|
||||
- Get XDMA driver source
|
||||
```bash
|
||||
git clone https://github.com/Xilinx/dma_ip_drivers.git
|
||||
cd dma_ip_drivers/XDMA/linux-kernel
|
||||
```
|
||||
The *xdma_driver* directory contains the following:
|
||||
|
||||
```bash
|
||||
dma_ip_drivers/XDMA/linux-kernel/
|
||||
├── COPYING
|
||||
├── include
|
||||
├── LICENSE
|
||||
├── readme.txt
|
||||
├── RELEASE
|
||||
├── tests
|
||||
├── tools
|
||||
└── xdma
|
||||
```
|
||||
|
||||
Before building the driver, ensure that your system recognizes the Xilinx device. You can check this using the `lspci` command:
|
||||
|
||||
```bash
|
||||
$ lspci | grep Xilinx
|
||||
01:00.0 Serial controller: Xilinx Corporation Device 8038
|
||||
```
|
||||
|
||||
Building and Installing the Driver
|
||||
|
||||
```bash
|
||||
cd ~/dma_ip_drivers/XDMA/linux-kernel/xdma
|
||||
make clean
|
||||
make
|
||||
# install to make the driver loading automatically at system startup
|
||||
sudo make install
|
||||
```
|
||||
|
||||
Load the Driver
|
||||
|
||||
```bash
|
||||
cd ~/dma_ip_drivers/XDMA/linux-kernel/tests
|
||||
sudo ./load_driver.sh
|
||||
```
|
||||
|
||||
## OAI Build
|
||||
|
||||
```bash
|
||||
# Get openairinterface5g source code
|
||||
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
|
||||
cd ~/openairinterface5g
|
||||
|
||||
# Install OAI dependencies
|
||||
cd ~/openairinterface5g/cmake_targets
|
||||
./build_oai -I
|
||||
|
||||
# Build OAI gNB & UE
|
||||
cd ~/openairinterface5g
|
||||
source oaienv
|
||||
cd cmake_targets
|
||||
./build_oai --ninja -w SIMU --gNB --nrUE -P --build-lib "ldpc_xdma" -C -c
|
||||
```
|
||||
|
||||
Shared object file *libldpc_xdma.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using `--build-lib ldpc_xdma`.
|
||||
|
||||
## 5G PHY simulators
|
||||
|
||||
The simulated test uses the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface. Additionally, the option `--nrLDPC_coding_xdma.num_threads_prepare` is used to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
|
||||
|
||||
Another way to activate the feature is to add the `xdma.conf` file with the following content:
|
||||
|
||||
```
|
||||
nrLDPC_coding_xdma : {
|
||||
num_threads_prepare : 2;
|
||||
};
|
||||
|
||||
loader : {
|
||||
ldpc : {
|
||||
shlibversion : "_xdma";
|
||||
};
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
and use option `-O xdma.conf`.
|
||||
|
||||
### nr_ulsim test
|
||||
|
||||
Example command for running nr_ulsim with LDPC decoding offload to the FPGA:
|
||||
|
||||
```bash
|
||||
cd ~/openairinterface5g/cmake_targets/ran_build/build
|
||||
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```
|
||||
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P -O xdma.conf
|
||||
```
|
||||
|
||||
## Run
|
||||
|
||||
Both gNB and nrUE use the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface and `--nrLDPC_coding_xdma.num_threads_prepare` to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
|
||||
|
||||
Another way to activate the feature is to add the following content to the `.conf` file you want to use:
|
||||
|
||||
```
|
||||
nrLDPC_coding_xdma : {
|
||||
num_threads_prepare : 2;
|
||||
};
|
||||
|
||||
loader : {
|
||||
ldpc : {
|
||||
shlibversion : "_xdma";
|
||||
};
|
||||
};
|
||||
|
||||
```
|
||||
|
||||
and use option `-O *.conf`.
|
||||
|
||||
### gNB
|
||||
|
||||
Example command using rfsim:
|
||||
|
||||
```bash
|
||||
cd ~/openairinterface5g/cmake_targets/ran_build/build
|
||||
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf
|
||||
```
|
||||
|
||||
if you have added the configuration to the `.conf` file.
|
||||
|
||||
### UE
|
||||
|
||||
Example command using rfsim:
|
||||
|
||||
```bash
|
||||
cd ~/openairinterface5g/cmake_targets/ran_build/build
|
||||
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100 --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
|
||||
```
|
||||
|
||||
or
|
||||
|
||||
```bash
|
||||
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100
|
||||
```
|
||||
|
||||
if you have added the configuration to the `.conf` file.
|
||||
@@ -617,9 +617,9 @@ jumboframe 1 # enable jumbo frame
|
||||
|
||||
###### FR2
|
||||
|
||||
The OAI configuration file [`gnb.sa.band257.66prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.66prb.fhi72.2x2-liteon.conf) corresponds to:
|
||||
- TDD pattern `DDDDDDDSUU`, 1.25ms
|
||||
- Bandwidth 100MHz
|
||||
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-liteon.conf) corresponds to:
|
||||
- TDD pattern `DDDSU`, 0.625ms
|
||||
- Bandwidth 200MHz
|
||||
- FW v02.00.07
|
||||
- DL uses jumbo frame, UL uses standard MTU of 1500 bytes
|
||||
|
||||
|
||||
@@ -92,18 +92,17 @@ static void tx_func(processingData_L1tx_t *info)
|
||||
pushNotifiedFIFO(&gNB->resp_L1, res);
|
||||
|
||||
int tx_slot_type = nr_slot_select(cfg, frame_tx, slot_tx);
|
||||
// TODO check for analog_bf_vendor_ext set to 1 is a workaround while no beam API for beam selection is implemented
|
||||
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx
|
||||
|| IS_SOFTMODEM_RFSIM || cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) {
|
||||
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) {
|
||||
start_meas(&info->gNB->phy_proc_tx);
|
||||
phy_procedures_gNB_TX(info->gNB, &sched_response.DL_req, &sched_response.TX_req, &sched_response.UL_dci_req, frame_tx,slot_tx);
|
||||
|
||||
PHY_VARS_gNB *gNB = info->gNB;
|
||||
processingData_RU_t syncMsgRU;
|
||||
syncMsgRU.frame_tx = frame_tx;
|
||||
syncMsgRU.slot_tx = slot_tx;
|
||||
syncMsgRU.ru = gNB->RU_list[0];
|
||||
syncMsgRU.timestamp_tx = info->timestamp_tx;
|
||||
}
|
||||
processingData_RU_t syncMsgRU;
|
||||
syncMsgRU.frame_tx = frame_tx;
|
||||
syncMsgRU.slot_tx = slot_tx;
|
||||
syncMsgRU.ru = gNB->RU_list[0];
|
||||
syncMsgRU.timestamp_tx = info->timestamp_tx;
|
||||
ru_ctrl_func((void *)&syncMsgRU);
|
||||
if (tx_slot_type == NR_DOWNLINK_SLOT || tx_slot_type == NR_MIXED_SLOT || get_softmodem_params()->continuous_tx || IS_SOFTMODEM_RFSIM) {
|
||||
LOG_D(PHY, "gNB: %d.%d : calling RU TX function\n", syncMsgRU.frame_tx, syncMsgRU.slot_tx);
|
||||
ru_tx_func((void *)&syncMsgRU);
|
||||
stop_meas(&info->gNB->phy_proc_tx);
|
||||
|
||||
@@ -347,17 +347,28 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
|
||||
AssertFatal(*slot < fp->slots_per_frame && *slot >= 0, "slot %d is illegal (%d)\n", *slot, fp->slots_per_frame);
|
||||
|
||||
start_meas(&ru->rx_fhaul);
|
||||
int nb = ru->nb_rx * ru->num_beams_period;
|
||||
void *rxp[nb];
|
||||
for (int i = 0; i < nb; i++)
|
||||
rxp[i] = (void *)&ru->common.rxdata[i][get_samples_slot_timestamp(fp, *slot)];
|
||||
|
||||
openair0_timestamp_t old_ts = proc->timestamp_rx;
|
||||
LOG_D(PHY,"Reading %d samples for slot %d (%p)\n", samples_per_slot, *slot, rxp[0]);
|
||||
|
||||
openair0_timestamp_t ts;
|
||||
unsigned int rxs;
|
||||
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, nb);
|
||||
metadata mt = {.slot = *slot, .frame = *frame};
|
||||
if (ru->num_beams_period > 1) {
|
||||
void *rxp_flat[ru->num_beams_period * ru->nb_rx];
|
||||
void **rxp[ru->num_beams_period];
|
||||
for (int j = 0; j < ru->num_beams_period; j++) {
|
||||
for (int i = 0; i < ru->nb_rx; i++)
|
||||
rxp_flat[i + j * ru->nb_rx] = (void *)&ru->common.rxdata[i + j * ru->nb_rx][get_samples_slot_timestamp(fp, *slot)];
|
||||
rxp[j] = &rxp_flat[j * ru->nb_rx];
|
||||
}
|
||||
rxs = ru->rfdevice.trx_read_beams_func(&ru->rfdevice, &ts, (void ***)rxp, samples_per_slot, ru->nb_rx, ru->num_beams_period);
|
||||
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0][0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
|
||||
} else {
|
||||
void *rxp[ru->nb_rx];
|
||||
for (int i = 0; i < ru->nb_rx; i++)
|
||||
rxp[i] = (void *)&ru->common.rxdata[i][get_samples_slot_timestamp(fp, *slot)];
|
||||
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_per_slot, ru->nb_rx);
|
||||
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
|
||||
}
|
||||
|
||||
proc->timestamp_rx = ts-ru->ts_offset;
|
||||
|
||||
if (rxs != samples_per_slot)
|
||||
@@ -422,17 +433,28 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
|
||||
proc->first_rx = 0;
|
||||
*frame = proc->frame_rx;
|
||||
*slot = proc->tti_rx;
|
||||
samples_per_slot = get_samples_per_slot(*slot, fp); // slot number might change depending on the timestamp
|
||||
|
||||
// Align to slot boundary
|
||||
uint64_t samples_to_slot_boundary = 0;
|
||||
uint64_t sample_offset_within_frame = proc->timestamp_rx % fp->samples_per_frame;
|
||||
uint64_t sample_offset_within_slot = sample_offset_within_frame - get_samples_slot_timestamp(fp, *slot);
|
||||
if (sample_offset_within_slot > 0) {
|
||||
samples_to_slot_boundary = get_samples_per_slot(*slot, fp) - sample_offset_within_slot;
|
||||
samples_to_slot_boundary = samples_per_slot - sample_offset_within_slot;
|
||||
LOG_A(NR_PHY, "Aligning to the slot boundary %lu\n", samples_to_slot_boundary);
|
||||
|
||||
// Read and discard the samples in the first_rx to align to the slot boundary
|
||||
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp, samples_to_slot_boundary, nb);
|
||||
if (ru->num_beams_period > 1) {
|
||||
void *rxp_skip_b[ru->num_beams_period][ru->nb_rx];
|
||||
rxs = ru->rfdevice.trx_read_beams_func(&ru->rfdevice,
|
||||
&ts,
|
||||
(void ***)rxp_skip_b,
|
||||
samples_to_slot_boundary,
|
||||
ru->nb_rx,
|
||||
ru->num_beams_period);
|
||||
} else {
|
||||
void *rxp_skip[ru->nb_rx];
|
||||
rxs = ru->rfdevice.trx_read_func(&ru->rfdevice, &ts, rxp_skip, samples_to_slot_boundary, ru->nb_rx);
|
||||
}
|
||||
if (rxs != samples_to_slot_boundary)
|
||||
LOG_E(PHY, "rx_rf: Asked for %ld samples, got %d from USRP\n", samples_to_slot_boundary, rxs);
|
||||
|
||||
@@ -442,64 +464,92 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
|
||||
*slot = (*slot + 1) % fp->slots_per_frame;
|
||||
}
|
||||
}
|
||||
|
||||
metadata mt = {.slot = *slot, .frame = *frame};
|
||||
gNBscopeCopyWithMetadata(ru, gNbTimeDomainSamples, rxp[0], sizeof(c16_t), 1, samples_per_slot, 0, &mt);
|
||||
|
||||
if (rxs != samples_per_slot) {
|
||||
//exit_fun( "problem receiving samples" );
|
||||
LOG_E(PHY, "problem receiving samples\n");
|
||||
}
|
||||
|
||||
stop_meas(&ru->rx_fhaul);
|
||||
}
|
||||
|
||||
static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
|
||||
static void ctrl_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
radio_tx_gpio_flag_t flags_gpio = 0;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
openair0_config_t *cfg0 = &ru->openair0_cfg;
|
||||
int num_events = 0;
|
||||
int event_symbol[fp->symbols_per_slot];
|
||||
int event_counts[fp->symbols_per_slot];
|
||||
int beam_events[fp->symbols_per_slot][ru->num_beams_period];
|
||||
|
||||
switch (cfg0->gpio_controller) {
|
||||
case RU_GPIO_CONTROL_GENERIC:
|
||||
// currently we switch beams at the beginning of a slot and we take the beam index of the first symbol of this slot
|
||||
// we only send the beam to the gpio if the beam is different from the previous slot
|
||||
int last_applied_beams[ru->num_beams_period];
|
||||
for (int i = 0; i < ru->num_beams_period; i++)
|
||||
last_applied_beams[i] = -1;
|
||||
|
||||
if (ru->common.beam_id) {
|
||||
int prev_slot = (slot - 1 + fp->slots_per_frame) % fp->slots_per_frame;
|
||||
const int *beam_ids = ru->common.beam_id[0];
|
||||
int prev_beam = beam_ids[prev_slot * fp->symbols_per_slot];
|
||||
int beam = beam_ids[slot * fp->symbols_per_slot];
|
||||
if (prev_beam != beam) {
|
||||
flags_gpio = beam | TX_GPIO_CHANGE; // enable change of gpio
|
||||
LOG_I(HW, "slot %d, beam %d\n", slot, ru->common.beam_id[0][slot * fp->symbols_per_slot]);
|
||||
}
|
||||
}
|
||||
break;
|
||||
int first_available_sym = 0;
|
||||
bool idle_beam = true;
|
||||
for (int j = 0; j < fp->symbols_per_slot; j++) {
|
||||
int current_beams[ru->num_beams_period];
|
||||
int active_count = 0;
|
||||
bool differs = false;
|
||||
|
||||
case RU_GPIO_CONTROL_INTERDIGITAL: {
|
||||
// the beam index is written in bits 8-10 of the flags
|
||||
// bit 11 enables the gpio programming
|
||||
int beam = 0;
|
||||
if ((slot % 10 == 0) && ru->common.beam_id && (ru->common.beam_id[0][slot * fp->symbols_per_slot] < 64)) {
|
||||
// beam = ru->common.beam_id[0][slot*fp->symbols_per_slot] | 64;
|
||||
beam = 1024; // hardcoded now for beam32 boresight
|
||||
// beam = 127; //for the sake of trying beam63
|
||||
LOG_D(HW, "slot %d, beam %d\n", slot, beam);
|
||||
}
|
||||
flags_gpio = beam | TX_GPIO_CHANGE;
|
||||
// flags_gpio |= beam << 8; // MSB 8 bits are used for beam
|
||||
LOG_I(HW, "slot %d, beam %d, flags_gpio %d\n", slot, beam, flags_gpio);
|
||||
break;
|
||||
for (int i = 0; i < ru->num_beams_period; i++) {
|
||||
current_beams[i] = ru->common.beam_id[i][slot * fp->symbols_per_slot + j];
|
||||
if (current_beams[i] != last_applied_beams[i])
|
||||
differs = true;
|
||||
if (current_beams[i] != -1)
|
||||
active_count++;
|
||||
}
|
||||
|
||||
if (active_count > 0) {
|
||||
if (differs) {
|
||||
event_symbol[num_events] = first_available_sym;
|
||||
event_counts[num_events] = 0;
|
||||
for (int i = 0; i < ru->num_beams_period; i++) {
|
||||
if (current_beams[i] != -1) {
|
||||
beam_events[num_events][event_counts[num_events]] = current_beams[i];
|
||||
event_counts[num_events]++;
|
||||
}
|
||||
last_applied_beams[i] = current_beams[i];
|
||||
}
|
||||
num_events++;
|
||||
}
|
||||
idle_beam = false;
|
||||
first_available_sym = j;
|
||||
} else {
|
||||
if (!idle_beam) {
|
||||
first_available_sym = j;
|
||||
idle_beam = true;
|
||||
for (int i = 0; i < ru->num_beams_period; i++)
|
||||
last_applied_beams[i] = -1;
|
||||
}
|
||||
}
|
||||
default:
|
||||
AssertFatal(false, "illegal GPIO controller %d\n", cfg0->gpio_controller);
|
||||
}
|
||||
|
||||
return flags_gpio;
|
||||
nfapi_nr_config_request_scf_t *cfg = &ru->config;
|
||||
uint64_t slot_base_ts = timestamp + ru->ts_offset;
|
||||
if (cfg->cell_config.frame_duplex_type.value == TDD
|
||||
&& nr_slot_select(cfg, frame, slot % fp->slots_per_frame) == NR_DOWNLINK_SLOT
|
||||
&& nr_slot_select(cfg, frame, (slot + fp->slots_per_frame - 1) % fp->slots_per_frame) == NR_UPLINK_SLOT
|
||||
&& !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM) {
|
||||
slot_base_ts -= ru->sf_extension;
|
||||
}
|
||||
|
||||
for (int e = 0; e < num_events; e++) {
|
||||
if (e > 0 && event_symbol[e] == event_symbol[e-1])
|
||||
continue;
|
||||
|
||||
uint64_t symbol_offset = 0;
|
||||
if (event_symbol[e] > 0)
|
||||
symbol_offset = get_samples_symbol_duration(fp, slot, 0, event_symbol[e]);
|
||||
|
||||
uint64_t event_ts = slot_base_ts + symbol_offset;
|
||||
for (int n = 0; n < event_counts[e]; n++)
|
||||
LOG_D(NR_PHY,
|
||||
"RU Control [%d.%d]: Trigger Sym %d, Beam %d at TS %lu\n",
|
||||
frame,
|
||||
slot,
|
||||
event_symbol[e],
|
||||
beam_events[e][n],
|
||||
event_ts);
|
||||
ru->rfdevice.trx_set_beams2(&ru->rfdevice, beam_events[e], event_counts[e], event_ts);
|
||||
}
|
||||
}
|
||||
|
||||
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
static void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
|
||||
{
|
||||
RU_proc_t *proc = &ru->proc;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
@@ -514,7 +564,6 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
int sf_extension = 0;
|
||||
int siglen = get_samples_per_slot(slot, fp);
|
||||
radio_tx_burst_flag_t flags_burst = TX_BURST_INVALID;
|
||||
radio_tx_gpio_flag_t flags_gpio = 0;
|
||||
|
||||
if (cfg->cell_config.frame_duplex_type.value == TDD && !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM) {
|
||||
int slot_type = nr_slot_select(cfg,frame,slot%fp->slots_per_frame);
|
||||
@@ -558,24 +607,41 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
|
||||
}
|
||||
|
||||
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
|
||||
flags_gpio = get_gpio_flags(ru, slot);
|
||||
|
||||
const int flags = flags_burst | (flags_gpio << 4);
|
||||
proc->first_tx = 0;
|
||||
|
||||
int nt = ru->nb_tx * ru->num_beams_period;
|
||||
void *txp[nt];
|
||||
for (int i = 0; i < nt; i++)
|
||||
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
|
||||
double sig_en = 0;
|
||||
uint32_t txs;
|
||||
if (ru->num_beams_period > 1) {
|
||||
void *txp_flat[ru->num_beams_period * ru->nb_tx];
|
||||
void **txp[ru->num_beams_period];
|
||||
for (int b = 0; b < ru->num_beams_period; b++) {
|
||||
for (int i = 0; i < ru->nb_tx; i++)
|
||||
txp_flat[b * ru->nb_tx + i] = (void *)&ru->common.txdata[i + b * ru->nb_tx][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
|
||||
txp[b] = &txp_flat[b * ru->nb_tx];
|
||||
}
|
||||
sig_en = (double)signal_energy(txp[0][0], siglen + sf_extension);
|
||||
txs = ru->rfdevice.trx_write_beams_func(&ru->rfdevice,
|
||||
timestamp + ru->ts_offset - sf_extension,
|
||||
(void ***)txp,
|
||||
siglen + sf_extension,
|
||||
ru->nb_tx,
|
||||
ru->num_beams_period,
|
||||
flags_burst);
|
||||
} else {
|
||||
void *txp[ru->nb_tx];
|
||||
for (int i = 0; i < ru->nb_tx; i++)
|
||||
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
|
||||
|
||||
sig_en = (double)signal_energy(txp[0], siglen + sf_extension);
|
||||
// prepare tx buffer pointers
|
||||
txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
|
||||
timestamp + ru->ts_offset - sf_extension,
|
||||
txp,
|
||||
siglen + sf_extension,
|
||||
ru->nb_tx,
|
||||
flags_burst);
|
||||
}
|
||||
|
||||
// prepare tx buffer pointers
|
||||
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
|
||||
timestamp + ru->ts_offset - sf_extension,
|
||||
txp,
|
||||
siglen + sf_extension,
|
||||
nt,
|
||||
flags);
|
||||
LOG_D(PHY,
|
||||
"[TXPATH] RU %d tx_rf, writing to TS %lu, %d.%d, unwrapped_frame %d, slot %d, flags %d, siglen+sf_extension %d, "
|
||||
"returned %d, E %f\n",
|
||||
@@ -585,10 +651,10 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
|
||||
slot,
|
||||
proc->frame_tx_unwrap,
|
||||
slot,
|
||||
flags,
|
||||
flags_burst,
|
||||
siglen + sf_extension,
|
||||
txs,
|
||||
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
|
||||
10 * log10(sig_en));
|
||||
}
|
||||
|
||||
static void fill_rf_config(RU_t *ru, char *rf_config_file)
|
||||
@@ -742,6 +808,25 @@ int setup_RU_buffers(RU_t *ru)
|
||||
return(0);
|
||||
}
|
||||
|
||||
void ru_ctrl_func(void *param)
|
||||
{
|
||||
processingData_RU_t *info = (processingData_RU_t *) param;
|
||||
int frame = info->frame_tx;
|
||||
int slot = info->slot_tx;
|
||||
LOG_D(PHY,"ru_ctrl_func: frame = %d, slot = %d\n", frame, slot);
|
||||
RU_t *ru = info->ru;
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
if (ru->gNB_list[0]->common_vars.analog_bf) {
|
||||
for (int i = 0; i < ru->num_beams_period; i++) {
|
||||
memcpy((void*) &ru->common.beam_id[i][slot * fp->symbols_per_slot],
|
||||
(void*) &ru->gNB_list[0]->common_vars.beam_id[i][slot * fp->symbols_per_slot],
|
||||
(fp->symbols_per_slot) * sizeof(int));
|
||||
}
|
||||
}
|
||||
if (ru->fh_south_ctrl)
|
||||
ru->fh_south_ctrl(ru, frame, slot, info->timestamp_tx);
|
||||
}
|
||||
|
||||
void ru_tx_func(void *param)
|
||||
{
|
||||
processingData_RU_t *info = (processingData_RU_t *) param;
|
||||
@@ -1134,6 +1219,7 @@ void set_function_spec_param(RU_t *ru)
|
||||
ru->rfdevice.host_type = RAU_HOST;
|
||||
ru->fh_south_in = rx_rf; // local synchronous RF RX
|
||||
ru->fh_south_out = tx_rf; // local synchronous RF TX
|
||||
ru->fh_south_ctrl = ctrl_rf; // local sunchronous RF control
|
||||
ru->start_rf = start_rf; // need to start the local RF interface
|
||||
ru->stop_rf = stop_rf;
|
||||
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
|
||||
|
||||
@@ -19,11 +19,6 @@ 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>
|
||||
@@ -532,13 +527,6 @@ 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
|
||||
@@ -745,11 +733,6 @@ 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");
|
||||
|
||||
@@ -310,7 +310,7 @@ extern int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
|
||||
extern int usrp_tx_thread;
|
||||
extern int sf_ahead;
|
||||
extern int oai_exit;
|
||||
|
||||
void ru_ctrl_func(void *param);
|
||||
void ru_tx_func(void *param);
|
||||
void configure_ru(void *, void *arg);
|
||||
void configure_rru(void *, void *arg);
|
||||
|
||||
@@ -48,30 +48,6 @@ Please refer to the dedicated documentation at [LDPC_OFFLOAD_SETUP.md](file://..
|
||||
|
||||
`libldpc_aal.so` has its decoder and its encoder implemented in [nrLDPC_coding_aal.c](file://../nrLDPC_coding/nrLDPC_coding_aal/nrLDPC_coding_aal.c).
|
||||
|
||||
loading `libldpc_xdma.so` instead of `libldpc.so`:
|
||||
|
||||
`make ldpc_xdma` or `ninja ldpc_xdma`
|
||||
|
||||
This command creates the `libldpc_xdma.so` shared library.
|
||||
|
||||
```
|
||||
ninja ldpc_xdma
|
||||
[2/2] Linking C shared module libldpc_xdma.so
|
||||
```
|
||||
|
||||
At runtime, to successfully use the xdma, you need to install vendor specific drivers and tools.\
|
||||
Please refer to the dedicated documentation at [LDPC_XDMA_OFFLOAD_SETUP.md](file://../../../../doc/LDPC_XDMA_OFFLOAD_SETUP.md).
|
||||
|
||||
```
|
||||
./nr-softmodem -O libconfig:gnb.band78.sa.fr1.106PRB.usrpb210.conf:dbgl5 --rfsim --rfsimulator.[0].serveraddr server --log_config.gtpu_log_level info --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
|
||||
```
|
||||
|
||||
`libldpc_xdma.so` has its decoder implemented in [nrLDPC_coding_xdma.c](file://../nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c).\
|
||||
Its encoder is implemented in [nrLDPC_coding_segment_encoder.c](file://../nrLDPC_coding/nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c).
|
||||
|
||||
*Note: `libldpc_xdma.so` relies on a segment coding library for encoding.*
|
||||
*The segment coding library is `libldpc.so` by default but it can be chosen with option `--nrLDPC_coding_xdma.encoder_shlibversion` followed by the library version - like with `--loder.ldpc.shlibversion` in the segment coding case above -*
|
||||
|
||||
#### Examples of ldpc shared lib selection when running ldpctest:
|
||||
|
||||
Slot coding libraries cannot be used yet within ldpctest.
|
||||
@@ -86,8 +62,6 @@ Libraries implementing the slotwise LDPC coding must be named `libldpc<_version>
|
||||
|
||||
`libldpc_aal.so` is completed.
|
||||
|
||||
`libldpc_xdma.so` is completed.
|
||||
|
||||
## LDPC segment coding
|
||||
The interface of the library is defined in [nrLDPC_defs.h](file://../nrLDPC_defs.h) as typedefs of the functions of the interface.
|
||||
The name of the functions implementing these typedefs can be found in [nrLDPC_extern.h](file://../nrLDPC_extern.h).
|
||||
|
||||
@@ -1,5 +1,4 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
add_subdirectory(nrLDPC_coding_segment)
|
||||
add_subdirectory(nrLDPC_coding_xdma)
|
||||
add_subdirectory(nrLDPC_coding_aal)
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
|
||||
##########################################################
|
||||
|
||||
# LDPC offload library - XDMA
|
||||
##########################################################
|
||||
|
||||
add_boolean_option(ENABLE_LDPC_XDMA OFF "Build support for LDPC Offload to XDMA library" OFF)
|
||||
|
||||
if (ENABLE_LDPC_XDMA)
|
||||
|
||||
add_library(ldpc_xdma MODULE
|
||||
nrLDPC_coding_xdma_offload.c
|
||||
nrLDPC_coding_xdma.c
|
||||
../nrLDPC_coding_segment/nr_rate_matching.c
|
||||
../nrLDPC_coding_segment/nrLDPC_coding_segment_encoder.c
|
||||
../../nrLDPC_encoder/ldpc_encoder_optim8segmulti.c
|
||||
)
|
||||
target_include_directories(ldpc_xdma PRIVATE ../nrLDPC_coding_segment)
|
||||
|
||||
target_link_libraries(ldpc_xdma PRIVATE ldpc_gen_HEADERS log_headers)
|
||||
set_target_properties(ldpc_xdma PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR})
|
||||
|
||||
add_dependencies(nr-softmodem ldpc_xdma)
|
||||
add_dependencies(nr-uesoftmodem ldpc_xdma)
|
||||
add_dependencies(nr_ulsim ldpc_xdma)
|
||||
add_dependencies(nr_ulschsim ldpc_xdma)
|
||||
add_dependencies(nr_dlsim ldpc_xdma)
|
||||
add_dependencies(nr_dlschsim ldpc_xdma)
|
||||
|
||||
endif()
|
||||
@@ -1,414 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
/*! \file PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma.c
|
||||
* \brief Top-level routines for decoding LDPC (ULSCH) transport channels
|
||||
* decoding implemented using a FEC IP core on FPGA through XDMA driver
|
||||
*/
|
||||
|
||||
// [from gNB coding]
|
||||
#include <syscall.h>
|
||||
|
||||
#include <nr_rate_matching.h>
|
||||
#include "PHY/CODING/coding_defs.h"
|
||||
#include "PHY/CODING/coding_extern.h"
|
||||
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_xdma/nrLDPC_coding_xdma_offload.h"
|
||||
#include "PHY/CODING/nrLDPC_extern.h"
|
||||
#include "common/utils/LOG/log.h"
|
||||
#include "defs.h"
|
||||
// #define DEBUG_ULSCH_DECODING
|
||||
// #define gNB_DEBUG_TRACE
|
||||
|
||||
#define OAI_UL_LDPC_MAX_NUM_LLR 27000 // 26112 // NR_LDPC_NCOL_BG1*NR_LDPC_ZMAX = 68*384
|
||||
// #define DEBUG_CRC
|
||||
#ifdef DEBUG_CRC
|
||||
#define PRINT_CRC_CHECK(a) a
|
||||
#else
|
||||
#define PRINT_CRC_CHECK(a)
|
||||
#endif
|
||||
|
||||
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_interface.h"
|
||||
#include "nfapi/open-nFAPI/nfapi/public_inc/nfapi_nr_interface.h"
|
||||
|
||||
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"
|
||||
|
||||
// Global var to limit the rework of the dirty legacy code
|
||||
int num_threads_prepare_max = 0;
|
||||
char *user_device = NULL;
|
||||
char *enc_read_device = NULL;
|
||||
char *enc_write_device = NULL;
|
||||
char *dec_read_device = NULL;
|
||||
char *dec_write_device = NULL;
|
||||
|
||||
/*!
|
||||
* \typedef args_fpga_decode_prepare_t
|
||||
* \struct args_fpga_decode_prepare_s
|
||||
* \brief arguments structure for passing arguments to the nr_ulsch_FPGA_decoding_prepare_blocks function
|
||||
*/
|
||||
typedef struct args_fpga_decode_prepare_s {
|
||||
nrLDPC_TB_decoding_parameters_t *TB_params; /*!< transport blocks parameters */
|
||||
|
||||
uint8_t *multi_indata; /*!< pointer to the head of the block destination array that is then passed to the FPGA decoding */
|
||||
int no_iteration_ldpc; /*!< pointer to the number of iteration set by this function */
|
||||
uint32_t r_first; /*!< index of the first block to be prepared within this function */
|
||||
uint32_t r_span; /*!< number of blocks to be prepared within this function */
|
||||
int r_offset; /*!< r index expressed in bits */
|
||||
int input_CBoffset; /*!< */
|
||||
int Kc; /*!< ratio between the number of columns in the parity check graph and the lifting size */
|
||||
int Kprime; /*!< size of payload and CRC bits in a code block */
|
||||
task_ans_t *ans; /*!< pointer to the answer that is used by thread pool to detect job completion */
|
||||
} args_fpga_decode_prepare_t;
|
||||
|
||||
int32_t nrLDPC_coding_init(void);
|
||||
int32_t nrLDPC_coding_shutdown(void);
|
||||
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *slot_params, int frame_rx, int slot_rx);
|
||||
// int32_t nrLDPC_coding_encoder(void);
|
||||
int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int slot_rx, tpool_t *ldpc_threadPool);
|
||||
void nr_ulsch_FPGA_decoding_prepare_blocks(void *args);
|
||||
|
||||
int32_t nrLDPC_coding_init(void)
|
||||
{
|
||||
paramdef_t LoaderParams[] = {
|
||||
{"num_threads_prepare", NULL, 0, .iptr = &num_threads_prepare_max, .defintval = 0, TYPE_INT, 0, NULL},
|
||||
{"user_device", NULL, 0, .strptr = &user_device, .defstrval = DEVICE_NAME_DEFAULT_USER, TYPE_STRING, 0, NULL},
|
||||
{"enc_read_device", NULL, 0, .strptr = &enc_read_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_READ, TYPE_STRING, 0, NULL},
|
||||
{"enc_write_device", NULL, 0, .strptr = &enc_write_device, .defstrval = DEVICE_NAME_DEFAULT_ENC_WRITE, TYPE_STRING, 0, NULL},
|
||||
{"dec_read_device", NULL, 0, .strptr = &dec_read_device, .defstrval = DEVICE_NAME_DEFAULT_DEC_READ, TYPE_STRING, 0, NULL},
|
||||
{"dec_write_device", NULL, 0, .strptr = &dec_write_device, .defstrval = DEVICE_NAME_DEFAULT_DEC_WRITE, TYPE_STRING, 0, NULL}};
|
||||
config_get(config_get_if(), LoaderParams, sizeofArray(LoaderParams), "nrLDPC_coding_xdma");
|
||||
AssertFatal(num_threads_prepare_max != 0, "nrLDPC_coding_xdma.num_threads_prepare was not provided");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t nrLDPC_coding_shutdown(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int32_t nrLDPC_coding_decoder(nrLDPC_slot_decoding_parameters_t *slot_params, int frame_rx, int slot_rx)
|
||||
{
|
||||
int nbDecode = 0;
|
||||
for (int ULSCH_id = 0; ULSCH_id < slot_params->nb_TBs; ULSCH_id++)
|
||||
nbDecode += decoder_xdma(&slot_params->TBs[ULSCH_id], frame_rx, slot_rx, slot_params->threadPool);
|
||||
return nbDecode;
|
||||
}
|
||||
|
||||
/*
|
||||
int32_t nrLDPC_coding_encoder(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
|
||||
int decoder_xdma(nrLDPC_TB_decoding_parameters_t *TB_params, int frame_rx, int slot_rx, tpool_t *ldpc_threadPool)
|
||||
{
|
||||
const uint32_t K = TB_params->K;
|
||||
const int Kc = TB_params->BG == 2 ? 52 : 68;
|
||||
int r_offset = 0, offset = 0;
|
||||
int Kprime = K - TB_params->F;
|
||||
|
||||
// FPGA parameter preprocessing
|
||||
static uint8_t multi_indata[27000 * 25]; // FPGA input data
|
||||
static uint8_t multi_outdata[1100 * 25]; // FPGA output data
|
||||
|
||||
int bg_len = TB_params->BG == 1 ? 22 : 10;
|
||||
|
||||
// Calc input CB offset
|
||||
int input_CBoffset = TB_params->Z * Kc * 8;
|
||||
if ((input_CBoffset & 0x7F) == 0)
|
||||
input_CBoffset = input_CBoffset / 8;
|
||||
else
|
||||
input_CBoffset = 16 * ((input_CBoffset / 128) + 1);
|
||||
|
||||
DecIFConf dec_conf = {0};
|
||||
dec_conf.Zc = TB_params->Z;
|
||||
dec_conf.BG = TB_params->BG;
|
||||
dec_conf.max_iter = TB_params->max_ldpc_iterations;
|
||||
dec_conf.numCB = TB_params->C;
|
||||
// input soft bits length, Zc x 66 - length of filler bits
|
||||
dec_conf.numChannelLls = (Kprime - 2 * TB_params->Z) + (Kc * TB_params->Z - K);
|
||||
// filler bits length
|
||||
dec_conf.numFillerBits = TB_params->F;
|
||||
dec_conf.max_schedule = 0;
|
||||
dec_conf.SetIdx = 12;
|
||||
dec_conf.nRows = (dec_conf.BG == 1) ? 46 : 42;
|
||||
|
||||
dec_conf.user_device = user_device;
|
||||
dec_conf.enc_read_device = enc_read_device;
|
||||
dec_conf.enc_write_device = enc_write_device;
|
||||
dec_conf.dec_read_device = dec_read_device;
|
||||
dec_conf.dec_write_device = dec_write_device;
|
||||
|
||||
int out_CBoffset = dec_conf.Zc * bg_len;
|
||||
if ((out_CBoffset & 0x7F) == 0)
|
||||
out_CBoffset = out_CBoffset / 8;
|
||||
else
|
||||
out_CBoffset = 16 * ((out_CBoffset / 128) + 1);
|
||||
|
||||
#ifdef LDPC_DATA
|
||||
printf("\n------------------------\n");
|
||||
printf("BG:\t\t%d\n", dec_conf.BG);
|
||||
printf("TB_params->C: %d\n", TB_params->C);
|
||||
printf("TB_params->K: %d\n", TB_params->K);
|
||||
printf("TB_params->Z: %d\n", TB_params->Z);
|
||||
printf("TB_params->F: %d\n", TB_params->F);
|
||||
printf("numChannelLls:\t %d = (%d - 2 * %d) + (%d * %d - %d)\n",
|
||||
dec_conf.numChannelLls,
|
||||
Kprime,
|
||||
TB_params->Z,
|
||||
Kc,
|
||||
TB_params->Z,
|
||||
K);
|
||||
printf("numFillerBits:\t %d\n", TB_params->F);
|
||||
printf("------------------------\n");
|
||||
// ===================================
|
||||
// debug mode
|
||||
// ===================================
|
||||
FILE *fptr_llr, *fptr_ldpc;
|
||||
fptr_llr = fopen("../../../cmake_targets/log/ulsim_ldpc_llr.txt", "w");
|
||||
fptr_ldpc = fopen("../../../cmake_targets/log/ulsim_ldpc_output.txt", "w");
|
||||
// ===================================
|
||||
#endif
|
||||
|
||||
int length_dec = lenWithCrc(TB_params->C, TB_params->A);
|
||||
uint8_t crc_type = crcType(TB_params->C, TB_params->A);
|
||||
int no_iteration_ldpc = 2;
|
||||
|
||||
uint32_t num_threads_prepare = 0;
|
||||
|
||||
// calculate required number of jobs
|
||||
uint32_t r_while = 0;
|
||||
while (r_while < TB_params->C) {
|
||||
// calculate number of segments processed in the new job
|
||||
uint32_t modulus = (TB_params->C - r_while) % (num_threads_prepare_max - num_threads_prepare);
|
||||
uint32_t quotient = (TB_params->C - r_while) / (num_threads_prepare_max - num_threads_prepare);
|
||||
uint32_t r_span_max = modulus == 0 ? quotient : quotient + 1;
|
||||
|
||||
// saturate to be sure to not go above C
|
||||
uint32_t r_span = TB_params->C - r_while < r_span_max ? TB_params->C - r_while : r_span_max;
|
||||
|
||||
// increment
|
||||
num_threads_prepare++;
|
||||
r_while += r_span;
|
||||
}
|
||||
|
||||
args_fpga_decode_prepare_t arr[num_threads_prepare];
|
||||
task_ans_t ans[num_threads_prepare];
|
||||
memset(ans, 0, num_threads_prepare * sizeof(task_ans_t));
|
||||
thread_info_tm_t t_info = {.buf = (uint8_t *)arr, .len = 0, .cap = num_threads_prepare, .ans = ans};
|
||||
|
||||
// start the prepare jobs
|
||||
uint32_t r_remaining = 0;
|
||||
for (uint32_t r = 0; r < TB_params->C; r++) {
|
||||
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
|
||||
if (r_remaining == 0) {
|
||||
// TODO: int nr_tti_rx = 0;
|
||||
|
||||
args_fpga_decode_prepare_t *args = &((args_fpga_decode_prepare_t *)t_info.buf)[t_info.len];
|
||||
DevAssert(t_info.len < t_info.cap);
|
||||
args->ans = &t_info.ans[t_info.len];
|
||||
t_info.len += 1;
|
||||
|
||||
args->TB_params = TB_params;
|
||||
args->multi_indata = multi_indata;
|
||||
args->no_iteration_ldpc = no_iteration_ldpc;
|
||||
args->r_first = r;
|
||||
|
||||
uint32_t modulus = (TB_params->C - r) % (num_threads_prepare_max - num_threads_prepare);
|
||||
uint32_t quotient = (TB_params->C - r) / (num_threads_prepare_max - num_threads_prepare);
|
||||
uint32_t r_span_max = modulus == 0 ? quotient : quotient + 1;
|
||||
|
||||
uint32_t r_span = TB_params->C - r < r_span_max ? TB_params->C - r : r_span_max;
|
||||
args->r_span = r_span;
|
||||
args->r_offset = r_offset;
|
||||
args->input_CBoffset = input_CBoffset;
|
||||
args->Kc = Kc;
|
||||
args->Kprime = Kprime;
|
||||
|
||||
r_remaining = r_span;
|
||||
|
||||
task_t t = {.func = &nr_ulsch_FPGA_decoding_prepare_blocks, .args = args};
|
||||
pushTpool(ldpc_threadPool, t);
|
||||
|
||||
LOG_D(PHY, "Added %d block(s) to prepare for decoding, in pipe: %d to %d\n", r_span, r, r + r_span - 1);
|
||||
}
|
||||
r_offset += segment_params->E;
|
||||
offset += ((K >> 3) - (TB_params->F >> 3) - ((TB_params->C > 1) ? 3 : 0));
|
||||
r_remaining -= 1;
|
||||
}
|
||||
|
||||
// reset offset in order to properly fill the output array later
|
||||
offset = 0;
|
||||
|
||||
DevAssert(num_threads_prepare == t_info.len);
|
||||
|
||||
// wait for the prepare jobs to complete
|
||||
join_task_ans(t_info.ans);
|
||||
|
||||
for (uint32_t job = 0; job < num_threads_prepare; job++) {
|
||||
args_fpga_decode_prepare_t *args = &arr[job];
|
||||
if (args->no_iteration_ldpc >= TB_params->max_ldpc_iterations)
|
||||
no_iteration_ldpc = TB_params->max_ldpc_iterations;
|
||||
}
|
||||
|
||||
// launch decode with FPGA
|
||||
LOG_I(PHY, "Run the LDPC ------[FPGA version]------\n");
|
||||
//==================================================================
|
||||
// Xilinx FPGA LDPC decoding function -> nrLDPC_decoder_FPGA_PYM()
|
||||
//==================================================================
|
||||
start_meas(&TB_params->segments[0].ts_ldpc_decode);
|
||||
nrLDPC_decoder_FPGA_PYM(&multi_indata[0], &multi_outdata[0], dec_conf);
|
||||
// printf("Xilinx FPGA -> CB = %d\n", harq_process->C);
|
||||
stop_meas(&TB_params->segments[0].ts_ldpc_decode);
|
||||
|
||||
*TB_params->processedSegments = 0;
|
||||
for (uint32_t r = 0; r < TB_params->C; r++) {
|
||||
// ------------------------------------------------------------
|
||||
// --------------------- copy FPGA output ---------------------
|
||||
// ------------------------------------------------------------
|
||||
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
|
||||
if (check_crc(multi_outdata, length_dec, crc_type)) {
|
||||
#ifdef DEBUG_CRC
|
||||
LOG_I(PHY, "Segment %d CRC OK\n", r);
|
||||
#endif
|
||||
no_iteration_ldpc = 2;
|
||||
} else {
|
||||
#ifdef DEBUG_CRC
|
||||
LOG_I(PHY, "segment %d CRC NOK\n", r);
|
||||
#endif
|
||||
no_iteration_ldpc = TB_params->max_ldpc_iterations;
|
||||
}
|
||||
for (int i = 0; i < out_CBoffset; i++) {
|
||||
segment_params->c[i] = multi_outdata[i + r * out_CBoffset];
|
||||
}
|
||||
segment_params->decodeSuccess = (no_iteration_ldpc < TB_params->max_ldpc_iterations);
|
||||
if (segment_params->decodeSuccess) {
|
||||
*TB_params->processedSegments = *TB_params->processedSegments + 1;
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*!
|
||||
* \fn nr_ulsch_FPGA_decoding_prepare_blocks(void *args)
|
||||
* \brief prepare blocks for LDPC decoding on FPGA
|
||||
*
|
||||
* \param args pointer to the arguments of the function in a structure of type args_fpga_decode_prepare_t
|
||||
*/
|
||||
void nr_ulsch_FPGA_decoding_prepare_blocks(void *args)
|
||||
{
|
||||
// extract the arguments
|
||||
args_fpga_decode_prepare_t *arguments = (args_fpga_decode_prepare_t *)args;
|
||||
|
||||
nrLDPC_TB_decoding_parameters_t *TB_params = arguments->TB_params;
|
||||
|
||||
uint8_t Qm = TB_params->Qm;
|
||||
|
||||
uint8_t BG = TB_params->BG;
|
||||
uint8_t rv_index = TB_params->rv_index;
|
||||
uint8_t max_ldpc_iterations = TB_params->max_ldpc_iterations;
|
||||
|
||||
uint32_t tbslbrm = TB_params->tbslbrm;
|
||||
uint32_t K = TB_params->K;
|
||||
uint32_t Z = TB_params->Z;
|
||||
uint32_t F = TB_params->F;
|
||||
|
||||
uint32_t C = TB_params->C;
|
||||
|
||||
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[0];
|
||||
|
||||
short *ulsch_llr = segment_params->llr;
|
||||
|
||||
uint8_t *multi_indata = arguments->multi_indata;
|
||||
int no_iteration_ldpc = arguments->no_iteration_ldpc;
|
||||
uint32_t r_first = arguments->r_first;
|
||||
uint32_t r_span = arguments->r_span;
|
||||
int r_offset = arguments->r_offset;
|
||||
int input_CBoffset = arguments->input_CBoffset;
|
||||
int Kc = arguments->Kc;
|
||||
int Kprime = arguments->Kprime;
|
||||
|
||||
int16_t z[68 * 384 + 16] __attribute__((aligned(16)));
|
||||
simde__m128i *pv = (simde__m128i *)&z;
|
||||
|
||||
// the function processes r_span blocks starting from block at index r_first in ulsch_llr
|
||||
for (uint32_t r = r_first; r < (r_first + r_span); r++) {
|
||||
nrLDPC_segment_decoding_parameters_t *segment_params = &TB_params->segments[r];
|
||||
// ----------------------- FPGA pre process ------------------------
|
||||
simde__m128i ones = simde_mm_set1_epi8(255); // Generate a vector with all elements set to 255
|
||||
simde__m128i *temp_multi_indata = (simde__m128i *)&multi_indata[r * input_CBoffset];
|
||||
// -----------------------------------------------------------------
|
||||
|
||||
// code blocks after bit selection in rate matching for LDPC code (38.212 V15.4.0 section 5.4.2.1)
|
||||
int16_t harq_e[segment_params->E];
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// deinterleaving
|
||||
// -------------------------------------------------------------------------------------------
|
||||
start_meas(&segment_params->ts_deinterleave);
|
||||
nr_deinterleaving_ldpc(segment_params->E, Qm, harq_e, ulsch_llr + r_offset);
|
||||
stop_meas(&segment_params->ts_deinterleave);
|
||||
// -------------------------------------------------------------------------------------------
|
||||
// dematching
|
||||
// -------------------------------------------------------------------------------------------
|
||||
start_meas(&segment_params->ts_rate_unmatch);
|
||||
if (nr_rate_matching_ldpc_rx(tbslbrm,
|
||||
BG,
|
||||
Z,
|
||||
segment_params->d,
|
||||
harq_e,
|
||||
C,
|
||||
rv_index,
|
||||
*segment_params->d_to_be_cleared,
|
||||
segment_params->E,
|
||||
F,
|
||||
K - F - 2 * Z)
|
||||
== -1) {
|
||||
stop_meas(&segment_params->ts_rate_unmatch);
|
||||
LOG_E(PHY, "ulsch_decoding.c: Problem in rate_matching\n");
|
||||
no_iteration_ldpc = max_ldpc_iterations;
|
||||
arguments->no_iteration_ldpc = no_iteration_ldpc;
|
||||
return;
|
||||
} else {
|
||||
stop_meas(&segment_params->ts_rate_unmatch);
|
||||
}
|
||||
|
||||
*segment_params->d_to_be_cleared = false;
|
||||
|
||||
memset(segment_params->c, 0, K >> 3);
|
||||
|
||||
// set first 2*Z_c bits to zeros
|
||||
memset(&z[0], 0, 2 * Z * sizeof(int16_t));
|
||||
// set Filler bits
|
||||
memset((&z[0] + Kprime), 127, F * sizeof(int16_t));
|
||||
// Move coded bits before filler bits
|
||||
memcpy((&z[0] + 2 * Z), segment_params->d, (Kprime - 2 * Z) * sizeof(int16_t));
|
||||
// skip filler bits
|
||||
memcpy((&z[0] + K), segment_params->d + (K - 2 * Z), (Kc * Z - K) * sizeof(int16_t));
|
||||
|
||||
// Saturate coded bits before decoding into 8 bits values
|
||||
for (int i = 0, j = 0; j < ((Kc * Z) >> 4); i += 2, j++) {
|
||||
temp_multi_indata[j] =
|
||||
simde_mm_xor_si128(simde_mm_packs_epi16(pv[i], pv[i + 1]),
|
||||
simde_mm_cmpeq_epi32(ones,
|
||||
ones)); // Perform NOT operation and write the result to temp_multi_indata[j]
|
||||
}
|
||||
|
||||
// the last bytes before reaching "Kc * harq_process->Z" should not be written 128 bits at a time to avoid overwritting the
|
||||
// following block in multi_indata
|
||||
simde__m128i tmp =
|
||||
simde_mm_xor_si128(simde_mm_packs_epi16(pv[2 * ((Kc * Z) >> 4)], pv[2 * ((Kc * Z) >> 4) + 1]),
|
||||
simde_mm_cmpeq_epi32(ones,
|
||||
ones)); // Perform NOT operation and write the result to temp_multi_indata[j]
|
||||
uint8_t *tmp_p = (uint8_t *)&tmp;
|
||||
for (int i = 0, j = ((Kc * Z) & 0xfffffff0); j < Kc * Z; i++, j++) {
|
||||
multi_indata[r * input_CBoffset + j] = tmp_p[i];
|
||||
}
|
||||
|
||||
r_offset += segment_params->E;
|
||||
}
|
||||
|
||||
arguments->no_iteration_ldpc = no_iteration_ldpc;
|
||||
}
|
||||
@@ -1,783 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Xilinx, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license
|
||||
* the terms of the BSD Licence are reported below:
|
||||
*
|
||||
* BSD License
|
||||
*
|
||||
* For Xilinx DMA IP software
|
||||
*
|
||||
* Copyright (c) 2016-present, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name Xilinx nor the names of its contributors may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#define _DEFAULT_SOURCE
|
||||
#define _XOPEN_SOURCE 500
|
||||
|
||||
#include <assert.h>
|
||||
#include <fcntl.h>
|
||||
#include <getopt.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <time.h>
|
||||
#include <byteswap.h>
|
||||
#include <signal.h>
|
||||
#include <ctype.h>
|
||||
#include <termios.h>
|
||||
|
||||
#include <sys/mman.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/types.h>
|
||||
|
||||
#include "xdma_diag.h"
|
||||
#include "nrLDPC_coding_xdma_offload.h"
|
||||
|
||||
#include "common/utils/assertions.h"
|
||||
|
||||
typedef unsigned long long U64;
|
||||
void* map_base;
|
||||
int fd;
|
||||
int fd_enc_write, fd_enc_read;
|
||||
char *dev_enc_write, *dev_enc_read;
|
||||
int fd_dec_write, fd_dec_read;
|
||||
char *dev_dec_write, *dev_dec_read;
|
||||
char allocated_write[24 * 1024] __attribute__((aligned(4096)));
|
||||
char allocated_read[24 * 1024 * 3] __attribute__((aligned(4096)));
|
||||
|
||||
// dma_from_device.c
|
||||
|
||||
// [Start] #include "dma_utils.c" ===================================
|
||||
|
||||
/*
|
||||
* man 2 write:
|
||||
* On Linux, write() (and similar system calls) will transfer at most
|
||||
* 0x7ffff000 (2,147,479,552) bytes, returning the number of bytes
|
||||
* actually transferred. (This is true on both 32-bit and 64-bit
|
||||
* systems.)
|
||||
*/
|
||||
|
||||
#define RW_MAX_SIZE 0x7ffff000
|
||||
|
||||
int verbose = 0;
|
||||
|
||||
uint64_t getopt_integer(char* optarg)
|
||||
{
|
||||
int rc;
|
||||
uint64_t value;
|
||||
|
||||
rc = sscanf(optarg, "0x%lx", &value);
|
||||
if (rc <= 0)
|
||||
rc = sscanf(optarg, "%lu", &value);
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
ssize_t read_to_buffer(char* fname, int fd, char* buffer, uint64_t size, uint64_t base)
|
||||
{
|
||||
ssize_t rc;
|
||||
uint64_t count = 0;
|
||||
char* buf = buffer;
|
||||
off_t offset = base;
|
||||
|
||||
while (count < size) {
|
||||
uint64_t bytes = size - count;
|
||||
|
||||
if (bytes > RW_MAX_SIZE)
|
||||
bytes = RW_MAX_SIZE;
|
||||
|
||||
if (offset) {
|
||||
rc = lseek(fd, offset, SEEK_SET);
|
||||
if (rc != offset) {
|
||||
fprintf(stderr, "%s, seek off 0x%lx != 0x%lx.\n", fname, rc, offset);
|
||||
perror("seek file");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
/* read data from file into memory buffer */
|
||||
rc = read(fd, buf, bytes);
|
||||
|
||||
if (rc != bytes) {
|
||||
fprintf(stderr, "%s, R off 0x%lx, 0x%lx != 0x%lx.\n", fname, count, rc, bytes);
|
||||
perror("read file");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
count += bytes;
|
||||
buf += bytes;
|
||||
offset += bytes;
|
||||
}
|
||||
|
||||
if (count != size) {
|
||||
fprintf(stderr, "%s, R failed 0x%lx != 0x%lx.\n", fname, count, size);
|
||||
return -EIO;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
ssize_t write_from_buffer(char* fname, int fd, char* buffer, uint64_t size, uint64_t base)
|
||||
{
|
||||
ssize_t rc;
|
||||
uint64_t count = 0;
|
||||
char* buf = buffer;
|
||||
off_t offset = base;
|
||||
|
||||
while (count < size) {
|
||||
uint64_t bytes = size - count;
|
||||
|
||||
if (bytes > RW_MAX_SIZE)
|
||||
bytes = RW_MAX_SIZE;
|
||||
|
||||
if (offset) {
|
||||
rc = lseek(fd, offset, SEEK_SET);
|
||||
if (rc != offset) {
|
||||
fprintf(stderr, "%s, seek off 0x%lx != 0x%lx.\n", fname, rc, offset);
|
||||
perror("seek file");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
/* write data to file from memory buffer */
|
||||
rc = write(fd, buf, bytes);
|
||||
if (rc != bytes) {
|
||||
fprintf(stderr, "%s, W off 0x%lx, 0x%lx != 0x%lx.\n", fname, offset, rc, bytes);
|
||||
perror("write file");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
count += bytes;
|
||||
buf += bytes;
|
||||
offset += bytes;
|
||||
}
|
||||
|
||||
if (count != size) {
|
||||
fprintf(stderr, "%s, R failed 0x%lx != 0x%lx.\n", fname, count, size);
|
||||
return -EIO;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
||||
// [End] #include "dma_utils.c" ===================================
|
||||
|
||||
int test_dma_enc_read(char* EncOut, EncIPConf Confparam)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
||||
void* virt_addr;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t writeval;
|
||||
|
||||
uint32_t Z_val;
|
||||
|
||||
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a;
|
||||
uint16_t z_set;
|
||||
uint32_t ctrl_data;
|
||||
uint32_t CB_num = CB_PROCESS_NUMBER;
|
||||
|
||||
// this values should be given by Shane
|
||||
max_schedule = 0;
|
||||
mb = Confparam.mb;
|
||||
id = CB_num;
|
||||
bg = Confparam.BGSel - 1;
|
||||
z_set = Confparam.z_set - 1;
|
||||
z_j = Confparam.z_j;
|
||||
|
||||
if (z_set == 0)
|
||||
z_a = 2;
|
||||
else if (z_set == 1)
|
||||
z_a = 3;
|
||||
else if (z_set == 2)
|
||||
z_a = 5;
|
||||
else if (z_set == 3)
|
||||
z_a = 7;
|
||||
else if (z_set == 4)
|
||||
z_a = 9;
|
||||
else if (z_set == 5)
|
||||
z_a = 11;
|
||||
else if (z_set == 6)
|
||||
z_a = 13;
|
||||
else
|
||||
z_a = 15;
|
||||
|
||||
if (bg == 0)
|
||||
kb = 22;
|
||||
else if (bg == 1)
|
||||
kb = 10;
|
||||
else if (bg == 2)
|
||||
kb = 9;
|
||||
else if (bg == 3)
|
||||
kb = 8;
|
||||
else
|
||||
kb = 6;
|
||||
mb = Confparam.kb_1 + kb;
|
||||
Z_val = (unsigned int)(z_a << z_j);
|
||||
ctrl_data = (max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (bg << 6) | (z_set << 3) | z_j;
|
||||
uint32_t OutDataNUM = Z_val * mb;
|
||||
uint32_t Out_dwNumItems_p128;
|
||||
uint32_t Out_dwNumItems;
|
||||
|
||||
if ((OutDataNUM & 0x7F) == 0)
|
||||
Out_dwNumItems_p128 = OutDataNUM >> 5;
|
||||
else
|
||||
Out_dwNumItems_p128 = ((OutDataNUM >> 7) + 1) << 2;
|
||||
Out_dwNumItems = ((Out_dwNumItems_p128 << 2) * CB_num);
|
||||
size = Out_dwNumItems;
|
||||
writeval = ctrl_data;
|
||||
|
||||
/* calculate the virtual address to be accessed */
|
||||
virt_addr = map_base + OFFSET_ENC_OUT;
|
||||
|
||||
/* swap 32-bit endianess if host is not little-endian */
|
||||
writeval = htoll(writeval);
|
||||
*((uint32_t*)virt_addr) = writeval;
|
||||
if (fd_enc_read < 0) {
|
||||
fprintf(stderr, "unable to open device %s, %d.\n", dev_enc_read, fd_enc_read);
|
||||
perror("open device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lseek & read data from AXI MM into buffer using SGDMA */
|
||||
rc = read_to_buffer(dev_enc_read, fd_enc_read, EncOut, size, 0);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
int test_dma_enc_write(char* data, EncIPConf Confparam)
|
||||
{
|
||||
ssize_t rc;
|
||||
void* virt_addr;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t writeval;
|
||||
|
||||
uint32_t Z_val;
|
||||
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a;
|
||||
uint16_t z_set;
|
||||
uint32_t ctrl_data;
|
||||
uint32_t CB_num = CB_PROCESS_NUMBER;
|
||||
|
||||
// this values should be given by Shane
|
||||
max_schedule = 0;
|
||||
|
||||
mb = Confparam.mb;
|
||||
id = CB_num;
|
||||
bg = Confparam.BGSel - 1;
|
||||
z_set = Confparam.z_set - 1;
|
||||
z_j = Confparam.z_j;
|
||||
|
||||
if (z_set == 0)
|
||||
z_a = 2;
|
||||
else if (z_set == 1)
|
||||
z_a = 3;
|
||||
else if (z_set == 2)
|
||||
z_a = 5;
|
||||
else if (z_set == 3)
|
||||
z_a = 7;
|
||||
else if (z_set == 4)
|
||||
z_a = 9;
|
||||
else if (z_set == 5)
|
||||
z_a = 11;
|
||||
else if (z_set == 6)
|
||||
z_a = 13;
|
||||
else
|
||||
z_a = 15;
|
||||
|
||||
if (bg == 0)
|
||||
kb = 22;
|
||||
else if (bg == 1)
|
||||
kb = 10;
|
||||
else if (bg == 2)
|
||||
kb = 9;
|
||||
else if (bg == 3)
|
||||
kb = 8;
|
||||
else
|
||||
kb = 6;
|
||||
mb = Confparam.kb_1 + kb;
|
||||
Z_val = (unsigned int)(z_a << z_j);
|
||||
ctrl_data = (max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (bg << 6) | (z_set << 3) | z_j;
|
||||
uint32_t InDataNUM = Z_val * kb;
|
||||
uint32_t In_dwNumItems_p128;
|
||||
uint32_t In_dwNumItems;
|
||||
|
||||
if ((InDataNUM & 0x7F) == 0)
|
||||
In_dwNumItems_p128 = InDataNUM >> 5;
|
||||
else
|
||||
In_dwNumItems_p128 = ((InDataNUM >> 7) + 1) << 2;
|
||||
|
||||
In_dwNumItems = ((In_dwNumItems_p128 << 2) * CB_num);
|
||||
size = In_dwNumItems;
|
||||
writeval = ctrl_data;
|
||||
|
||||
/* calculate the virtual address to be accessed */
|
||||
virt_addr = map_base + OFFSET_ENC_IN;
|
||||
|
||||
/* swap 32-bit endianess if host is not little-endian */
|
||||
writeval = htoll(writeval);
|
||||
*((uint32_t*)virt_addr) = writeval;
|
||||
if (fd_enc_write < 0) {
|
||||
fprintf(stderr, "unable to open device %s, %d.\n", dev_enc_write, fd_enc_write);
|
||||
perror("open device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = write_from_buffer(dev_enc_write, fd_enc_write, data, size, 0);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// int test_dma_dec_read(unsigned int *DecOut, DecIPConf Confparam)
|
||||
int test_dma_dec_read(char* DecOut, DecIPConf Confparam)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
||||
void* virt_addr;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t writeval;
|
||||
|
||||
uint32_t Z_val;
|
||||
|
||||
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a, max_iter, sc_idx;
|
||||
uint16_t z_set;
|
||||
uint32_t ctrl_data;
|
||||
uint32_t CB_num = Confparam.CB_num;
|
||||
|
||||
// this values should be given by Shane
|
||||
max_schedule = 0;
|
||||
mb = Confparam.mb;
|
||||
id = CB_num;
|
||||
bg = Confparam.BGSel - 1;
|
||||
z_set = Confparam.z_set - 1;
|
||||
z_j = Confparam.z_j;
|
||||
max_iter = 8;
|
||||
sc_idx = 12;
|
||||
|
||||
if (z_set == 0)
|
||||
z_a = 2;
|
||||
else if (z_set == 1)
|
||||
z_a = 3;
|
||||
else if (z_set == 2)
|
||||
z_a = 5;
|
||||
else if (z_set == 3)
|
||||
z_a = 7;
|
||||
else if (z_set == 4)
|
||||
z_a = 9;
|
||||
else if (z_set == 5)
|
||||
z_a = 11;
|
||||
else if (z_set == 6)
|
||||
z_a = 13;
|
||||
else
|
||||
z_a = 15;
|
||||
|
||||
if (bg == 0)
|
||||
kb = 22;
|
||||
else if (bg == 1)
|
||||
kb = 10;
|
||||
else if (bg == 2)
|
||||
kb = 9;
|
||||
else if (bg == 3)
|
||||
kb = 8;
|
||||
else
|
||||
kb = 6;
|
||||
|
||||
Z_val = (unsigned int)(z_a << z_j);
|
||||
ctrl_data =
|
||||
(max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (max_iter << 13) | (sc_idx << 9) | (bg << 6) | (z_set) << 3 | z_j;
|
||||
|
||||
uint32_t OutDataNUM = Z_val * kb;
|
||||
uint32_t Out_dwNumItems_p128;
|
||||
uint32_t Out_dwNumItems;
|
||||
|
||||
if (CB_num & 0x01) // odd cb number
|
||||
{
|
||||
if ((OutDataNUM & 0xFF) == 0)
|
||||
Out_dwNumItems_p128 = OutDataNUM;
|
||||
else
|
||||
Out_dwNumItems_p128 = 256 * ((OutDataNUM / 256) + 1);
|
||||
|
||||
Out_dwNumItems = (Out_dwNumItems_p128 * CB_num) >> 3;
|
||||
} else {
|
||||
if ((OutDataNUM & 0x7F) == 0)
|
||||
Out_dwNumItems_p128 = OutDataNUM;
|
||||
else
|
||||
Out_dwNumItems_p128 = 128 * ((OutDataNUM / 128) + 1);
|
||||
|
||||
Out_dwNumItems = (Out_dwNumItems_p128 * CB_num) >> 3;
|
||||
if ((Out_dwNumItems & 0x1f) != 0)
|
||||
Out_dwNumItems = ((Out_dwNumItems + 31) >> 5) << 5;
|
||||
|
||||
}
|
||||
size = Out_dwNumItems;
|
||||
writeval = ctrl_data;
|
||||
|
||||
/* calculate the virtual address to be accessed */
|
||||
virt_addr = map_base + OFFSET_DEC_OUT;
|
||||
|
||||
/* swap 32-bit endianess if host is not little-endian */
|
||||
writeval = htoll(writeval);
|
||||
*((uint32_t*)virt_addr) = writeval;
|
||||
|
||||
if (fd_dec_read < 0) {
|
||||
fprintf(stderr, "unable to open device %s, %d.\n", dev_dec_read, fd_dec_read);
|
||||
perror("open device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
/* lseek & read data from AXI MM into buffer using SGDMA */
|
||||
rc = read_to_buffer(dev_dec_read, fd_dec_read, DecOut, size, 0);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
// int test_dma_dec_write(unsigned int *data, DecIPConf Confparam)
|
||||
int test_dma_dec_write(char* data, DecIPConf Confparam)
|
||||
{
|
||||
ssize_t rc;
|
||||
|
||||
void* virt_addr;
|
||||
|
||||
uint64_t size;
|
||||
uint32_t writeval;
|
||||
|
||||
uint32_t Z_val;
|
||||
uint16_t max_schedule, mb, id, bg, z_j, kb, z_a, max_iter, sc_idx;
|
||||
uint16_t z_set;
|
||||
uint32_t ctrl_data;
|
||||
uint32_t CB_num = Confparam.CB_num; // CB_PROCESS_NUMBER_Dec;//
|
||||
|
||||
// this values should be given by Shane
|
||||
max_schedule = 0;
|
||||
mb = Confparam.mb;
|
||||
id = CB_num;
|
||||
bg = Confparam.BGSel - 1;
|
||||
z_set = Confparam.z_set - 1;
|
||||
z_j = Confparam.z_j;
|
||||
|
||||
max_iter = 8;
|
||||
sc_idx = 12;
|
||||
|
||||
if (z_set == 0)
|
||||
z_a = 2;
|
||||
else if (z_set == 1)
|
||||
z_a = 3;
|
||||
else if (z_set == 2)
|
||||
z_a = 5;
|
||||
else if (z_set == 3)
|
||||
z_a = 7;
|
||||
else if (z_set == 4)
|
||||
z_a = 9;
|
||||
else if (z_set == 5)
|
||||
z_a = 11;
|
||||
else if (z_set == 6)
|
||||
z_a = 13;
|
||||
else
|
||||
z_a = 15;
|
||||
|
||||
if (bg == 0)
|
||||
kb = 22;
|
||||
else if (bg == 1)
|
||||
kb = 10;
|
||||
else if (bg == 2)
|
||||
kb = 9;
|
||||
else if (bg == 3)
|
||||
kb = 8;
|
||||
else
|
||||
kb = 6;
|
||||
|
||||
Z_val = (unsigned int)(z_a << z_j);
|
||||
ctrl_data =
|
||||
(max_schedule << 30) | ((mb - kb) << 24) | (id << 19) | (max_iter << 13) | (sc_idx << 9) | (bg << 6) | (z_set) << 3 | z_j;
|
||||
|
||||
uint32_t InDataNUM = Z_val * mb;
|
||||
uint32_t In_dwNumItems_p128;
|
||||
uint32_t In_dwNumItems;
|
||||
|
||||
InDataNUM = Z_val * mb * 8;
|
||||
if ((InDataNUM & 0x7F) == 0)
|
||||
In_dwNumItems_p128 = InDataNUM;
|
||||
else
|
||||
In_dwNumItems_p128 = 128 * ((InDataNUM / 128) + 1);
|
||||
|
||||
In_dwNumItems = (In_dwNumItems_p128 * CB_num) >> 3;
|
||||
if ((In_dwNumItems & 0x1f) != 0)
|
||||
In_dwNumItems = ((In_dwNumItems + 31) >> 5) << 5;
|
||||
|
||||
size = In_dwNumItems;
|
||||
writeval = ctrl_data;
|
||||
|
||||
/* calculate the virtual address to be accessed */
|
||||
virt_addr = map_base + OFFSET_DEC_IN;
|
||||
|
||||
/* swap 32-bit endianess if host is not little-endian */
|
||||
writeval = htoll(writeval);
|
||||
*((uint32_t*)virt_addr) = writeval;
|
||||
|
||||
if (fd_dec_write < 0) {
|
||||
fprintf(stderr, "unable to open device %s, %d.\n", dev_dec_write, fd_dec_write);
|
||||
perror("open device");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rc = write_from_buffer(dev_dec_write, fd_dec_write, data, size, 0);
|
||||
if (rc < 0)
|
||||
goto out;
|
||||
|
||||
rc = 0;
|
||||
|
||||
out:
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
void test_dma_init(devices_t devices)
|
||||
{
|
||||
/* access width */
|
||||
char* device2 = devices.user_device;
|
||||
|
||||
AssertFatal((fd = open(device2, O_RDWR | O_SYNC)) != -1, "CHARACTER DEVICE %s OPEN FAILURE\n", device2);
|
||||
fflush(stdout);
|
||||
|
||||
/* map one page */
|
||||
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
AssertFatal(map_base != (void*)-1, "MEMORY MAP AT ADDRESS %p FAILED\n", map_base);
|
||||
|
||||
void* virt_addr;
|
||||
virt_addr = map_base + OFFSET_RESET;
|
||||
*((uint32_t*)virt_addr) = 1;
|
||||
|
||||
dev_enc_write = devices.enc_write_device;
|
||||
dev_enc_read = devices.enc_read_device;
|
||||
dev_dec_write = devices.dec_write_device;
|
||||
dev_dec_read = devices.dec_read_device;
|
||||
|
||||
fd_enc_write = open(dev_enc_write, O_RDWR);
|
||||
fd_enc_read = open(dev_enc_read, O_RDWR);
|
||||
fd_dec_write = open(dev_dec_write, O_RDWR);
|
||||
fd_dec_read = open(dev_dec_read, O_RDWR);
|
||||
|
||||
fflush(stdout);
|
||||
|
||||
}
|
||||
|
||||
void dma_reset(devices_t devices)
|
||||
{
|
||||
char* device2 = devices.user_device;
|
||||
|
||||
void* virt_addr;
|
||||
virt_addr = map_base + PCIE_OFF;
|
||||
*((uint32_t*)virt_addr) = 1;
|
||||
|
||||
AssertFatal(munmap(map_base, MAP_SIZE) != -1, "munmap failure");
|
||||
close(fd_enc_write);
|
||||
close(fd_enc_read);
|
||||
close(fd_dec_write);
|
||||
close(fd_dec_read);
|
||||
close(fd);
|
||||
|
||||
AssertFatal((fd = open(device2, O_RDWR | O_SYNC)) != -1, "CHARACTER DEVICE %s OPEN FAILURE\n", device2);
|
||||
fflush(stdout);
|
||||
|
||||
/* map one page */
|
||||
map_base = mmap(0, MAP_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
|
||||
AssertFatal(map_base != (void*)-1, "MEMORY MAP AT ADDRESS %p FAILED\n", map_base);
|
||||
|
||||
virt_addr = map_base + PCIE_OFF;
|
||||
*((uint32_t*)virt_addr) = 1;
|
||||
|
||||
virt_addr = map_base + OFFSET_RESET;
|
||||
*((uint32_t*)virt_addr) = 1;
|
||||
|
||||
dev_enc_write = devices.enc_write_device;
|
||||
dev_enc_read = devices.enc_read_device;
|
||||
dev_dec_write = devices.dec_write_device;
|
||||
dev_dec_read = devices.dec_read_device;
|
||||
|
||||
fd_enc_write = open(dev_enc_write, O_RDWR);
|
||||
fd_enc_read = open(dev_enc_read, O_RDWR);
|
||||
fd_dec_write = open(dev_dec_write, O_RDWR);
|
||||
fd_dec_read = open(dev_dec_read, O_RDWR);
|
||||
|
||||
fflush(stdout);
|
||||
}
|
||||
|
||||
void test_dma_shutdown()
|
||||
{
|
||||
|
||||
void* virt_addr;
|
||||
virt_addr = map_base + PCIE_OFF;
|
||||
*((uint32_t*)virt_addr) = 1;
|
||||
|
||||
AssertFatal(munmap(map_base, MAP_SIZE) != -1, "munmap failure");
|
||||
close(fd_enc_write);
|
||||
close(fd_enc_read);
|
||||
close(fd_dec_write);
|
||||
close(fd_dec_read);
|
||||
close(fd);
|
||||
|
||||
}
|
||||
|
||||
// reg_rx.c
|
||||
int nrLDPC_decoder_FPGA_PYM(uint8_t* buf_in, uint8_t* buf_out, DecIFConf dec_conf)
|
||||
{
|
||||
struct timespec ts_start0; // evaluate time from input setting to output setting including xdma
|
||||
|
||||
int Zc;
|
||||
int nRows;
|
||||
int baseGraph;
|
||||
int CB_num;
|
||||
|
||||
DecIPConf Confparam;
|
||||
int z_a, z_tmp;
|
||||
int z_j = 0;
|
||||
|
||||
|
||||
int input_CBoffset, output_CBoffset;
|
||||
|
||||
uint8_t i_LS;
|
||||
|
||||
devices_t devices = {
|
||||
.user_device = dec_conf.user_device,
|
||||
.enc_write_device = dec_conf.enc_write_device,
|
||||
.enc_read_device = dec_conf.enc_read_device,
|
||||
.dec_write_device = dec_conf.dec_write_device,
|
||||
.dec_read_device = dec_conf.dec_read_device
|
||||
};
|
||||
|
||||
static int init_flag = 0;
|
||||
if (init_flag == 0) {
|
||||
/*Init*/
|
||||
test_dma_init(devices);
|
||||
init_flag = 1;
|
||||
} else {
|
||||
dma_reset(devices);
|
||||
}
|
||||
|
||||
clock_gettime(CLOCK_MONOTONIC, &ts_start0); // time start0
|
||||
// LDPC input parameter
|
||||
Zc = dec_conf.Zc; // shifting size
|
||||
nRows = dec_conf.nRows; // number of Rows
|
||||
baseGraph = dec_conf.BG; // base graph
|
||||
CB_num = dec_conf.numCB; // 31 number of code block
|
||||
|
||||
// calc xdma LDPC parameter
|
||||
// calc i_LS
|
||||
if ((Zc % 15) == 0)
|
||||
i_LS = 7;
|
||||
else if ((Zc % 13) == 0)
|
||||
i_LS = 6;
|
||||
else if ((Zc % 11) == 0)
|
||||
i_LS = 5;
|
||||
else if ((Zc % 9) == 0)
|
||||
i_LS = 4;
|
||||
else if ((Zc % 7) == 0)
|
||||
i_LS = 3;
|
||||
else if ((Zc % 5) == 0)
|
||||
i_LS = 2;
|
||||
else if ((Zc % 3) == 0)
|
||||
i_LS = 1;
|
||||
else
|
||||
i_LS = 0;
|
||||
|
||||
// calc z_a
|
||||
if (i_LS == 0)
|
||||
z_a = 2;
|
||||
else
|
||||
z_a = i_LS * 2 + 1;
|
||||
|
||||
// calc z_j
|
||||
z_tmp = Zc / z_a;
|
||||
while (z_tmp % 2 == 0) {
|
||||
z_j = z_j + 1;
|
||||
z_tmp = z_tmp / 2;
|
||||
}
|
||||
|
||||
// calc CB_num and mb
|
||||
Confparam.CB_num = CB_num;
|
||||
if (baseGraph == 1)
|
||||
Confparam.mb = 22 + nRows;
|
||||
else
|
||||
Confparam.mb = 10 + nRows;
|
||||
|
||||
// set BGSel, z_set, z_j
|
||||
Confparam.BGSel = baseGraph;
|
||||
Confparam.z_set = i_LS + 1;
|
||||
Confparam.z_j = z_j;
|
||||
|
||||
// Calc input CB offset
|
||||
input_CBoffset = Zc * Confparam.mb * 8;
|
||||
if ((input_CBoffset & 0x7F) == 0)
|
||||
input_CBoffset = input_CBoffset / 8;
|
||||
else
|
||||
input_CBoffset = 16 * ((input_CBoffset / 128) + 1);
|
||||
|
||||
// Calc output CB offset
|
||||
output_CBoffset = Zc * (Confparam.mb - nRows);
|
||||
if ((output_CBoffset & 0x7F) == 0)
|
||||
output_CBoffset = output_CBoffset / 8;
|
||||
else
|
||||
output_CBoffset = 16 * ((output_CBoffset / 128) + 1);
|
||||
|
||||
// LDPC accelerator start
|
||||
// write into accelerator
|
||||
if (test_dma_dec_write((char *)buf_in, Confparam) != 0) {
|
||||
exit(1);
|
||||
printf("write exit!!\n");
|
||||
}
|
||||
|
||||
// read output of accelerator
|
||||
if (test_dma_dec_read((char *)buf_out, Confparam) != 0) {
|
||||
exit(1);
|
||||
printf("read exit!!\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -1,46 +0,0 @@
|
||||
/*
|
||||
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
|
||||
*/
|
||||
|
||||
/*!
|
||||
* \briefFPGA accelerator integrated into OAI (for one and multi code block)
|
||||
*/
|
||||
|
||||
#ifndef __NRLDPC_CODING_XDMA_OFFLOAD__H_
|
||||
|
||||
#define __NRLDPC_CODING_XDMA_OFFLOAD__H_
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#define DEVICE_NAME_DEFAULT_USER "/dev/xdma0_user"
|
||||
#define DEVICE_NAME_DEFAULT_ENC_READ "/dev/xdma0_c2h_1"
|
||||
#define DEVICE_NAME_DEFAULT_ENC_WRITE "/dev/xdma0_h2c_1"
|
||||
#define DEVICE_NAME_DEFAULT_DEC_READ "/dev/xdma0_c2h_0"
|
||||
#define DEVICE_NAME_DEFAULT_DEC_WRITE "/dev/xdma0_h2c_0"
|
||||
|
||||
/**
|
||||
\brief LDPC input parameter
|
||||
\param Zc shifting size
|
||||
\param Rows
|
||||
\param baseGraph base graph
|
||||
\param CB_num number of code block
|
||||
\param numChannelLlrs input soft bits length, Zc x 66 - length of filler bits
|
||||
\param numFillerBits filler bits length
|
||||
*/
|
||||
typedef struct {
|
||||
char *user_device, *enc_write_device, *enc_read_device, *dec_write_device, *dec_read_device;
|
||||
unsigned char max_schedule;
|
||||
unsigned char SetIdx;
|
||||
int Zc;
|
||||
unsigned char numCB;
|
||||
unsigned char BG;
|
||||
unsigned char max_iter;
|
||||
int nRows;
|
||||
int numChannelLls;
|
||||
int numFillerBits;
|
||||
} DecIFConf;
|
||||
|
||||
int nrLDPC_decoder_FPGA_PYM(uint8_t *buf_in, uint8_t *buf_out, DecIFConf dec_conf);
|
||||
|
||||
#endif // __NRLDPC_CODING_XDMA_OFFLOAD__H_
|
||||
|
||||
@@ -1,119 +0,0 @@
|
||||
/*
|
||||
* Copyright (c) 2016-present, Xilinx, Inc.
|
||||
* All rights reserved.
|
||||
*
|
||||
* This source code is licensed under the BSD-style license
|
||||
* the terms of the BSD Licence are reported below:
|
||||
*
|
||||
* BSD License
|
||||
*
|
||||
* For Xilinx DMA IP software
|
||||
*
|
||||
* Copyright (c) 2016-present, Xilinx, Inc. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without modification,
|
||||
* are permitted provided that the following conditions are met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright notice, this
|
||||
* list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright notice,
|
||||
* this list of conditions and the following disclaimer in the documentation
|
||||
* and/or other materials provided with the distribution.
|
||||
*
|
||||
* * Neither the name Xilinx nor the names of its contributors may be used to
|
||||
* endorse or promote products derived from this software without specific
|
||||
* prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
|
||||
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
|
||||
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
|
||||
* ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
|
||||
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
|
||||
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
|
||||
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
|
||||
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_TXCTRL_INC_XDMA_DIAG_H_
|
||||
#define MODULES_TXCTRL_INC_XDMA_DIAG_H_
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
unsigned char max_schedule; // max_schedule = 0;
|
||||
unsigned char mb; // mb = 32;
|
||||
unsigned char CB_num; // id = CB_num;
|
||||
unsigned char BGSel; // bg = 1;
|
||||
unsigned char z_set; // z_set = 0;
|
||||
unsigned char z_j; // z_j = 6;
|
||||
unsigned char max_iter; // max_iter = 8;
|
||||
unsigned char SetIdx; // sc_idx = 12;
|
||||
} DecIPConf;
|
||||
|
||||
typedef struct {
|
||||
int SetIdx;
|
||||
int NumCBSegm;
|
||||
int PayloadLen;
|
||||
int Z;
|
||||
int z_set;
|
||||
int z_j;
|
||||
int Kbmax;
|
||||
int BGSel;
|
||||
unsigned mb;
|
||||
unsigned char CB_num;
|
||||
unsigned char kb_1;
|
||||
} EncIPConf;
|
||||
|
||||
typedef struct {
|
||||
char *user_device, *enc_write_device, *enc_read_device, *dec_write_device, *dec_read_device;
|
||||
} devices_t;
|
||||
|
||||
/* ltoh: little to host */
|
||||
/* htol: little to host */
|
||||
#if __BYTE_ORDER == __LITTLE_ENDIAN
|
||||
#define ltohl(x) (x)
|
||||
#define ltohs(x) (x)
|
||||
#define htoll(x) (x)
|
||||
#define htols(x) (x)
|
||||
#elif __BYTE_ORDER == __BIG_ENDIAN
|
||||
#define ltohl(x) __bswap_32(x)
|
||||
#define ltohs(x) __bswap_16(x)
|
||||
#define htoll(x) __bswap_32(x)
|
||||
#define htols(x) __bswap_16(x)
|
||||
#endif
|
||||
|
||||
#define MAP_SIZE (32 * 1024UL)
|
||||
#define MAP_MASK (MAP_SIZE - 1)
|
||||
|
||||
#define SIZE_DEFAULT (32)
|
||||
#define COUNT_DEFAULT (1)
|
||||
|
||||
#define OFFSET_DEC_IN 0x0000
|
||||
#define OFFSET_DEC_OUT 0x0004
|
||||
#define OFFSET_ENC_IN 0x0008
|
||||
#define OFFSET_ENC_OUT 0x000c
|
||||
#define OFFSET_RESET 0x0020
|
||||
#define PCIE_OFF 0x0030
|
||||
|
||||
#define CB_PROCESS_NUMBER 24 // add by JW
|
||||
#define CB_PROCESS_NUMBER_Dec 24
|
||||
|
||||
// dma_from_device.c
|
||||
|
||||
int test_dma_enc_read(char *EncOut, EncIPConf Confparam);
|
||||
int test_dma_enc_write(char *data, EncIPConf Confparam);
|
||||
int test_dma_dec_read(char *DecOut, DecIPConf Confparam);
|
||||
int test_dma_dec_write(char *data, DecIPConf Confparam);
|
||||
void test_dma_init(devices_t devices);
|
||||
void test_dma_shutdown();
|
||||
void dma_reset(devices_t devices);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
@@ -424,11 +424,15 @@ void init_nr_transport(PHY_VARS_gNB *gNB)
|
||||
gNB->max_nb_pusch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1;
|
||||
|
||||
int max_nb_pucch = buffer_ul_slots ? MAX_MOBILES_PER_GNB * buffer_ul_slots : 1;
|
||||
gNB->pucch_queue = spsc_q_alloc(max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t));
|
||||
gNB->pusch_queue = spsc_q_alloc(gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t));
|
||||
bool ret;
|
||||
ret = spsc_q_alloc(&gNB->pucch_queue, max_nb_pucch, sizeof(NR_gNB_PUCCH_job_t));
|
||||
DevAssert(ret);
|
||||
ret = spsc_q_alloc(&gNB->pusch_queue, gNB->max_nb_pusch, sizeof(NR_gNB_PUSCH_job_t));
|
||||
DevAssert(ret);
|
||||
|
||||
int max_nb_srs = buffer_ul_slots ? buffer_ul_slots << 1 : 1; // assuming at most 2 SRS per slot
|
||||
gNB->srs_queue = spsc_q_alloc(max_nb_srs, sizeof(NR_gNB_SRS_job_t));
|
||||
ret = spsc_q_alloc(&gNB->srs_queue, max_nb_srs, sizeof(NR_gNB_SRS_job_t));
|
||||
DevAssert(ret);
|
||||
|
||||
gNB->ulsch = (NR_gNB_ULSCH_t *)malloc16(gNB->max_nb_pusch * sizeof(NR_gNB_ULSCH_t));
|
||||
for (int i = 0; i < gNB->max_nb_pusch; i++) {
|
||||
|
||||
@@ -15,8 +15,11 @@
|
||||
void init_nr_prach(PHY_VARS_gNB *gNB)
|
||||
{
|
||||
int num_prach = 16;
|
||||
gNB->prach_ru_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t));
|
||||
gNB->prach_l1rx_queue = spsc_q_alloc(num_prach, sizeof(prach_item_t));
|
||||
bool ret;
|
||||
ret = spsc_q_alloc(&gNB->prach_ru_queue, num_prach, sizeof(prach_item_t));
|
||||
DevAssert(ret);
|
||||
ret = spsc_q_alloc(&gNB->prach_l1rx_queue, num_prach, sizeof(prach_item_t));
|
||||
DevAssert(ret);
|
||||
}
|
||||
|
||||
void reset_nr_prach(PHY_VARS_gNB *gNB)
|
||||
|
||||
@@ -490,6 +490,8 @@ typedef struct RU_t_s {
|
||||
void (*fh_south_in)(struct RU_t_s *ru, int *frame, int *subframe);
|
||||
/// function pointer to synchronous TX fronthaul function
|
||||
void (*fh_south_out)(struct RU_t_s *ru, int frame_tx, int tti_tx, uint64_t timestamp_tx);
|
||||
/// function pointer to synchronous control fronthaul function
|
||||
void (*fh_south_ctrl)(struct RU_t_s *ru, int frame, int slot, uint64_t timestamp);
|
||||
/// function pointer to synchronous RX fronthaul function (RRU)
|
||||
void (*fh_north_in)(struct RU_t_s *ru, int *frame, int *subframe);
|
||||
/// function pointer to synchronous RX fronthaul function (RRU)
|
||||
|
||||
@@ -151,14 +151,6 @@ void nr_feptx_prec(RU_t *ru, int frame_tx, int slot_tx)
|
||||
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
|
||||
start_meas(&ru->precoding_stats);
|
||||
|
||||
if (gNB->common_vars.analog_bf) {
|
||||
for (int i = 0; i < ru->num_beams_period; i++) {
|
||||
memcpy((void*) &ru->common.beam_id[i][slot_tx * fp->symbols_per_slot],
|
||||
(void*) &gNB->common_vars.beam_id[i][slot_tx * fp->symbols_per_slot],
|
||||
(fp->symbols_per_slot) * sizeof(int));
|
||||
}
|
||||
}
|
||||
|
||||
if (nr_slot_select(cfg,frame_tx,slot_tx) == NR_UPLINK_SLOT)
|
||||
return;
|
||||
|
||||
@@ -197,16 +189,9 @@ void nr_feptx(void *arg)
|
||||
if (tx_idx == 0)
|
||||
start_meas(&ru->precoding_stats);
|
||||
|
||||
if (ru->gNB_list[0]->common_vars.analog_bf) {
|
||||
memcpy(&ru->common.beam_id[bb][slot * fp->symbols_per_slot],
|
||||
&ru->gNB_list[0]->common_vars.beam_id[bb][slot * fp->symbols_per_slot],
|
||||
(fp->symbols_per_slot) * sizeof(int));
|
||||
}
|
||||
|
||||
// If there is no digital beamforming we just need to copy the data to RU
|
||||
if (ru->config.dbt_config.num_dig_beams == 0 || ru->gNB_list[0]->common_vars.analog_bf) {
|
||||
// FFT shift
|
||||
const NR_DL_FRAME_PARMS *fp = &ru->gNB_list[0]->frame_parms;
|
||||
fft_shift(ru->gNB_list[0]->common_vars.txdataF[bb][aa],
|
||||
fp->ofdm_symbol_size,
|
||||
fp->N_RB_DL,
|
||||
|
||||
@@ -25,10 +25,6 @@
|
||||
#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
|
||||
|
||||
@@ -1237,32 +1233,6 @@ int phy_procedures_gNB_uespec_RX(PHY_VARS_gNB *gNB, int frame_rx, int slot_rx, N
|
||||
stop_meas(&gNB->rx_srs_stats);
|
||||
}
|
||||
|
||||
#ifdef E3_AGENT
|
||||
// Spectrum Listening Symbols
|
||||
c16_t **rxdataF_sen = gNB->common_vars.rxdataF[0];
|
||||
if (nr_slot_select(&gNB->gNB_config, frame_rx, slot_rx) == NR_UPLINK_SLOT && slot_rx == 8) {
|
||||
e3_sm_spectrum_control->sampling_counter++;
|
||||
if (e3_sm_spectrum_control->sampling_counter > e3_sm_spectrum_control->sampling_threshold) {
|
||||
const uint16_t n_symbols = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot;
|
||||
// Extracting 12th symbol
|
||||
uint64_t symbol_offset = (n_symbols)*gNB->frame_parms.ofdm_symbol_size + (12) * gNB->frame_parms.ofdm_symbol_size;
|
||||
int32_t *rx_signal = (int32_t *)&rxdataF_sen[0][symbol_offset];
|
||||
|
||||
// for(int i = 0; i < gNB->frame_parms.ofdm_symbol_size; i++){
|
||||
// LOG_D(E3AP, "i = %d, (%d %d) \n",i, ((c16_t*) rx_signal)[i].r, ((c16_t*) rx_signal)[i].i);
|
||||
// }
|
||||
|
||||
T(T_GNB_PHY_UL_FREQ_SENSING_SYMBOL,
|
||||
T_INT(0),
|
||||
T_INT(frame_rx),
|
||||
T_INT(slot_rx),
|
||||
T_INT(0),
|
||||
T_BUFFER(rx_signal, (gNB->frame_parms.ofdm_symbol_size) * sizeof(int32_t)));
|
||||
e3_sm_spectrum_control->sampling_counter = 0;
|
||||
}
|
||||
}
|
||||
#endif // E3_AGENT
|
||||
|
||||
stop_meas(&gNB->phy_proc_rx);
|
||||
|
||||
if (n_pucch > 0 || num_pusch > 0) {
|
||||
|
||||
@@ -5,10 +5,6 @@ add_subdirectory(E1AP)
|
||||
if(E2_AGENT)
|
||||
add_subdirectory(E2AP)
|
||||
endif()
|
||||
if(E3_AGENT)
|
||||
message(STATUS "Add E3 Agent capabilities")
|
||||
add_subdirectory(E3AP)
|
||||
endif()
|
||||
add_subdirectory(F1AP)
|
||||
add_subdirectory(LAYER2)
|
||||
add_subdirectory(M2AP)
|
||||
|
||||
@@ -81,12 +81,6 @@ set(SM_ENCODING_GTP "PLAIN" CACHE STRING "The GTP SM encoding to use")
|
||||
set_property(CACHE SM_ENCODING_GTP PROPERTY STRINGS "PLAIN")
|
||||
message(STATUS "Selected GTP SM_ENCODING: ${SM_ENCODING_GTP}")
|
||||
|
||||
# DAPP Service Model
|
||||
if(E3_AGENT)
|
||||
set(SM_ENCODING_DAPP "ASN" CACHE STRING "The DAPP SM encoding to use")
|
||||
set_property(CACHE SM_ENCODING_DAPP PROPERTY STRINGS "ASN")
|
||||
message(STATUS "Selected DAPP SM_ENCODING: ${SM_ENCODING_DAPP}")
|
||||
endif()
|
||||
|
||||
if(E2AP_VERSION STREQUAL "E2AP_V1")
|
||||
add_library(e2ap_ran_func_obj OBJECT
|
||||
|
||||
@@ -39,14 +39,6 @@ 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
|
||||
@@ -66,13 +58,6 @@ 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}
|
||||
@@ -97,13 +82,6 @@ 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
|
||||
|
||||
@@ -112,4 +90,4 @@ endif()
|
||||
# - 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
|
||||
|
||||
@@ -1,478 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,80 +0,0 @@
|
||||
#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
|
||||
@@ -1,18 +0,0 @@
|
||||
#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
|
||||
@@ -1,241 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
#ifndef RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
|
||||
#define RAN_FUNC_SM_DAPP_SUBSCRIPTION_AGENT_H
|
||||
|
||||
#include "openair2/E2AP/flexric/src/sm/dapp_sm/ie/dapp_data_ie.h"
|
||||
#include "common/utils/ds/seq_arr.h"
|
||||
|
||||
/**
|
||||
* @brief Per-SM subscription bookkeeping for the DAPP service model.
|
||||
*
|
||||
* Tracks which RIC request IDs are registered for each indication format.
|
||||
* Used by the DAPP agent to remove subscriptions when the RIC deletes them.
|
||||
*/
|
||||
typedef struct {
|
||||
seq_arr_t frmt_1_subs; /**< RIC request IDs subscribed to Format 1 (E3 data reports) */
|
||||
seq_arr_t frmt_2_subs; /**< RIC request IDs subscribed to Format 2 (E3 subscription map) */
|
||||
} dapp_subs_data_t;
|
||||
|
||||
/**
|
||||
* @brief Initialize both per-SM subscription lists.
|
||||
*
|
||||
* Must be called once before any insert/remove operations.
|
||||
*
|
||||
* @param dapp_subs_data Pointer to the per-SM subscription state.
|
||||
*/
|
||||
void init_dapp_subs_data(dapp_subs_data_t *dapp_subs_data);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from all per-SM lists and global registries.
|
||||
*
|
||||
* Searches both frmt_1_subs and frmt_2_subs for @p ric_req_id.
|
||||
* For each list where it is found, erases it locally and calls the
|
||||
* corresponding global remove function. Logs a warning if the ID
|
||||
* is not found in either list.
|
||||
*
|
||||
* @param dapp_subs_data Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to remove.
|
||||
*/
|
||||
void remove_dapp_subs_data(dapp_subs_data_t *dapp_subs_data, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Record a RIC request ID in the per-SM Format 1 list.
|
||||
*
|
||||
* @param d Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to insert.
|
||||
*/
|
||||
void insert_frmt_1_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Add a RIC request ID to the global Format 1 registry.
|
||||
*
|
||||
* The global registry is used by generate_e2_indication_from_e3_dapp_report()
|
||||
* to find all subscribers that should receive E3 data report indications.
|
||||
* Duplicates are silently ignored.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to register.
|
||||
*/
|
||||
void ric_subs_frmt1_add(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from the global Format 1 registry.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to unregister.
|
||||
*/
|
||||
void ric_subs_frmt1_remove(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Snapshot all RIC request IDs in the global Format 1 registry.
|
||||
*
|
||||
* Allocates and returns a caller-owned array of RIC request IDs.
|
||||
* The caller must free() the returned array.
|
||||
*
|
||||
* @param[out] out Set to the allocated array, or NULL if empty.
|
||||
* @return Number of IDs in the snapshot (0 if none).
|
||||
*/
|
||||
size_t ric_subs_frmt1_snapshot(uint32_t **out);
|
||||
|
||||
/**
|
||||
* @brief Record a RIC request ID in the per-SM Format 2 list.
|
||||
*
|
||||
* @param d Pointer to the per-SM subscription state.
|
||||
* @param ric_req_id The RIC request ID to insert.
|
||||
*/
|
||||
void insert_frmt_2_ric_id(dapp_subs_data_t *d, uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Add a RIC request ID to the global Format 2 registry.
|
||||
*
|
||||
* The global registry is used by generate_e2_indication_dapp_e3_subscriptions()
|
||||
* to find all subscribers that should receive subscription map indications.
|
||||
* Duplicates are silently ignored.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to register.
|
||||
*/
|
||||
void ric_subs_frmt2_add(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Remove a RIC request ID from the global Format 2 registry.
|
||||
*
|
||||
* @param ric_req_id The RIC request ID to unregister.
|
||||
*/
|
||||
void ric_subs_frmt2_remove(uint32_t ric_req_id);
|
||||
|
||||
/**
|
||||
* @brief Snapshot all RIC request IDs in the global Format 2 registry.
|
||||
*
|
||||
* Allocates and returns a caller-owned array of RIC request IDs.
|
||||
* The caller must free() the returned array.
|
||||
*
|
||||
* @param[out] out Set to the allocated array, or NULL if empty.
|
||||
* @return Number of IDs in the snapshot (0 if none).
|
||||
*/
|
||||
size_t ric_subs_frmt2_snapshot(uint32_t **out);
|
||||
|
||||
#endif
|
||||
@@ -20,10 +20,6 @@
|
||||
#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])
|
||||
{
|
||||
@@ -40,9 +36,6 @@ 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
|
||||
@@ -61,9 +54,6 @@ 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
|
||||
@@ -81,9 +71,6 @@ 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
|
||||
@@ -102,9 +89,6 @@ 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)
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
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()
|
||||
@@ -1,83 +0,0 @@
|
||||
#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();
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
#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
|
||||
@@ -1,267 +0,0 @@
|
||||
#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;
|
||||
}
|
||||
@@ -1,59 +0,0 @@
|
||||
#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
|
||||
@@ -1,18 +0,0 @@
|
||||
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)
|
||||
@@ -1,44 +0,0 @@
|
||||
# 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)
|
||||
@@ -1,65 +0,0 @@
|
||||
-- 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
|
||||
@@ -1,102 +0,0 @@
|
||||
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
|
||||
)
|
||||
@@ -1,26 +0,0 @@
|
||||
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)
|
||||
@@ -1,375 +0,0 @@
|
||||
# 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.
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
#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);
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
#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
|
||||
@@ -1,291 +0,0 @@
|
||||
#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
|
||||
@@ -1,46 +0,0 @@
|
||||
#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
|
||||
@@ -1,607 +0,0 @@
|
||||
#include "spectrum_sm.h"
|
||||
#include "spectrum_enc.h"
|
||||
#include "spectrum_dec.h"
|
||||
|
||||
#include "../../e3_agent.h"
|
||||
|
||||
#include "database.h"
|
||||
#include "event.h"
|
||||
#include "handler.h"
|
||||
#include "utils.h"
|
||||
#include "event_selector.h"
|
||||
#include "configuration.h"
|
||||
#include "logger/logger.h"
|
||||
|
||||
#include "common/utils/LOG/log.h"
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <signal.h>
|
||||
#include <unistd.h>
|
||||
|
||||
// T-tracer constants
|
||||
#define DEFAULT_REMOTE_IP "127.0.0.1"
|
||||
#define DEFAULT_REMOTE_PORT 2021
|
||||
|
||||
// SM default constants
|
||||
#define DEFAULT_SM_SAMPLING_THRESHOLD 5
|
||||
|
||||
e3_sm_spectrum_control_t *e3_sm_spectrum_control = NULL;
|
||||
static spectrum_sm_context_t spectrum_ctx = {0};
|
||||
static uint8_t *spectrum_ran_function_data = NULL;
|
||||
static size_t spectrum_ran_function_data_len = 0;
|
||||
static int spectrum_ran_function_data_ready = 0;
|
||||
|
||||
|
||||
// Spectrum SM RAN function IDs
|
||||
#define SPECTRUM_SM_RAN_FUNCTION_ID 1
|
||||
|
||||
static uint32_t spectrum_telemetry_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
|
||||
static uint32_t spectrum_control_ids[] = {SPECTRUM_SM_RAN_FUNCTION_ID};
|
||||
|
||||
/* this macro looks for a particular element and checks its type */
|
||||
#define GET_DATA_FROM_TRACER(var_name, var_type, var) \
|
||||
if (!strcmp(f.name[i], var_name)) { \
|
||||
if (strcmp(f.type[i], var_type)) { \
|
||||
LOG_E(E3AP, "bad type for %s\n", var_name); \
|
||||
return NULL; \
|
||||
} \
|
||||
var = i; \
|
||||
continue; \
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_init(void *sm_context);
|
||||
static void spectrum_sm_destroy(void *sm_context);
|
||||
static e3_error_t spectrum_sm_start(void *sm_context);
|
||||
static void spectrum_sm_stop(void *sm_context);
|
||||
static int spectrum_sm_is_running(void *sm_context);
|
||||
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
|
||||
void* sm_context,
|
||||
uint32_t request_message_id,
|
||||
uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
uint32_t control_id,
|
||||
const uint8_t* data,
|
||||
size_t data_len);
|
||||
|
||||
static e3_c_service_model_desc_t spectrum_sm_desc = {
|
||||
.name = "spectrum_sm",
|
||||
.version = 1,
|
||||
.ran_function_id = SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
.telemetry_ids = spectrum_telemetry_ids,
|
||||
.telemetry_ids_len = sizeof(spectrum_telemetry_ids) / sizeof(spectrum_telemetry_ids[0]),
|
||||
.control_ids = spectrum_control_ids,
|
||||
.control_ids_len = sizeof(spectrum_control_ids) / sizeof(spectrum_control_ids[0]),
|
||||
.ran_function_data = NULL,
|
||||
.ran_function_data_len = 0,
|
||||
.sm_init = spectrum_sm_init,
|
||||
.sm_destroy = spectrum_sm_destroy,
|
||||
.sm_start = spectrum_sm_start,
|
||||
.sm_stop = spectrum_sm_stop,
|
||||
.sm_is_running = spectrum_sm_is_running,
|
||||
.sm_process_control = spectrum_sm_process_control,
|
||||
.sm_context = &spectrum_ctx,
|
||||
};
|
||||
|
||||
static int is_running_locked(const spectrum_sm_context_t *ctx)
|
||||
{
|
||||
return ctx->running ? 1 : 0;
|
||||
}
|
||||
|
||||
void spectrum_sm_set_handle(e3_service_model_handle_t *sm_handle)
|
||||
{
|
||||
pthread_mutex_lock(&spectrum_ctx.lock);
|
||||
spectrum_ctx.sm_handle = sm_handle;
|
||||
pthread_mutex_unlock(&spectrum_ctx.lock);
|
||||
}
|
||||
|
||||
e3_c_service_model_desc_t *create_spectrum_sm_model(void)
|
||||
{
|
||||
if (!spectrum_ran_function_data_ready) {
|
||||
#ifdef SPECTRUM_SM_ASN1_FORMAT
|
||||
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with ASN.1 encoder\n");
|
||||
#else
|
||||
LOG_I(E3AP, "[SPECTRUM] Encoding RAN function data with JSON encoder\n");
|
||||
#endif
|
||||
int rc = spectrum_encode_ran_function_data(&spectrum_ran_function_data,
|
||||
&spectrum_ran_function_data_len);
|
||||
if (rc != E3_SUCCESS) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to encode RAN function data (%d)\n", rc);
|
||||
spectrum_ran_function_data = NULL;
|
||||
spectrum_ran_function_data_len = 0;
|
||||
}
|
||||
spectrum_ran_function_data_ready = 1;
|
||||
}
|
||||
|
||||
spectrum_sm_desc.ran_function_data = spectrum_ran_function_data;
|
||||
spectrum_sm_desc.ran_function_data_len = spectrum_ran_function_data_len;
|
||||
return &spectrum_sm_desc;
|
||||
}
|
||||
|
||||
static void activate_traces(int socket, int number_of_events, int *is_on)
|
||||
{
|
||||
char t = 1;
|
||||
if (socket_send(socket, &t, 1) == -1 ||
|
||||
socket_send(socket, &number_of_events, sizeof(int)) == -1 ||
|
||||
socket_send(socket, is_on, number_of_events * sizeof(int)) == -1) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to activate T-tracer events\n");
|
||||
return;
|
||||
}
|
||||
|
||||
LOG_I(E3AP, "[SPECTRUM] T-tracer events activated\n");
|
||||
}
|
||||
|
||||
static void *t_tracer_connector_thread_main(void *context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
|
||||
if (!ctx) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int keep_running = ctx->connector_running ? 1 : 0;
|
||||
int has_socket = (ctx->t_tracer_socket >= 0) ? 1 : 0;
|
||||
int prepared = (ctx->database != NULL && ctx->event_flags != NULL) ? 1 : 0;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!keep_running) {
|
||||
break;
|
||||
}
|
||||
|
||||
if (!prepared) {
|
||||
char *database_filename = T_MESSAGES_PATH;
|
||||
LOG_D(E3AP, "[SPECTRUM] Loading T-tracer database\n");
|
||||
void *db = parse_database(database_filename);
|
||||
if (!db) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to parse T-tracer database\n");
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
load_config_file(database_filename);
|
||||
|
||||
int n_events = number_of_ids(db);
|
||||
int *flags = calloc(n_events, sizeof(int));
|
||||
if (!flags) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to allocate event flags array\n");
|
||||
free_database(db);
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (!ctx->database && !ctx->event_flags) {
|
||||
ctx->database = db;
|
||||
ctx->number_of_events = n_events;
|
||||
ctx->event_flags = flags;
|
||||
} else {
|
||||
free(flags);
|
||||
free_database(db);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (has_socket) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int s = try_connect_to((char *)DEFAULT_REMOTE_IP, DEFAULT_REMOTE_PORT);
|
||||
if (s < 0) {
|
||||
sleep(1);
|
||||
continue;
|
||||
}
|
||||
|
||||
int should_activate = 0;
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->t_tracer_socket < 0) {
|
||||
ctx->t_tracer_socket = s;
|
||||
ctx->tracer_ready = true;
|
||||
should_activate = 1;
|
||||
LOG_I(E3AP, "[SPECTRUM] Connected to T-tracer at %s:%d\n",
|
||||
DEFAULT_REMOTE_IP,
|
||||
DEFAULT_REMOTE_PORT);
|
||||
} else {
|
||||
close(s);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (should_activate) {
|
||||
/* Keep old init behavior: activate traces right after successful connect. */
|
||||
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
|
||||
}
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static int wait_for_t_tracer_ready(spectrum_sm_context_t *ctx)
|
||||
{
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
int sock = ctx->t_tracer_socket;
|
||||
int ready = ctx->tracer_ready ? 1 : 0;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!running) {
|
||||
return -1;
|
||||
}
|
||||
if (ready && sock >= 0) {
|
||||
return sock;
|
||||
}
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
static void *spectrum_sm_thread_main(void *context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)context;
|
||||
OBUF ebuf = {osize : 0, omaxsize : 0, obuf : NULL};
|
||||
|
||||
if (!ctx || !ctx->initialized) {
|
||||
LOG_E(E3AP, "[SPECTRUM] SM not initialized, exiting worker\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (signal(SIGPIPE, SIG_IGN) == SIG_ERR) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to set SIGPIPE handler\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
if (wait_for_t_tracer_ready(ctx) < 0) {
|
||||
LOG_E(E3AP, "[SPECTRUM] T-tracer not ready, exiting worker\n");
|
||||
return NULL;
|
||||
}
|
||||
on_off(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL", ctx->event_flags, 1);
|
||||
activate_traces(ctx->t_tracer_socket, ctx->number_of_events, ctx->event_flags);
|
||||
|
||||
ctx->iq_data_event_id = event_id_from_name(ctx->database, "GNB_PHY_UL_FREQ_SENSING_SYMBOL");
|
||||
database_event_format f = get_format(ctx->database, ctx->iq_data_event_id);
|
||||
|
||||
int data_index = -1;
|
||||
for (int i = 0; i < f.count; i++) {
|
||||
GET_DATA_FROM_TRACER("rxdata", "buffer", data_index);
|
||||
}
|
||||
|
||||
if (data_index == -1) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to find rxdata in event format\n");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
LOG_I(E3AP, "[SPECTRUM] Start main extraction loop\n");
|
||||
|
||||
while (1) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
e3_service_model_handle_t *sm_handle = ctx->sm_handle;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!running) {
|
||||
LOG_D(E3AP, "[SPECTRUM] Worker loop stopping (running=0)\n");
|
||||
break;
|
||||
}
|
||||
|
||||
if (ctx->t_tracer_socket < 0 && wait_for_t_tracer_ready(ctx) < 0) {
|
||||
LOG_E(E3AP, "[SPECTRUM] T-tracer became unavailable, stopping worker loop\n");
|
||||
break;
|
||||
}
|
||||
|
||||
event e = get_event(ctx->t_tracer_socket, &ebuf, ctx->database);
|
||||
if (e.type == -1) {
|
||||
LOG_W(E3AP, "[SPECTRUM] T-tracer connection lost\n");
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->t_tracer_socket >= 0) {
|
||||
close(ctx->t_tracer_socket);
|
||||
ctx->t_tracer_socket = -1;
|
||||
ctx->tracer_ready = false;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (e.type != ctx->iq_data_event_id) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!sm_handle) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Missing SM handle, cannot emit indication\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
LOG_D(E3AP,
|
||||
"[SPECTRUM] IQ event received (sampling handled in PHY producer, threshold=%u)\n",
|
||||
e3_sm_spectrum_control->sampling_threshold);
|
||||
|
||||
uint8_t *encoded_data = NULL;
|
||||
size_t encoded_size = 0;
|
||||
LOG_D(E3AP, "[SPECTRUM] Encoding indication from rxdata buffer (%d bytes)\n",
|
||||
e.e[data_index].bsize);
|
||||
int rc = spectrum_encode_indication(e.e[data_index].b,
|
||||
e.e[data_index].bsize,
|
||||
0,
|
||||
&encoded_data,
|
||||
&encoded_size);
|
||||
if (rc != E3_SUCCESS || !encoded_data) {
|
||||
LOG_E(E3AP,
|
||||
"[SPECTRUM] Failed to encode indication (rc=%d, rxdata_size=%d)\n",
|
||||
rc,
|
||||
e.e[data_index].bsize);
|
||||
continue;
|
||||
}
|
||||
LOG_D(E3AP, "[SPECTRUM] Encoded indication payload (%zu bytes)\n", encoded_size);
|
||||
|
||||
size_t dapp_len = 0;
|
||||
uint32_t *dapp_ids = e3_agent_get_ran_function_subscribers(e3.agent,
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
&dapp_len);
|
||||
LOG_D(E3AP, "[SPECTRUM] Found %zu subscribers for ran_function_id=%u\n",
|
||||
dapp_len,
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID);
|
||||
|
||||
if (dapp_ids) {
|
||||
if (dapp_len > 0) {
|
||||
for (size_t i = 0; i < dapp_len; ++i) {
|
||||
e3_error_t emit_rc =
|
||||
e3_service_model_emit_indication(sm_handle, dapp_ids[i], SPECTRUM_SM_RAN_FUNCTION_ID, encoded_data, encoded_size);
|
||||
if (emit_rc != E3_SUCCESS) {
|
||||
LOG_E(E3AP, "[SPECTRUM] Failed to emit indication for dApp %u (err=%d)\n", dapp_ids[i], emit_rc);
|
||||
} else {
|
||||
LOG_D(E3AP, "[SPECTRUM] Emitted indication to dApp %u (%zu bytes)\n", dapp_ids[i], encoded_size);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
size_t total_subscriptions = e3_agent_subscription_count(e3.agent);
|
||||
size_t registered_dapp_len = 0;
|
||||
uint32_t *registered_dapps = e3_agent_get_registered_dapps(e3.agent, ®istered_dapp_len);
|
||||
LOG_E(E3AP,
|
||||
"[SPECTRUM] No subscribers for ran_function_id=%u "
|
||||
"(total_subscriptions=%zu, registered_dapps=%zu)\n",
|
||||
SPECTRUM_SM_RAN_FUNCTION_ID,
|
||||
total_subscriptions,
|
||||
registered_dapp_len);
|
||||
if (registered_dapps) {
|
||||
e3_agent_free_uint32_array(registered_dapps);
|
||||
}
|
||||
}
|
||||
}
|
||||
e3_agent_free_uint32_array(dapp_ids);
|
||||
|
||||
free(encoded_data);
|
||||
}
|
||||
|
||||
if (ebuf.obuf) {
|
||||
free(ebuf.obuf);
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_init(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
/* Preserve handle set before SM registration; memset below would clear it. */
|
||||
e3_service_model_handle_t *preset_sm_handle = ctx->sm_handle;
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->t_tracer_socket = -1;
|
||||
ctx->sm_handle = preset_sm_handle;
|
||||
pthread_mutex_init(&ctx->lock, NULL);
|
||||
ctx->initialized = true;
|
||||
LOG_D(E3AP, "[SPECTRUM] SM init complete (preset_sm_handle=%p)\n", (void *)ctx->sm_handle);
|
||||
|
||||
ctx->connector_running = true;
|
||||
if (pthread_create(&ctx->connector_thread, NULL, t_tracer_connector_thread_main, ctx) != 0) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_THREAD_FAILED;
|
||||
}
|
||||
ctx->connector_started = true;
|
||||
|
||||
e3_sm_spectrum_control = calloc(1, sizeof(*e3_sm_spectrum_control));
|
||||
if (!e3_sm_spectrum_control) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
e3_sm_spectrum_control->action_list = calloc(MAX_BWP_SIZE, sizeof(uint16_t));
|
||||
if (!e3_sm_spectrum_control->action_list) {
|
||||
spectrum_sm_destroy(ctx);
|
||||
return E3_SM_ERROR_MEMORY;
|
||||
}
|
||||
|
||||
pthread_mutex_init(&e3_sm_spectrum_control->mutex, NULL);
|
||||
e3_sm_spectrum_control->sampling_threshold = DEFAULT_SM_SAMPLING_THRESHOLD;
|
||||
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_start(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx || !ctx->initialized) {
|
||||
return E3_NOT_INITIALIZED;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
if (ctx->running) {
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
ctx->running = true;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (pthread_create(&ctx->worker_thread, NULL, spectrum_sm_thread_main, ctx) != 0) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return E3_SM_ERROR_THREAD_FAILED;
|
||||
}
|
||||
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
|
||||
static void spectrum_sm_stop(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int was_running = ctx->running;
|
||||
ctx->running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (was_running) {
|
||||
pthread_cancel(ctx->worker_thread);
|
||||
pthread_join(ctx->worker_thread, NULL);
|
||||
}
|
||||
|
||||
if (ctx->connector_started) {
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->connector_running = false;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
pthread_join(ctx->connector_thread, NULL);
|
||||
ctx->connector_started = false;
|
||||
}
|
||||
}
|
||||
|
||||
static int spectrum_sm_is_running(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return 0;
|
||||
}
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
int running = is_running_locked(ctx);
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
return running;
|
||||
}
|
||||
|
||||
static void spectrum_sm_destroy(void *sm_context)
|
||||
{
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx) {
|
||||
return;
|
||||
}
|
||||
|
||||
spectrum_sm_stop(sm_context);
|
||||
|
||||
if (ctx->event_flags) {
|
||||
free(ctx->event_flags);
|
||||
ctx->event_flags = NULL;
|
||||
}
|
||||
|
||||
if (ctx->database) {
|
||||
free_database(ctx->database);
|
||||
ctx->database = NULL;
|
||||
}
|
||||
|
||||
if (ctx->t_tracer_socket >= 0) {
|
||||
close(ctx->t_tracer_socket);
|
||||
ctx->t_tracer_socket = -1;
|
||||
}
|
||||
|
||||
if (e3_sm_spectrum_control) {
|
||||
if (e3_sm_spectrum_control->action_list) {
|
||||
free(e3_sm_spectrum_control->action_list);
|
||||
e3_sm_spectrum_control->action_list = NULL;
|
||||
}
|
||||
pthread_mutex_destroy(&e3_sm_spectrum_control->mutex);
|
||||
free(e3_sm_spectrum_control);
|
||||
e3_sm_spectrum_control = NULL;
|
||||
}
|
||||
|
||||
if (ctx->initialized) {
|
||||
pthread_mutex_destroy(&ctx->lock);
|
||||
}
|
||||
|
||||
if (spectrum_ran_function_data) {
|
||||
free(spectrum_ran_function_data);
|
||||
spectrum_ran_function_data = NULL;
|
||||
spectrum_ran_function_data_len = 0;
|
||||
spectrum_ran_function_data_ready = 0;
|
||||
spectrum_sm_desc.ran_function_data = NULL;
|
||||
spectrum_sm_desc.ran_function_data_len = 0;
|
||||
}
|
||||
|
||||
memset(ctx, 0, sizeof(*ctx));
|
||||
ctx->t_tracer_socket = -1;
|
||||
}
|
||||
|
||||
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
|
||||
void* sm_context,
|
||||
uint32_t request_message_id,
|
||||
uint32_t dapp_id,
|
||||
uint32_t ran_function_id,
|
||||
uint32_t control_id,
|
||||
const uint8_t* data,
|
||||
size_t data_len)
|
||||
{
|
||||
(void)dapp_id;
|
||||
int response_code = 0; // Positive
|
||||
|
||||
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
|
||||
if (!ctx || !sm_handle || ran_function_id != SPECTRUM_SM_RAN_FUNCTION_ID ||
|
||||
control_id != SPECTRUM_SM_RAN_FUNCTION_ID) {
|
||||
if (sm_handle) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
}
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&ctx->lock);
|
||||
ctx->sm_handle = sm_handle;
|
||||
pthread_mutex_unlock(&ctx->lock);
|
||||
|
||||
if (!data || data_len == 0 || !e3_sm_spectrum_control) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
spectrum_prb_control_t* control_payload =
|
||||
spectrum_decode_prb_control((uint8_t *)data, data_len);
|
||||
if (!control_payload) {
|
||||
response_code = 1; // Negative
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SM_ERROR_INVALID_PARAM;
|
||||
}
|
||||
|
||||
const size_t max_elems = MAX_BWP_SIZE;
|
||||
size_t elems = control_payload->prb_count;
|
||||
if (elems > max_elems) {
|
||||
elems = max_elems;
|
||||
}
|
||||
|
||||
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
uint16_t *alist16 = e3_sm_spectrum_control->action_list;
|
||||
memcpy(alist16, control_payload->blacklisted_prbs, elems * sizeof(uint16_t));
|
||||
e3_sm_spectrum_control->action_size = (int)elems;
|
||||
|
||||
memset(e3_sm_spectrum_control->dyn_prbbl, 0, max_elems * sizeof(uint16_t));
|
||||
for (size_t j = 0; j < elems; ++j) {
|
||||
uint16_t prb = alist16[j];
|
||||
if (prb < MAX_BWP_SIZE) {
|
||||
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
|
||||
}
|
||||
}
|
||||
|
||||
memset(alist16, 0, max_elems * sizeof(uint16_t));
|
||||
if (control_payload->sampling_threshold > 0) {
|
||||
e3_sm_spectrum_control->sampling_threshold = control_payload->sampling_threshold;
|
||||
}
|
||||
|
||||
e3_sm_spectrum_control->ready = 1;
|
||||
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
spectrum_free_decoded_control(control_payload);
|
||||
|
||||
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
|
||||
return E3_SUCCESS;
|
||||
}
|
||||
@@ -1,52 +0,0 @@
|
||||
#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
|
||||
@@ -462,11 +462,10 @@ static void configure_prach_occasions(NR_UE_MAC_INST_t *mac, int scs)
|
||||
for (int s = 0; s < prach_info.N_RA_sfn; s++) { // subframe/60kHz slot occasions in period
|
||||
while (((temp_s_map >> sf) & 0x01) == 0)
|
||||
sf++;
|
||||
int sl = sf;
|
||||
int sl = scs == 1 || scs == 3 ? sf * 2 : sf;
|
||||
for (int i = 0; i < prach_slots_in_sf; i++) { // slot per subframe/60kHz slot
|
||||
int add_slot = i;
|
||||
if (scs == 1 || scs == 3) {
|
||||
sl *= 2;
|
||||
// if only 1 slot per subframe (or 60kHz slot) in case of 30 or 120kHz it's the odd one
|
||||
// as defined in 5.3.2 of 211
|
||||
if (((prach_info.format & 0xff) > 3) && prach_slots_in_sf == 1)
|
||||
@@ -528,6 +527,14 @@ static void configure_prach_occasions(NR_UE_MAC_INST_t *mac, int scs)
|
||||
LOG_D(NR_MAC, "PRACH configuration period %d association period %d\n", config_period, ra->association_periods);
|
||||
|
||||
select_prach_occasion(ra, mac->ssb_list.nb_tx_ssb, max_num_occasions, ra_occasions_period, num_ra_occasions_period);
|
||||
const prach_occasion_info_t *pi = &ra->sched_ro_info;
|
||||
LOG_I(NR_MAC,
|
||||
"[UE %d] selected PRACH occasion: start_symbol %d fdm %d slot %d format %d\n",
|
||||
mac->ue_id,
|
||||
pi->start_symbol,
|
||||
pi->fdm,
|
||||
pi->slot,
|
||||
pi->format);
|
||||
}
|
||||
|
||||
/* TS 38.321 subclause 7.3 - return DELTA_PREAMBLE values in dB */
|
||||
|
||||
@@ -144,11 +144,6 @@ 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);
|
||||
}
|
||||
@@ -171,9 +166,6 @@ void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame, slot_t slo
|
||||
"periodical UE stats deactivated after reaching %d UEs, please check nrMAC_stats.log, or increase MACRLCs.[0].stats_max_ue\n",
|
||||
gNB->stats_max_ue);
|
||||
}
|
||||
#ifdef E3_AGENT
|
||||
nr_update_prb_policy(module_idP, frame, slot);
|
||||
#endif // E3_AGENT
|
||||
}
|
||||
|
||||
nr_measgap_scheduling(gNB, frame, slot);
|
||||
|
||||
@@ -20,10 +20,6 @@
|
||||
#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) */////
|
||||
////////////////////////////////////////////////////////
|
||||
@@ -53,30 +49,6 @@ int get_dl_tda(const gNB_MAC_INST *nrmac, int slot)
|
||||
return 0; // if FDD or not mixed slot in TDD, for now use default TDA
|
||||
}
|
||||
|
||||
#ifdef E3_AGENT
|
||||
void nr_update_prb_policy(module_id_t module_idP, frame_t frame, sub_frame_t slot)
|
||||
{
|
||||
gNB_MAC_INST *gNB = RC.nrmac[module_idP];
|
||||
|
||||
NR_SCHED_ENSURE_LOCKED(&gNB->sched_lock);
|
||||
LOG_W(NR_MAC, "Barred_PRBs ");
|
||||
|
||||
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
|
||||
if (e3_sm_spectrum_control->ready) {
|
||||
memcpy(gNB->ulprbbl, e3_sm_spectrum_control->dyn_prbbl, MAX_BWP_SIZE * sizeof(uint16_t));
|
||||
e3_sm_spectrum_control->ready = 0; // Reset ready flag for next production
|
||||
}
|
||||
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
|
||||
|
||||
for (int i = 0; i < MAX_BWP_SIZE; i++) {
|
||||
if (gNB->ulprbbl[i] == 0x3FFF)
|
||||
printf("%d ", i);
|
||||
}
|
||||
|
||||
printf("\n");
|
||||
}
|
||||
#endif // E3_AGENT
|
||||
|
||||
// Compute and write all MAC CEs and subheaders, and return number of written bytes
|
||||
int nr_write_ce_dlsch_pdu(module_id_t module_idP,
|
||||
const NR_UE_sched_ctrl_t *ue_sched_ctl,
|
||||
|
||||
@@ -110,15 +110,6 @@ const NR_tda_info_t *get_best_ul_tda(const gNB_MAC_INST *nrmac, int beam, const
|
||||
int start = check_rb_start;
|
||||
int len = check_rb_len;
|
||||
uint16_t tda_mask = SL_to_bitmap(tdas->startSymbolIndex, tdas->nrOfSymbols);
|
||||
|
||||
#ifdef E3_AGENT
|
||||
// Skip TDAs that use symbol 12 in slot 8 (reserved for spectrum sensing)
|
||||
if (slot == 8 && (tda_mask & (1 << 12))) {
|
||||
LOG_D(NR_MAC, "%4d.%2d skipping TDA (mask 0x%04x) overlapping spectrum sensing symbol\n", frame, slot, tda_mask);
|
||||
continue;
|
||||
}
|
||||
#endif
|
||||
|
||||
get_max_rb_range(vrb_map_UL, nrmac->ulprbbl, tda_mask, &start, &len);
|
||||
uint64_t s = (uint64_t)tdas->nrOfSymbols * len;
|
||||
if (s > score) {
|
||||
|
||||
@@ -49,10 +49,6 @@ void clear_nr_nfapi_information(gNB_MAC_INST *gNB, int CC_idP, frame_t frameP, s
|
||||
|
||||
void nr_mac_update_timers(module_id_t module_id);
|
||||
|
||||
#ifdef E3_AGENT
|
||||
void nr_update_prb_policy(module_id_t module_id, frame_t frame, sub_frame_t slot);
|
||||
#endif // E3_AGENT
|
||||
|
||||
void gNB_dlsch_ulsch_scheduler(module_id_t module_idP, frame_t frame_rxP, slot_t slot_rxP, NR_Sched_Rsp_t *sched_info);
|
||||
|
||||
/* \brief main DL scheduler function. Calls a preprocessor to decide on
|
||||
|
||||
@@ -1099,13 +1099,7 @@ 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
|
||||
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) {
|
||||
if (do_SRS) {
|
||||
tda = set_TimeDomainResourceAllocation(k2, get_SLIV(0, 12));
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
}
|
||||
@@ -1159,7 +1153,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 (need_no_sym12_tda) {
|
||||
if (do_SRS) {
|
||||
tda = set_TimeDomainResourceAllocation(i, get_SLIV(0, 12));
|
||||
asn1cSeqAdd(&tda_list->list, tda);
|
||||
}
|
||||
|
||||
@@ -272,6 +272,12 @@ void nr_rrc_trigger_f1_ho(gNB_RRC_INST *rrc,
|
||||
DevAssert(rrc != NULL);
|
||||
DevAssert(ue != NULL);
|
||||
|
||||
/* 3GPP TS 38.473, clause 8.3.1.2: The gNB-CU shall only initiate UeContextSetupRequest
|
||||
* for handover when at least one DRB is setup for the UE. */
|
||||
if (!seq_arr_size(&ue->drbs)) {
|
||||
LOG_W(NR_RRC, "UE %u: no DRB configured, cannot trigger handover\n", ue->rrc_ue_id);
|
||||
return;
|
||||
}
|
||||
uint8_t buf[NR_RRC_BUF_SIZE];
|
||||
int size = do_NR_HandoverPreparationInformation(ue->ue_cap_buffer.buf, ue->ue_cap_buffer.len, buf, sizeof buf);
|
||||
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
/*!
|
||||
* \brief common APIs for different RF frontend device
|
||||
*/
|
||||
#include <pthread.h>
|
||||
#include <stdio.h>
|
||||
#include <strings.h>
|
||||
#include <dlfcn.h>
|
||||
@@ -126,6 +127,8 @@ int openair0_device_load(openair0_device_t *device, openair0_config_t *openair0_
|
||||
} else
|
||||
AssertFatal(false, "can't open the radio device: %s\n", get_devname(device->type));
|
||||
|
||||
pthread_mutex_init(&device->reOrder.mutex_store, NULL);
|
||||
pthread_mutex_init(&device->reOrder.mutex_write, NULL);
|
||||
return rc;
|
||||
}
|
||||
|
||||
@@ -223,15 +226,15 @@ int openair0_write_reorder_common(nrue_ru_write_t nrue_ru_write,
|
||||
int wroteSamples = 0;
|
||||
re_order_t *ctx = &device->reOrder;
|
||||
LOG_D(HW, "received write order ts: %lu, nb samples %d, next ts %luflags %d\n", timestamp, nsamps, timestamp + nsamps, flags);
|
||||
pthread_mutex_lock(&ctx->mutex_store);
|
||||
if (!ctx->initDone) {
|
||||
ctx->nextTS = timestamp;
|
||||
pthread_mutex_init(&ctx->mutex_write, NULL);
|
||||
pthread_mutex_init(&ctx->mutex_store, NULL);
|
||||
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
|
||||
ctx->queue[i].txp = malloc(sizeof(void *) * NB_ANTENNAS_TX);
|
||||
}
|
||||
ctx->initDone = true;
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex_store);
|
||||
if (pthread_mutex_trylock(&ctx->mutex_write) == 0) {
|
||||
// We have the write exclusivity
|
||||
if (llabs(timestamp - ctx->nextTS) < MAX_GAP) { // We are writing in sequence of the previous write
|
||||
@@ -274,13 +277,13 @@ void openair0_write_reorder_clear_context(openair0_device_t *device)
|
||||
return;
|
||||
if (pthread_mutex_trylock(&ctx->mutex_write) != 0)
|
||||
LOG_E(HW, "write_reorder_clear_context call while still writing on the device\n");
|
||||
pthread_mutex_destroy(&ctx->mutex_write);
|
||||
else
|
||||
pthread_mutex_unlock(&ctx->mutex_write);
|
||||
pthread_mutex_lock(&ctx->mutex_store);
|
||||
for (int i = 0; i < WRITE_QUEUE_SZ; i++) {
|
||||
ctx->queue[i].active = false;
|
||||
free(ctx->queue[i].txp);
|
||||
}
|
||||
pthread_mutex_unlock(&ctx->mutex_store);
|
||||
pthread_mutex_destroy(&ctx->mutex_store);
|
||||
ctx->initDone = false;
|
||||
pthread_mutex_unlock(&ctx->mutex_store);
|
||||
}
|
||||
|
||||
@@ -80,6 +80,7 @@ typedef struct {
|
||||
int64_t rx_count;
|
||||
int wait_for_first_pps;
|
||||
int use_gps;
|
||||
int prev_beam;
|
||||
//int first_tx;
|
||||
//int first_rx;
|
||||
//! timestamp of RX packet
|
||||
@@ -313,6 +314,7 @@ static int trx_usrp_start(openair0_device_t *device)
|
||||
//s->first_tx = 1;
|
||||
//s->first_rx = 1;
|
||||
s->rx_timestamp = 0;
|
||||
s->prev_beam = -1;
|
||||
|
||||
//wait for next pps
|
||||
uhd::time_spec_t last_pps = s->usrp->get_time_last_pps();
|
||||
@@ -359,6 +361,43 @@ static void trx_usrp_finish_rx(usrp_state_t *s)
|
||||
} while (samples > 0);
|
||||
}
|
||||
|
||||
static int trx_set_beam(openair0_device_t *device, int *beams, int num_beams, openair0_timestamp_t timestamp)
|
||||
{
|
||||
AssertFatal(beams, "Invalid input for beams %p, vector not present\n", beams);
|
||||
AssertFatal(num_beams == 1, "Cannot handle more than 1 concurrent beam in USRP\n");
|
||||
usrp_state_t *s = (usrp_state_t *)device->priv;
|
||||
// to keep consistent with previous integration where we sent beam information to GPIO
|
||||
// only if it changed to avoid unnecessary timing issues that the procedure may cause
|
||||
if (beams[0] == s->prev_beam)
|
||||
return 0;
|
||||
else
|
||||
s->prev_beam = beams[0];
|
||||
int gpio = 0;
|
||||
switch (device->openair0_cfg->gpio_controller) {
|
||||
case RU_GPIO_CONTROL_GENERIC:
|
||||
AssertFatal(beams[0] < 8, "Only 3 bits available for setting beams\n");
|
||||
gpio = s->prev_beam | TX_GPIO_CHANGE;
|
||||
break;
|
||||
case RU_GPIO_CONTROL_INTERDIGITAL:
|
||||
// TODO
|
||||
break;
|
||||
default:
|
||||
AssertFatal(false, "illegal GPIO controller for beam handling %d\n", device->openair0_cfg->gpio_controller);
|
||||
}
|
||||
radio_tx_gpio_flag_t flags_gpio = (radio_tx_gpio_flag_t) gpio;
|
||||
// bit 13 enables gpio
|
||||
timestamp -= device->openair0_cfg->command_line_sample_advance + device->openair0_cfg->tx_sample_advance;
|
||||
s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 1);
|
||||
// push GPIO bits
|
||||
s->usrp->set_command_time(s->tx_md.time_spec);
|
||||
s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK);
|
||||
s->usrp->clear_command_time();
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void trx_usrp_write_reset(openair0_thread_t *wt);
|
||||
|
||||
/*! \brief Terminate operation of the USRP transceiver -- free all associated resources
|
||||
@@ -418,7 +457,6 @@ static int trx_usrp_write(openair0_device_t *device,
|
||||
int nsamps2; // aligned to upper 32 or 16 byte boundary
|
||||
|
||||
radio_tx_burst_flag_t flags_burst = (radio_tx_burst_flag_t) (flags & 0xf);
|
||||
radio_tx_gpio_flag_t flags_gpio = (radio_tx_gpio_flag_t) ((flags >> 4) & 0x1fff);
|
||||
|
||||
int end;
|
||||
openair0_thread_t *write_thread = &device->write_thread;
|
||||
@@ -480,16 +518,6 @@ static int trx_usrp_write(openair0_device_t *device,
|
||||
s->tx_md.time_spec = uhd::time_spec_t::from_ticks(timestamp, s->sample_rate);
|
||||
s->tx_count++;
|
||||
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 1);
|
||||
// bit 13 enables gpio
|
||||
if ((flags_gpio & TX_GPIO_CHANGE) != 0) {
|
||||
// push GPIO bits
|
||||
s->usrp->set_command_time(s->tx_md.time_spec);
|
||||
s->usrp->set_gpio_attr(s->gpio_bank, "OUT", flags_gpio, MAN_MASK);
|
||||
s->usrp->clear_command_time();
|
||||
}
|
||||
VCD_SIGNAL_DUMPER_DUMP_FUNCTION_BY_NAME(VCD_SIGNAL_DUMPER_FUNCTIONS_BEAM_SWITCHING_GPIO, 0);
|
||||
|
||||
if (cc > 1) {
|
||||
std::vector<void *> buff_ptrs;
|
||||
|
||||
@@ -524,7 +552,7 @@ static int trx_usrp_write(openair0_device_t *device,
|
||||
write_package[end].cc = cc;
|
||||
write_package[end].first_packet = first_packet_state;
|
||||
write_package[end].last_packet = last_packet_state;
|
||||
write_package[end].flags_gpio = flags_gpio;
|
||||
//write_package[end].flags_gpio = flags_gpio;
|
||||
for (int i = 0; i < cc; i++)
|
||||
write_package[end].buff[i] = buff[i];
|
||||
write_thread->count_write++;
|
||||
@@ -1519,6 +1547,7 @@ extern "C" {
|
||||
LOG_I(HW,"Device timestamp: %f...\n", s->usrp->get_time_now().get_real_secs());
|
||||
device->trx_write_func = trx_usrp_write;
|
||||
device->trx_read_func = trx_usrp_read;
|
||||
device->trx_set_beams2 = trx_set_beam;
|
||||
s->sample_rate = openair0_cfg[0].sample_rate;
|
||||
|
||||
// TODO:
|
||||
|
||||
@@ -1363,7 +1363,7 @@ static void rfsimulator_read_internal(rfsimulator_state_t *t,
|
||||
int16_t noise_power = (int16_t)(32767.0 / powf(10.0, .05 * -get_noise_power_dBFS()));
|
||||
for (int a = 0; a < nbAnt; a++) {
|
||||
for (int i = 0; i < nsamps; i++) {
|
||||
temp_array[a][i].r += noise_power + gaussZiggurat(0.0, 1.0);
|
||||
temp_array[a][i].r += noise_power * gaussZiggurat(0.0, 1.0);
|
||||
temp_array[a][i].i += noise_power * gaussZiggurat(0.0, 1.0);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -179,12 +179,13 @@ void cirdb_connect(int id,
|
||||
float want_speed = (sel && sel->want_speed_mps > 0) ? sel->want_speed_mps : -1.0f;
|
||||
|
||||
LOG_I(HW,
|
||||
"CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s\n",
|
||||
"CIRDB: Searching for entry with model_id=%d, antennas=%dx%d, DS=%.1fns, speed=%.1fm/s, AoA=%.1fdeg\n",
|
||||
want_model_id,
|
||||
G.num_tx,
|
||||
G.num_rx,
|
||||
want_ds,
|
||||
want_speed);
|
||||
want_speed,
|
||||
sel->want_aoa_deg);
|
||||
|
||||
cirdb_entry_meta_t m = (cirdb_entry_meta_t){0};
|
||||
cirdb_select_req_t req = {.want_model_id = want_model_id,
|
||||
@@ -192,6 +193,7 @@ void cirdb_connect(int id,
|
||||
.want_rx = G.num_rx,
|
||||
.want_ds_ns = want_ds,
|
||||
.want_speed_mps = want_speed,
|
||||
.want_aoa_deg = sel->want_aoa_deg,
|
||||
.allow_shape_swap = 0,
|
||||
.w_ds = 1.0f,
|
||||
.w_speed = 0.2f,
|
||||
@@ -279,7 +281,7 @@ void cirdb_connect(int id,
|
||||
}
|
||||
|
||||
LOG_I(HW,
|
||||
"CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s\n",
|
||||
"CIRDB: Selected entry - model=%d DS=%.3fns shape=%ux%u L=%u/%u S=%u fs=%.0f dt=%.6fs speed=%.3fm/s aoa=%.1fdeg\n",
|
||||
G.model_id,
|
||||
G.ds_ns,
|
||||
G.n_tx,
|
||||
@@ -289,7 +291,8 @@ void cirdb_connect(int id,
|
||||
G.S,
|
||||
G.fs_hz,
|
||||
G.snapshot_dt_s,
|
||||
G.speed_mps);
|
||||
G.speed_mps,
|
||||
sel->want_aoa_deg);
|
||||
}
|
||||
|
||||
void cirdb_update(uint64_t ns_since_start)
|
||||
|
||||
@@ -25,6 +25,7 @@ typedef struct {
|
||||
int want_model_id; /* 0..4, or <0 for no preference */
|
||||
float want_ds_ns; /* nonnegative, or <0 for no preference */
|
||||
float want_speed_mps; /* nonnegative, or <0 for no preference */
|
||||
float want_aoa_deg; /* degrees; TDL-D/E only; 0.0 = broadside default */
|
||||
} cirdb_select_opts_t;
|
||||
|
||||
/* Initialize provider and publish snapshot 0 through channel_desc_out. */
|
||||
|
||||
@@ -96,6 +96,11 @@ int cirdb_yaml_select(const cirdb_select_req_t *req, cirdb_entry_meta_t *out)
|
||||
|
||||
float ds_ns = e["ds_ns"].as<float>(0.0f);
|
||||
float sp = e["speed_mps"].as<float>(0.0f);
|
||||
float aoa = e["los_aoa_deg"].as<float>(0.0f);
|
||||
|
||||
// STRICT: AoA exact match when entry has los_aoa_deg field
|
||||
if (e["los_aoa_deg"].IsDefined() && dabs(aoa - req->want_aoa_deg) > 0.01)
|
||||
continue;
|
||||
|
||||
// FLEXIBLE: delay spread and speed - find closest match using cost function
|
||||
double cds = 0.0;
|
||||
|
||||
@@ -15,6 +15,7 @@ typedef struct {
|
||||
int want_rx; // exact match
|
||||
float want_ds_ns; // nearest
|
||||
float want_speed_mps; // nearest
|
||||
float want_aoa_deg; // exact match; 0.0 = broadside default
|
||||
int allow_shape_swap; // allow TX/RX swap when matching
|
||||
float w_ds; // DS distance weight
|
||||
float w_speed; // speed distance weight
|
||||
|
||||
@@ -76,6 +76,7 @@ typedef enum { ROLE_SERVER = 1, ROLE_CLIENT } role;
|
||||
{"cirdb_model_id", "Preferred TDL model id 0..4", 0, .iptr = &vrtsim_state->cirdb_model_id, .defintval = 0, TYPE_INT, 0}, \
|
||||
{"cirdb_ds_ns", "Desired RMS delay spread in ns", 0, .dblptr = &vrtsim_state->cirdb_ds_ns, .defdblval = 10.0, TYPE_DOUBLE, 0}, \
|
||||
{"cirdb_speed_mps", "Desired speed in m/s", 0, .dblptr = &vrtsim_state->cirdb_speed_mps, .defdblval = 1.5, TYPE_DOUBLE, 0}, \
|
||||
{"cirdb_aoa_deg", "Desired AoA in degrees (TDL-D/E only)", 0, .dblptr = &vrtsim_state->cirdb_aoa_deg, .defdblval = 0.0, TYPE_DOUBLE, 0}, \
|
||||
{"num_ues", "Number of UE slots (server only)\n", 0, .iptr = &vrtsim_state->num_ues, .defintval = 1, TYPE_INT, 0}, \
|
||||
{"ue_id", "UE slot index 0..num_ues-1 (client only)\n", 0, .iptr = &vrtsim_state->ue_id, .defintval = 0, TYPE_INT, 0}, \
|
||||
{"thread-pool", TPOOL_HLP, .strptr = &vrtsim_state->thread_pool_cores, .defstrval = "-1,-1,-1,-1", TYPE_STRING, 0} \
|
||||
@@ -106,6 +107,7 @@ typedef struct {
|
||||
int model_id;
|
||||
double ds_ns;
|
||||
double speed_mps;
|
||||
double aoa_deg;
|
||||
} cirdb_conf_t;
|
||||
|
||||
typedef struct {
|
||||
@@ -148,6 +150,7 @@ typedef struct {
|
||||
int cirdb_model_id;
|
||||
double cirdb_ds_ns;
|
||||
double cirdb_speed_mps;
|
||||
double cirdb_aoa_deg;
|
||||
// Multi-UE support
|
||||
int num_ues;
|
||||
int ue_id;
|
||||
@@ -323,13 +326,16 @@ static void parse_ue_config(vrtsim_state_t *vrtsim_state)
|
||||
int model_id = vrtsim_state->cirdb_model_id;
|
||||
double ds_ns = vrtsim_state->cirdb_ds_ns;
|
||||
double speed_mps = vrtsim_state->cirdb_speed_mps;
|
||||
double aoa_deg = vrtsim_state->cirdb_aoa_deg;
|
||||
|
||||
paramdef_t ue_params[] = {
|
||||
{"antennas", "Antenna config e.g. \"1x2\"", 0, .strptr = &antennas, .defstrval = NULL, TYPE_STRING, 0},
|
||||
{"model_id", "TDL model id 0..4", 0, .iptr = &model_id, .defintval = vrtsim_state->cirdb_model_id, TYPE_INT, 0},
|
||||
{"ds_ns", "Delay spread in ns", 0, .dblptr = &ds_ns, .defdblval = vrtsim_state->cirdb_ds_ns, TYPE_DOUBLE, 0},
|
||||
{"speed_mps", "Speed in m/s", 0, .dblptr = &speed_mps, .defdblval = vrtsim_state->cirdb_speed_mps, TYPE_DOUBLE, 0},
|
||||
{"aoa_deg", "LOS AoA in degrees (TDL-D/E only)", 0, .dblptr = &aoa_deg, .defdblval = vrtsim_state->cirdb_aoa_deg, TYPE_DOUBLE, 0},
|
||||
};
|
||||
|
||||
config_get(config_get_if(), ue_params, sizeofArray(ue_params), prefix);
|
||||
|
||||
if (antennas != NULL) {
|
||||
@@ -357,12 +363,13 @@ static void parse_ue_config(vrtsim_state_t *vrtsim_state)
|
||||
vrtsim_state->ue_conf[i].cir_conf.model_id = model_id;
|
||||
vrtsim_state->ue_conf[i].cir_conf.ds_ns = ds_ns;
|
||||
vrtsim_state->ue_conf[i].cir_conf.speed_mps = speed_mps;
|
||||
vrtsim_state->ue_conf[i].cir_conf.aoa_deg = aoa_deg;
|
||||
|
||||
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
|
||||
LOG_I(HW, "VRTSIM: UE %d - %dx%d antennas, Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s, AoA %.1fdeg\n",
|
||||
i,
|
||||
vrtsim_state->ue_conf[i].tx_ant,
|
||||
vrtsim_state->ue_conf[i].rx_ant,
|
||||
model_id, 'A' + model_id, ds_ns, speed_mps);
|
||||
model_id, 'A' + model_id, ds_ns, speed_mps, aoa_deg);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -450,14 +457,17 @@ static int vrtsim_connect(openair0_device_t *device)
|
||||
vrtsim_state->cirdb_model_id = vrtsim_state->ue.cir_conf.model_id;
|
||||
vrtsim_state->cirdb_ds_ns = vrtsim_state->ue.cir_conf.ds_ns;
|
||||
vrtsim_state->cirdb_speed_mps = vrtsim_state->ue.cir_conf.speed_mps;
|
||||
vrtsim_state->cirdb_aoa_deg = vrtsim_state->ue.cir_conf.aoa_deg;
|
||||
|
||||
|
||||
LOG_I(HW,
|
||||
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s\n",
|
||||
"VRTSIM: UE %d channel - Model %d (TDL-%c), DS %.1fns, Speed %.1fm/s, AoA %.1fdeg\n",
|
||||
vrtsim_state->ue_id,
|
||||
vrtsim_state->cirdb_model_id,
|
||||
'A' + vrtsim_state->cirdb_model_id,
|
||||
vrtsim_state->cirdb_ds_ns,
|
||||
vrtsim_state->cirdb_speed_mps);
|
||||
vrtsim_state->cirdb_speed_mps,
|
||||
vrtsim_state->cirdb_aoa_deg);
|
||||
}
|
||||
vrtsim_state->channel = shm_td_iq_channel_connect(DEFAULT_CHANNEL_NAME, 10);
|
||||
vrtsim_state->peer_info.num_rx_antennas = client_info.gnb_num_rx_ant;
|
||||
@@ -507,6 +517,7 @@ static int vrtsim_connect(openair0_device_t *device)
|
||||
sel.want_model_id = vrtsim_state->cirdb_model_id;
|
||||
sel.want_ds_ns = (float)(vrtsim_state->cirdb_ds_ns > 0.0 ? vrtsim_state->cirdb_ds_ns : -1.0);
|
||||
sel.want_speed_mps = (float)(vrtsim_state->cirdb_speed_mps > 0.0 ? vrtsim_state->cirdb_speed_mps : -1.0);
|
||||
sel.want_aoa_deg = (float)vrtsim_state->cirdb_aoa_deg;
|
||||
|
||||
LOG_A(HW,
|
||||
"VRTSIM: CIR DB select yaml='%s' bin='%s'\n",
|
||||
@@ -520,6 +531,7 @@ static int vrtsim_connect(openair0_device_t *device)
|
||||
ue_sel.want_model_id = vrtsim_state->ue_conf[u].cir_conf.model_id;
|
||||
ue_sel.want_ds_ns = vrtsim_state->ue_conf[u].cir_conf.ds_ns;
|
||||
ue_sel.want_speed_mps = vrtsim_state->ue_conf[u].cir_conf.speed_mps;
|
||||
ue_sel.want_aoa_deg = (float)vrtsim_state->ue_conf[u].cir_conf.aoa_deg;
|
||||
|
||||
AssertFatal(ue_sel.want_model_id >= 0 && ue_sel.want_model_id <= 4,
|
||||
"Invalid CIRDB model_id=%d for UE %d\n",
|
||||
|
||||
@@ -1,295 +0,0 @@
|
||||
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
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -1,290 +0,0 @@
|
||||
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";
|
||||
}
|
||||
@@ -1,281 +0,0 @@
|
||||
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";
|
||||
}
|
||||
@@ -1,295 +0,0 @@
|
||||
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
|
||||
};
|
||||
});
|
||||
};
|
||||
@@ -26,7 +26,7 @@ gNBs =
|
||||
maxMIMO_layers = 2;
|
||||
do_CSIRS = 1;
|
||||
do_SRS = 0;
|
||||
min_rxtxtime = 4;
|
||||
min_rxtxtime = 3;
|
||||
|
||||
servingCellConfigCommon = (
|
||||
{
|
||||
@@ -37,20 +37,20 @@ gNBs =
|
||||
# downlinkConfigCommon
|
||||
#frequencyInfoDL
|
||||
# center frequency = 28701.12 MHz
|
||||
# selected SSB frequency = 28691.04 MHz
|
||||
absoluteFrequencySSB = 2090683;
|
||||
# selected SSB frequency = 28656.48 MHz
|
||||
absoluteFrequencySSB = 2090107;
|
||||
dl_frequencyBand = 257;
|
||||
# frequency point A = 28653.6 MHz
|
||||
dl_absoluteFrequencyPointA = 2090059;
|
||||
# frequency point A = 28606.08 MHz
|
||||
dl_absoluteFrequencyPointA = 2089267;
|
||||
#scs-SpecificCarrierList
|
||||
dl_offstToCarrier = 0;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
dl_subcarrierSpacing = 3;
|
||||
dl_carrierBandwidth = 66;
|
||||
dl_carrierBandwidth = 132;
|
||||
#initialDownlinkBWP
|
||||
#genericParameters
|
||||
initialDLBWPlocationAndBandwidth = 17875;
|
||||
initialDLBWPlocationAndBandwidth = 36025;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialDLBWPsubcarrierSpacing = 3;
|
||||
@@ -66,11 +66,11 @@ gNBs =
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
ul_subcarrierSpacing = 3;
|
||||
ul_carrierBandwidth = 66;
|
||||
ul_carrierBandwidth = 132;
|
||||
pMax = 20;
|
||||
#initialUplinkBWP
|
||||
#genericParameters
|
||||
initialULBWPlocationAndBandwidth = 17875;
|
||||
initialULBWPlocationAndBandwidth = 36025;
|
||||
# subcarrierSpacing
|
||||
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
|
||||
initialULBWPsubcarrierSpacing = 3;
|
||||
@@ -90,7 +90,7 @@ gNBs =
|
||||
powerRampingStep = 2;
|
||||
#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;
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
|
||||
#one (0..15) 4,8,12,16,...60,64
|
||||
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
|
||||
#ra_ContentionResolutionTimer
|
||||
@@ -119,11 +119,11 @@ gNBs =
|
||||
hoppingId = 40;
|
||||
p0_nominal = -90;
|
||||
|
||||
ssb_PositionsInBurst_Bitmap = 1;
|
||||
ssb_PositionsInBurst_Bitmap = 4;
|
||||
|
||||
# ssb_periodicityServingCell
|
||||
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
|
||||
ssb_periodicityServingCell = 1;
|
||||
ssb_periodicityServingCell = 4;
|
||||
|
||||
# dmrs_TypeA_position
|
||||
# 0 = pos2, 1 = pos3
|
||||
@@ -141,11 +141,11 @@ gNBs =
|
||||
# pattern1
|
||||
# dl_UL_TransmissionPeriodicity
|
||||
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
|
||||
dl_UL_TransmissionPeriodicity = 3;
|
||||
nrofDownlinkSlots = 7;
|
||||
nrofDownlinkSymbols = 6;
|
||||
nrofUplinkSlots = 2;
|
||||
nrofUplinkSymbols = 4;
|
||||
dl_UL_TransmissionPeriodicity = 1;
|
||||
nrofDownlinkSlots = 3;
|
||||
nrofDownlinkSymbols = 10;
|
||||
nrofUplinkSlots = 1;
|
||||
nrofUplinkSymbols = 2;
|
||||
|
||||
ssPBCH_BlockPower = 0;
|
||||
}
|
||||
@@ -166,8 +166,8 @@ gNBs =
|
||||
|
||||
NETWORK_INTERFACES :
|
||||
{
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.51";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.51";
|
||||
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.18.51";
|
||||
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.18.51";
|
||||
GNB_PORT_FOR_S1U = 2152; # Spec 2152
|
||||
};
|
||||
}
|
||||
@@ -178,7 +178,7 @@ MACRLCs = (
|
||||
tr_s_preference = "local_L1";
|
||||
tr_n_preference = "local_RRC";
|
||||
pusch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 200;
|
||||
pucch_TargetSNRx10 = 250;
|
||||
pusch_FailureThres = 1000;
|
||||
#ul_bler_target_upper =.02;
|
||||
#ul_bler_target_lower =.01;
|
||||
@@ -195,12 +195,12 @@ L1s = (
|
||||
{
|
||||
tr_n_preference = "local_mac";
|
||||
prach_dtx_threshold = 250;
|
||||
pucch0_dtx_threshold = 100;
|
||||
pucch0_dtx_threshold = 150;
|
||||
pusch_dtx_threshold = -200;
|
||||
max_ldpc_iterations = 10;
|
||||
tx_amp_backoff_dB = 18;
|
||||
L1_rx_thread_core = 10;
|
||||
L1_tx_thread_core = 11;
|
||||
L1_rx_thread_core = 37;
|
||||
L1_tx_thread_core = 36;
|
||||
phase_compensation = 1;
|
||||
|
||||
}
|
||||
@@ -219,8 +219,8 @@ RUs = (
|
||||
max_rxgain = 60;
|
||||
#sf_extension = 0;
|
||||
eNB_instances = [0];
|
||||
ru_thread_core = 9;
|
||||
sl_ahead = 10;
|
||||
ru_thread_core = 35;
|
||||
sl_ahead = 5;
|
||||
tr_preference = "raw_if4p5";
|
||||
do_precoding = 0;
|
||||
}
|
||||
@@ -257,10 +257,10 @@ log_config :
|
||||
};
|
||||
|
||||
fhi_72 = {
|
||||
dpdk_devices = ("0000:c1:11.0", "0000:c1:11.1"); # one VF can be used as well
|
||||
system_core = 6;
|
||||
io_core = 7;
|
||||
worker_cores = (8);
|
||||
dpdk_devices = ("01:01.0","01:01.1");
|
||||
system_core = 32;
|
||||
io_core = 33;
|
||||
worker_cores = (34);
|
||||
ru_addr = ("E8:C7:4F:1E:C7:5B", "E8:C7:4F:1E:C7:5B");
|
||||
mtu = 9216;
|
||||
fh_config = ({
|
||||
@@ -241,7 +241,6 @@ log_config :
|
||||
rrc_log_level ="info";
|
||||
ngap_log_level ="debug";
|
||||
f1ap_log_level ="debug";
|
||||
e3ap_log_level ="debug";
|
||||
};
|
||||
|
||||
e2_agent = {
|
||||
|
||||
Reference in New Issue
Block a user