Compare commits

..

29 Commits

Author SHA1 Message Date
francescomani
1617c32e3c symbol based RU control function
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:20:03 +02:00
francescomani
ae780bae57 adapt FR2 RFsim CI test to use beam features
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:20:01 +02:00
francescomani
2834088ea0 do not send beam information to USRP if the beam is the same as before
this keeps the same logic as before, the understanding is that executing the GPIO command requires time or at least have an impact on the timing, so it is better not to execute it if not necessary

Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:59 +02:00
francescomani
0b320c8dce function to set GPIO beam for USRP using new API
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:57 +02:00
francescomani
9fc066394e RU control function for beam information
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:55 +02:00
francescomani
04ce714f37 modification in tx_rf and rx_rf to use read/write beams_func in case of concurrent beams
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 06:19:52 +02:00
Robert Schmidt
d420f8c123 Merge branch 'integration_2026_w18' into 'develop'
Integration `2026.w18`

* !4070 fix(nrLDPC_coding): Remove ldpc_xdma
* !4068 fix(radio): Make write_reorder context thread-safe
* !4031 Add AoA selection to vrtsim
* !4071 Add 200MHz DDDSU Liteon configuration file
* !4080 spsc_q: make compile on older compilers
* !4081 fix (F1 Handover): do not trigger when no DRB is configured
* !4072 UE PRACH slot fix
* !4082 fixing typo in AWGN calculation
* !4049 Validate signed MR commits and update contribution guidelines
* !4087 Remove unnecessary log

Closes #1072

See merge request oai/openairinterface5g!4075
2026-04-30 15:24:22 +00:00
Robert Schmidt
e92dfe2f93 Merge remote-tracking branch 'origin/log_fix' into integration_2026_w18 (!4087)
Remove unnecessary log

We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length of
the samples is no longer samples_per_slot.  # Please enter a commit
message to explain why this merge is necessary,
2026-04-30 11:50:37 +02:00
Rakesh Mundlamuri
35965a1418 Remove unnecessary log
We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length
of the samples is no longer samples_per_slot.

Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-04-30 13:29:44 +05:30
Robert Schmidt
103c1d48e3 Merge remote-tracking branch 'origin/ci-signed-commit' into integration_2026_w18 (!4049)
Validate signed MR commits and update contribution guidelines

This MR adds validation for signed merge request commits and updates the
contributing guidelines including the requirement for signing commits.

- The CI will warn in the Verify Guidelines stage if any commit in the
  merge request is unsigned.
  - Checks all commits in the MR source branch that are not present in
    the target branch
  - It then verifies if all these commits are signed
- Update contributing guidelines to include commit rules and signing
  commits using git commit -s.
- The CI will also check if there are any merge commits and fail the
  pipeline in that case.
2026-04-29 18:51:04 +02:00
Robert Schmidt
0755086cc2 Merge remote-tracking branch 'origin/hotfix_AWGN' into integration_2026_w18 (!4082)
fixing typo in AWGN calculation
2026-04-29 18:50:31 +02:00
Robert Schmidt
c8ff0819f8 Merge remote-tracking branch 'origin/issue1272' into integration_2026_w18 (!4072)
UE PRACH slot fix

This fixes a bug in prach slot computation if more than 1 slot in
subframe at UE

Closes #1072
2026-04-29 18:01:28 +02:00
Robert Schmidt
ee4018b5d0 Merge remote-tracking branch 'origin/f1-ho-trigger-no-drb' into integration_2026_w18 (!4081)
fix (F1 Handover): do not trigger when no DRB is configured

Prevent triggering F1 handover for UEs without any DRB, to align with
3GPP TS 38.473 clause 8.3.1.2 and avoid initiating an invalid UE Context
Setup Request in the F1 handover flow.

Changes:

- Add a preliminary check in nr_rrc_trigger_f1_ho() to verify at least
  one DRB exists in ue->drbs
- Abort F1 HO trigger early when no DRB is present, with warning
2026-04-29 18:01:04 +02:00
Robert Schmidt
49e6575910 Merge remote-tracking branch 'origin/fix-spsc-q-older-compilers' into integration_2026_w18 (!4080)
spsc_q: make compile on older compilers

While compiling the spsc_q unit tests on older compilers, some complain
with

     /openairinterface5g/common/utils/ds/tests/test_spsc_q.cpp:11:44: error: use of deleted function ‘spsc_q::spsc_q(spsc_q&&)’
     spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
     /openairinterface5g/common/utils/ds/spsc_q.h:19:16: error: use of deleted function ‘std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)’

These compiles cannot copy the atomic. Change the API to avoid this
copy.

"Older" compiler here means g++-11/12.
2026-04-29 18:00:36 +02:00
Florian Kaltenberger
79ebf23a80 fixing typo in AWGN calculation
Signed-off-by: Florian Kaltenberger <florian.kaltenberger@eurecom.fr>
2026-04-29 16:54:14 +02:00
Shubhika Garg
3cbf0927be ci: validate for signed MR commits in the CI
- Add a new script to validate for signed commits
- Post a GitLab MR Warning comment listing the unsigned commit details

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-04-29 12:22:52 +02:00
Guido Casati
6c534c3b76 F1 Handover: do not trigger when no DRB is configured
Prevent triggering F1 handover for UEs without any DRB, to align
with 3GPP TS 38.473 clause 8.3.1.2 and avoid initiating an invalid
UE Context Setup Request in the F1 handover flow.

Changes:

- Add a preliminary check in `nr_rrc_trigger_f1_ho()` to verify at
  least one DRB exists in ue->drbs
- Abort F1 HO trigger early when no DRB is present, with warning
2026-04-29 10:30:26 +02:00
Jaroslava Fiedlerova
b5e0cf6a2b Merge remote-tracking branch 'origin/liteon_200MHz' into integration_2026_w18 (!4071)
Add 200MHz DDDSU Liteon configuration file

Adding 200MHz DDDSU 2x2 Liteon configuration file. I think we should keep
only 200 MHz configuration for both FR2 RUs: Liteon and Microamp.
Achieved throughputs are DL: 980 Mbps UL 90 Mbps
2026-04-28 23:06:01 +02:00
Robert Schmidt
4d67a6e513 spsc_q: make compile on older compilers
While compiling the spsc_q unit tests on some compilers, some
complain with

     /openairinterface5g/common/utils/ds/tests/test_spsc_q.cpp:11:44: error: use of deleted function ‘spsc_q::spsc_q(spsc_q&&)’
     spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
     /openairinterface5g/common/utils/ds/spsc_q.h:19:16: error: use of deleted function ‘std::atomic<long unsigned int>::atomic(const std::atomic<long unsigned int>&)’

These compiles cannot copy the atomic. Change the API to avoid this
copy.

"Older" compiler here means g++-11/12.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-28 18:40:24 +02:00
Bartosz Podrygajlo
ced388a0f4 fix(ue): Add a log entry for PRACH occasion selection 2026-04-27 22:27:15 +02:00
francescomani
134d750a88 fix(mac): correct PRACH slot computation in configure_prach_occasions
Move the subframe scaling logic outside the PRACH slot loop to prevent the
subframe index from being incorrectly multiplied multiple times when there
are multiple PRACH slots per subframe.
2026-04-27 22:26:56 +02:00
Shubhika Garg
13963ba70d docs: update contributing guidelines to include commit signing rules
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
Co-authored-by: Sagar Arora <sagar.arora@openairinterface.org>
2026-04-27 13:59:00 +02:00
Karim Boutiba
28c1634fdd Add 200MHz DDDSU Liteon configuration file 2026-04-27 13:49:06 +02:00
Jaroslava Fiedlerova
ec171483e9 Merge remote-tracking branch 'origin/vrtsim-aoa-selection' into integration_2026_w18 (!4031)
Add AoA selection to vrtsim

Adds AoA as a selection parameter in vrtsim. Different UEs can be
configured at different arrival angles, emulating distinct spatial
locations, which can serve as an enabler for beamforming evaluation.
2026-04-27 10:00:10 +02:00
Jaroslava Fiedlerova
d75b8963ab Merge remote-tracking branch 'origin/fix-reorder-context' into integration_2026_w18 (!4068)
fix(radio): Make write_reorder context thread-safe
2026-04-27 09:58:01 +02:00
Jaroslava Fiedlerova
3329877914 Merge remote-tracking branch 'origin/remove_ldpc_xdma' into integration_2026_w18 (!4070)
fix(nrLDPC_coding): Remove ldpc_xdma

Arguments for removing:
- There are no resources to maintain it. Including no machine with the
  board.
- There is no clue that someone is actually using it.
- The library is not standard/cannot be generalized. It was designed for
  a specific solution and cannot be used for another.
- The standard AAL (BBDev or other) should be privileged for LDPC offload
  to a hardware accelerator.
2026-04-27 09:56:35 +02:00
Romain Beurdouche
0538ea4937 fix(nrLDPC_coding): Remove ldpc_xdma
Arguments for removing:
1. There are no resources to maintain it.
   Including no machine with the board.
2. There is no clue that someone is actually using it.
3. AAL is privileged for LDPC offload to a hardware accelerator.
2026-04-24 12:09:34 +00:00
Bartosz Podrygajlo
b5aa2d87fb fix(radio): Make write_reorder context thread-safe 2026-04-24 06:49:20 +02:00
Merkebu Girmay
58f00921c8 Add AoA selection to vrtsim in-process CIRDB path 2026-04-15 11:19:55 -07:00
80 changed files with 481 additions and 6465 deletions

2
.gitignore vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
LOG_T(<component>,<format>,<argument>,...)
)
```
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the 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 |
|:---------|:---------------|:---------------|----------------:|

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View 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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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, &registered_dapp_len);
LOG_E(E3AP,
"[SPECTRUM] No subscribers for ran_function_id=%u "
"(total_subscriptions=%zu, registered_dapps=%zu)\n",
SPECTRUM_SM_RAN_FUNCTION_ID,
total_subscriptions,
registered_dapp_len);
if (registered_dapps) {
e3_agent_free_uint32_array(registered_dapps);
}
}
}
e3_agent_free_uint32_array(dapp_ids);
free(encoded_data);
}
if (ebuf.obuf) {
free(ebuf.obuf);
}
return NULL;
}
static e3_error_t spectrum_sm_init(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return E3_SM_ERROR_INVALID_PARAM;
}
/* Preserve handle set before SM registration; memset below would clear it. */
e3_service_model_handle_t *preset_sm_handle = ctx->sm_handle;
memset(ctx, 0, sizeof(*ctx));
ctx->t_tracer_socket = -1;
ctx->sm_handle = preset_sm_handle;
pthread_mutex_init(&ctx->lock, NULL);
ctx->initialized = true;
LOG_D(E3AP, "[SPECTRUM] SM init complete (preset_sm_handle=%p)\n", (void *)ctx->sm_handle);
ctx->connector_running = true;
if (pthread_create(&ctx->connector_thread, NULL, t_tracer_connector_thread_main, ctx) != 0) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_THREAD_FAILED;
}
ctx->connector_started = true;
e3_sm_spectrum_control = calloc(1, sizeof(*e3_sm_spectrum_control));
if (!e3_sm_spectrum_control) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_MEMORY;
}
e3_sm_spectrum_control->action_list = calloc(MAX_BWP_SIZE, sizeof(uint16_t));
if (!e3_sm_spectrum_control->action_list) {
spectrum_sm_destroy(ctx);
return E3_SM_ERROR_MEMORY;
}
pthread_mutex_init(&e3_sm_spectrum_control->mutex, NULL);
e3_sm_spectrum_control->sampling_threshold = DEFAULT_SM_SAMPLING_THRESHOLD;
return E3_SUCCESS;
}
static e3_error_t spectrum_sm_start(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx || !ctx->initialized) {
return E3_NOT_INITIALIZED;
}
pthread_mutex_lock(&ctx->lock);
if (ctx->running) {
pthread_mutex_unlock(&ctx->lock);
return E3_SUCCESS;
}
ctx->running = true;
pthread_mutex_unlock(&ctx->lock);
if (pthread_create(&ctx->worker_thread, NULL, spectrum_sm_thread_main, ctx) != 0) {
pthread_mutex_lock(&ctx->lock);
ctx->running = false;
pthread_mutex_unlock(&ctx->lock);
return E3_SM_ERROR_THREAD_FAILED;
}
return E3_SUCCESS;
}
static void spectrum_sm_stop(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return;
}
pthread_mutex_lock(&ctx->lock);
int was_running = ctx->running;
ctx->running = false;
pthread_mutex_unlock(&ctx->lock);
if (was_running) {
pthread_cancel(ctx->worker_thread);
pthread_join(ctx->worker_thread, NULL);
}
if (ctx->connector_started) {
pthread_mutex_lock(&ctx->lock);
ctx->connector_running = false;
pthread_mutex_unlock(&ctx->lock);
pthread_join(ctx->connector_thread, NULL);
ctx->connector_started = false;
}
}
static int spectrum_sm_is_running(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return 0;
}
pthread_mutex_lock(&ctx->lock);
int running = is_running_locked(ctx);
pthread_mutex_unlock(&ctx->lock);
return running;
}
static void spectrum_sm_destroy(void *sm_context)
{
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx) {
return;
}
spectrum_sm_stop(sm_context);
if (ctx->event_flags) {
free(ctx->event_flags);
ctx->event_flags = NULL;
}
if (ctx->database) {
free_database(ctx->database);
ctx->database = NULL;
}
if (ctx->t_tracer_socket >= 0) {
close(ctx->t_tracer_socket);
ctx->t_tracer_socket = -1;
}
if (e3_sm_spectrum_control) {
if (e3_sm_spectrum_control->action_list) {
free(e3_sm_spectrum_control->action_list);
e3_sm_spectrum_control->action_list = NULL;
}
pthread_mutex_destroy(&e3_sm_spectrum_control->mutex);
free(e3_sm_spectrum_control);
e3_sm_spectrum_control = NULL;
}
if (ctx->initialized) {
pthread_mutex_destroy(&ctx->lock);
}
if (spectrum_ran_function_data) {
free(spectrum_ran_function_data);
spectrum_ran_function_data = NULL;
spectrum_ran_function_data_len = 0;
spectrum_ran_function_data_ready = 0;
spectrum_sm_desc.ran_function_data = NULL;
spectrum_sm_desc.ran_function_data_len = 0;
}
memset(ctx, 0, sizeof(*ctx));
ctx->t_tracer_socket = -1;
}
static e3_error_t spectrum_sm_process_control(e3_service_model_handle_t* sm_handle,
void* sm_context,
uint32_t request_message_id,
uint32_t dapp_id,
uint32_t ran_function_id,
uint32_t control_id,
const uint8_t* data,
size_t data_len)
{
(void)dapp_id;
int response_code = 0; // Positive
spectrum_sm_context_t *ctx = (spectrum_sm_context_t *)sm_context;
if (!ctx || !sm_handle || ran_function_id != SPECTRUM_SM_RAN_FUNCTION_ID ||
control_id != SPECTRUM_SM_RAN_FUNCTION_ID) {
if (sm_handle) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
}
return E3_SM_ERROR_INVALID_PARAM;
}
pthread_mutex_lock(&ctx->lock);
ctx->sm_handle = sm_handle;
pthread_mutex_unlock(&ctx->lock);
if (!data || data_len == 0 || !e3_sm_spectrum_control) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SM_ERROR_INVALID_PARAM;
}
spectrum_prb_control_t* control_payload =
spectrum_decode_prb_control((uint8_t *)data, data_len);
if (!control_payload) {
response_code = 1; // Negative
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SM_ERROR_INVALID_PARAM;
}
const size_t max_elems = MAX_BWP_SIZE;
size_t elems = control_payload->prb_count;
if (elems > max_elems) {
elems = max_elems;
}
pthread_mutex_lock(&e3_sm_spectrum_control->mutex);
uint16_t *alist16 = e3_sm_spectrum_control->action_list;
memcpy(alist16, control_payload->blacklisted_prbs, elems * sizeof(uint16_t));
e3_sm_spectrum_control->action_size = (int)elems;
memset(e3_sm_spectrum_control->dyn_prbbl, 0, max_elems * sizeof(uint16_t));
for (size_t j = 0; j < elems; ++j) {
uint16_t prb = alist16[j];
if (prb < MAX_BWP_SIZE) {
e3_sm_spectrum_control->dyn_prbbl[prb] = 0x3FFF;
}
}
memset(alist16, 0, max_elems * sizeof(uint16_t));
if (control_payload->sampling_threshold > 0) {
e3_sm_spectrum_control->sampling_threshold = control_payload->sampling_threshold;
}
e3_sm_spectrum_control->ready = 1;
pthread_mutex_unlock(&e3_sm_spectrum_control->mutex);
spectrum_free_decoded_control(control_payload);
e3_service_model_emit_message_ack(sm_handle, request_message_id, response_code);
return E3_SUCCESS;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -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 = ({

View File

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