Compare commits

..

1 Commits

Author SHA1 Message Date
Bartosz Podrygajlo
d3d9380131 pre-commit config 2026-04-20 16:50:54 +02:00
212 changed files with 5581 additions and 8528 deletions

1
.gitignore vendored
View File

@@ -7,7 +7,6 @@ cmake_targets/*/build/
cmake_targets/ran_build/
log/
lte_build_oai/
build*
# IDE files
.vscode

View File

@@ -83,40 +83,25 @@ option(PACKAGING_COMMON "Will produce a package containing common target, config
option(PACKAGING_USRP "Will produce a package containing usrp target, configuration files and docs" OFF)
option(PACKAGING_PHYSIM "Will produce a package containing physim target, configuration files and docs" OFF)
# Check if asn1c is installed, and check if it supports all options we need
# a user can provide ASN1C_EXEC to override an asn1c to use (e.g., parallel
# installation)
find_program(ASN1C_EXEC_PATH asn1c HINTS /opt/asn1c/bin)
set(ASN1C_EXEC ${ASN1C_EXEC_PATH} CACHE FILEPATH "path to asn1c executable")
option(AUTO_DOWNLOAD_ASN1C "Automatically download asn1c if not found" OFF)
if (ASN1C_EXEC)
check_option(${ASN1C_EXEC} -gen-APER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-UPER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-JER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-BER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-OER ASN1C_EXEC)
add_custom_target(asn1c DEPENDS ${ASN1C_EXEC})
else ()
if (NOT AUTO_DOWNLOAD_ASN1C)
message(FATAL_ERROR "asn1c not found. Install it globally or activate AUTO_DOWNLOAD_ASN1C")
endif()
message(STATUS "Downloading and compile asn1c local in this build tree")
include(ExternalProject)
ExternalProject_Add(
asn1c_gen
GIT_REPOSITORY https://github.com/mouse07410/asn1c
GIT_TAG 940dd5fa9f3917913fd487b13dfddfacd0ded06e
GIT_REMOTE_UPDATE_STRATEGY CHECKOUT
BUILD_BYPRODUCTS ${CMAKE_BINARY_DIR}/bin/asn1c
BUILD_IN_SOURCE True
CONFIGURE_COMMAND autoreconf -i
BUILD_COMMAND ./configure --prefix ${CMAKE_BINARY_DIR}
COMMAND make -j8 CFLAGS=-fno-strict-aliasing -Wmisleading-indentation
STEP_TARGETS build
)
set(ASN1C_EXEC ${CMAKE_BINARY_DIR}/bin/asn1c)
add_custom_target(asn1c DEPENDS asn1c_gen)
if(NOT ASN1C_EXEC)
message(FATAL_ERROR "No asn1c found!
You might want to re-run ./build_oai -I
Or provide a path to asn1c using
./build_oai ... --cmake-opt -DASN1C_EXEC=/path/to/asn1c
")
endif()
check_option(${ASN1C_EXEC} -gen-APER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-UPER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-JER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-BER ASN1C_EXEC)
check_option(${ASN1C_EXEC} -no-gen-OER ASN1C_EXEC)
#########################################################
# Base directories, compatible with legacy OAI building #
#########################################################
@@ -435,6 +420,7 @@ add_library(ngap
${NGAP_DIR}/ngap_gNB_pdu_session_management.c
${NGAP_DIR}/ngap_gNB_nnsf.c
${NGAP_DIR}/ngap_gNB_overload.c
${NGAP_DIR}/ngap_gNB_trace.c
${NGAP_DIR}/ngap_gNB_ue_context.c
${NGAP_DIR}/ngap_gNB_NRPPa_transport_procedures.c
)
@@ -882,6 +868,7 @@ set(PHY_SRC_COMMON
${OPENAIR1_DIR}/PHY/TOOLS/sqrt.c
${OPENAIR1_DIR}/PHY/TOOLS/get_sin_cos.c
${OPENAIR1_DIR}/PHY/TOOLS/oai_arith_operations.c
${OPENAIR1_DIR}/PHY/log_tools.c
)
set(PHY_SRC
@@ -1017,7 +1004,6 @@ set(PHY_SRC_UE
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/pucch_rx.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/srs_rx.c
${OPENAIR1_DIR}/PHY/NR_TRANSPORT/nr_uci_tools_common.c
${OPENAIR1_DIR}/PHY/phy_digital_beamforming.c
)
set(PHY_NR_UE_SRC
${OPENAIR1_DIR}/PHY/INIT/nr_parms.c

8
NOTICE
View File

@@ -84,11 +84,3 @@ Nuand: https://github.com/Nuand/bladeRF/tree/master?tab=License-1-ov-file
Credits for https://github.com/pothosware/SoapySDR
Pothosware: BSL 1.0 License
Credits for https://github.com/zeromq/libzmq
ZeroMQ authors: Mozilla Public License Version 2.0
Credits for source code:
- radio/zmq/zmq_imported.cpp
- radio/zmq/zmq_imported.h
Software Radio Systems Limited: BSD-3-Clause-Open-MPI

View File

@@ -0,0 +1,20 @@
repos:
# Clang-Format: Ensures code style is consistent
- repo: https://github.com/pre-commit/mirrors-clang-format
rev: 'v22.1.3'
hooks:
- id: clang-format
# Cppcheck: Static analysis to find bugs
- repo: https://github.com/pocc/pre-commit-hooks
rev: 'v1.3.5'
hooks:
- id: cppcheck
# Basic sanity checks (trailing whitespace, end of file, etc.)
- repo: https://github.com/pre-commit/pre-commit-hooks
rev: 'v6.0.0'
hooks:
- id: check-added-large-files
- id: check-case-conflict
- id: end-of-file-fixer

View File

@@ -79,11 +79,11 @@ oc-cn5g:
oc-cn5g-20897:
Host: cacofonix
NetworkScript: echo "inet 172.21.6.115"
NetworkScript: echo "inet 172.21.6.105"
RunIperf3Server: False
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2026-apr oaicicd-core-for-fhi72"
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2026-apr oaicicd-core-for-fhi72"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2026-apr oaicicd-core-for-fhi72 %%log_dir%%"
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-fhi72"
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-fhi72"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2025-jan oaicicd-core-for-fhi72 %%log_dir%%"
oc-cn5g-00103-ho:
Host: groot

View File

@@ -215,7 +215,6 @@ L1s =
(
{
num_cc = 1;
prach_dtx_threshold = 200;
tr_n_preference = "local_mac";
}
);

View File

@@ -186,10 +186,10 @@ MACRLCs = (
local_s_portd = 50011; // vnf p7 port [!]
tr_s_preference = "aerial";
tr_n_preference = "local_RRC";
pucch_RSSI_Threshold = -270;
pusch_RSSI_Threshold = -260;
pusch_TargetSNRx10 = 290;
pucch_TargetSNRx10 = 200;
pucch_RSSI_Threshold = -270;
pusch_RSSI_Threshold = -260;
pusch_TargetSNRx10 = 260; # 150;
pucch_TargetSNRx10 = 200; #200;
dl_max_mcs = 28;
ul_max_mcs = 28;
ul_min_mcs = 9;

View File

@@ -161,7 +161,7 @@ gNBs =
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.21.6.113"; });
amf_ip_address = ({ ipv4 = "172.21.6.103"; });
NETWORK_INTERFACES :
{

View File

@@ -197,7 +197,7 @@ RUs = (
nb_tx = 2;
nb_rx = 2;
att_tx = 0;
att_rx = 6;
att_rx = 0;
bands = [77];
max_pdschReferenceSignalPower = -27;
max_rxgain = 71;

View File

@@ -18,7 +18,7 @@ gNBs =
nr_cellid = 12345678L;
////////// Physical parameters:
use_deltaMCS = 1;
servingCellConfigCommon = (
{
@@ -180,7 +180,7 @@ MACRLCs = (
{
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 100;
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
ul_prbblack_SNR_threshold = 10;
}

View File

@@ -10,7 +10,7 @@ ColNames :
- Average vs Reference Deviation (Reference Value; Acceptability Deviation Threshold)
Ref :
feprx : 150.0
feptx_prec : 30.0
feptx_prec : 0.0
feptx_ofdm : 60.0
feptx_total : 150.0
L1 Tx processing : 475.0

View File

@@ -13,12 +13,11 @@ RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
libgtest-dev \
libyaml-cpp-dev \
libzmq3-dev
libyaml-cpp-dev
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
WORKDIR /oai-ran/build
RUN cmake -GNinja -DENABLE_TESTS=ON -DOAI_ZMQ=ON -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=True .. && ninja tests
RUN cmake -GNinja -DENABLE_TESTS=ON -DCMAKE_BUILD_TYPE=Debug -DSANITIZE_ADDRESS=True .. && ninja tests

View File

@@ -1,48 +0,0 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
set -e
RU_IP="10.10.0.111"
RU_HOST="root@10.10.0.111"
ssh_exec() {
ssh $RU_HOST $@ </dev/null
}
echo "→ Checking for PTP fluctuations in the last 60 minutes..."
logs=$(journalctl -u ptp4l.service --since "60 minutes ago")
check_ptp=$(echo "$logs" | awk '
/rms/ {
rms=$8
if (rms > 100) {
print "✗ PTP FLUCTUATION DETECTED → " $0
exit 0
}
}
')
if [ -n "$check_ptp" ]; then
echo "$check_ptp"
else
echo "✓ No PTP fluctuations detected."
fi
echo "→ Checking if VVDN LPRU is PTP synchronized (timeout 1 min)..."
if ssh_exec 'timeout 1m sh -c "tail -F /var/log/synctimingptp2.log | grep -m 1 -F ,\ synchronized"'; then
echo "✓ VVDN LPRU synchronized"
else
echo "✗ VVDN LPRU not synchronized"
fi
echo "→ Checking TX/RX carrier configuration..."
if ssh_exec 'sysrepocfg -X -d running -f xml | grep -q "<active>INACTIVE</active>"'; then
echo "→ Some carriers are not active, activating via sysrepocfg..."
ssh_exec 'sysrepocfg -X -d running -f xml | sed "s/<active>INACTIVE<\/active>/<active>ACTIVE<\/active>/g" | sysrepocfg -I -d running -f xml'
echo "✓ All TX/RX carriers activated."
else
echo "✓ All TX/RX carriers already activated."
fi
exit 0

View File

@@ -1,14 +0,0 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
set -e
RU_HOST="root@10.10.0.111"
ssh_exec() {
ssh $RU_HOST $@ </dev/null
}
echo "→ Inactivating TX/RX carriers on VVDN LPRU..."
ssh_exec 'sysrepocfg -X -d running -f xml | sed "s/ACTIVE/INACTIVE/g" | sysrepocfg -I -d running -f xml'
echo "✓ TX/RX carriers inactivated."

View File

@@ -1,109 +0,0 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>zmq-5gnr-tdd-2x2</htmlTabRef>
<htmlTabName>Monolithic SA TDD 2x2 gNB with zmq radio</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>localhost</node>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2/</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G gNB+UE RF sim SA</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2</yaml_path>
<services>oai-gnb oai-nr-ue</services>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<node>localhost</node>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/10Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 10M -t 20 -R</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/3Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 3M -t 20</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2</yaml_path>
<analysis>
<services>oai-gnb=RetxCheck=1,0,0,0 oai-gnb=EndsWithBye oai-nr-ue</services>
</analysis>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>

View File

@@ -1,109 +0,0 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>ocudu-5gnr-tdd-1x1</htmlTabRef>
<htmlTabName>OCUDU gnb split8 with OAI UE</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>localhost</node>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_1x1_ocudu</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OCUDU Split 8 gNB + OAI UE</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_1x1_ocudu</yaml_path>
<services>ocudu-gnb oai-nr-ue</services>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<node>localhost</node>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/3Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 3M -t 20 -R</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/3Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 3M -t 20</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_1x1_ocudu</yaml_path>
<analysis>
<services>oai-nr-ue</services>
</analysis>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>

View File

@@ -1,109 +0,0 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>ocudu-5gnr-tdd-2x2</htmlTabRef>
<htmlTabName>OCUDU gnb split8 with OAI UE</htmlTabName>
<htmlTabIcon>wrench</htmlTabIcon>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace</desc>
<node>localhost</node>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2_ocudu</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy OCUDU Split 8 gNB + OAI UE</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2_ocudu</yaml_path>
<services>ocudu-gnb oai-nr-ue</services>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach OAI UE (Wait for IP)</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE</desc>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>
<id>rfsim5g_ext_dn</id>
<node>localhost</node>
<svr_id>rfsim5g_ue</svr_id>
<svr_node>localhost</svr_node>
<ping_args>-c 20 -i 0.25</ping_args>
<ping_packetloss_threshold>5</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/10Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 1M -t 20 -R</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/3Mbps/UDP)(20 sec)</desc>
<iperf_args>-u -b 1M -t 20</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>5</iperf_packetloss_threshold>
<iperf_bitrate_threshold>90</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy all OAI 5G stack</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_zmq_radio_2x2_ocudu</yaml_path>
<analysis>
<services>oai-nr-ue</services>
</analysis>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>localhost</node>
<images>oai-gnb-asan oai-nr-ue-asan</images>
</testCase>
</testCaseList>

View File

@@ -122,7 +122,7 @@
<node>gracehopper1-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_gnb_aerial</yaml_path>
<analysis>
<services>oai-gnb-aerial=EndsWithBye</services>
<services>oai-gnb-aerial=RetxCheck=20,100,100,100 oai-gnb-aerial=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -111,7 +111,7 @@
<node>gracehopper1-oai</node>
<yaml_path>ci-scripts/yaml_files/sa_gnb_aerial_ul</yaml_path>
<analysis>
<services>oai-gnb-aerial=EndsWithBye</services>
<services>oai-gnb-aerial=RetxCheck=20,100,100,100 oai-gnb-aerial=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -12,13 +12,6 @@
<script>yaml_files/sa_fhi_7.2_vvdn_gnb/setup_config.sh</script>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Activate carriers on VVDN RU</desc>
<node>cacofonix</node>
<script>scripts/vvdn-activate-carriers.sh</script>
</testCase>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
@@ -141,9 +134,9 @@
<testCase>
<class>Custom_Script</class>
<always_exec>true</always_exec>
<desc>Inactivate carriers on VVDN RU</desc>
<desc>Set CPU to idle state, set kernel parameters to default values</desc>
<node>cacofonix</node>
<script>scripts/vvdn-inactivate-carriers.sh</script>
<script>yaml_files/sa_fhi_7.2_vvdn_gnb/setup_cleanup.sh</script>
</testCase>
</testCaseList>

View File

@@ -104,7 +104,7 @@
<node>matix</node>
<yaml_path>ci-scripts/yaml_files/5g_sa_n310_2x2_100MHz</yaml_path>
<analysis>
<services>oai-gnb=EndsWithBye</services>
<services>oai-gnb=RetxCheck=dl=10,100,100,100;ul=25,100,100,100 oai-gnb=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -104,7 +104,7 @@
<node>matix</node>
<yaml_path>ci-scripts/yaml_files/5g_sa_n310_2x2_60MHz</yaml_path>
<analysis>
<services>oai-gnb=EndsWithBye</services>
<services>oai-gnb=RetxCheck=dl=15,100,100,100;ul=20,100,100,100 oai-gnb=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -104,7 +104,7 @@
<node>matix</node>
<yaml_path>ci-scripts/yaml_files/5g_sa_n310_4x4_60MHz</yaml_path>
<analysis>
<services>oai-vnf=EndsWithBye oai-pnf=EndsWithBye</services>
<services>oai-vnf=RetxCheck=10,100,100,100 oai-vnf=EndsWithBye oai-pnf=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -1,162 +0,0 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
init: true
volumes:
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.71.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:v2.1.10
environment:
- TZ=Europe/paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.71.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.1.10
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.1.10
init: true
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.71.134
interface_name: eth0
traffic_net:
ipv4_address: 192.168.72.134
interface_name: eth1
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
init: true
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.72.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.72.134"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
ocudu-gnb:
image: ghcr.io/bpodrygajlo/ocudu/ocudu-avx2:7cecca5
container_name: ocudu-gnb
cap_drop:
- ALL
cap_add:
- SYS_NICE
depends_on:
- oai-ext-dn
networks:
public_net:
ipv4_address: 192.168.71.140
volumes:
- ./ocudu.yml:/ocudu.yml
healthcheck:
test: /bin/bash -c "pgrep gnb_split_8"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
command: ./usr/local/bin/gnb_split_8 -c /ocudu.yml
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: -r 51 --numerology 1 --band 78 -C 3489420000 --ssb 0 -E --uicc0.imsi 208990100001100 --uecap_file /opt/oai-nr-ue/etc/uecap.xml --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://0.0.0.0:4556 --zmq.[0].rx_channels tcp://192.168.71.140:4558
depends_on:
- ocudu-gnb
networks:
public_net:
ipv4_address: 192.168.71.150
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports1.xml:/opt/oai-nr-ue/etc/uecap.xml
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"

View File

@@ -1,52 +0,0 @@
# SPDX-FileCopyrightText: Copyright (C) 2021-2026 Software Radio Systems Limited
# SPDX-License-Identifier: BSD-3-Clause-Open-MPI
# Refer to https://gitlab.com/ocudu/ocudu/-/raw/dev/LICENSE?ref_type=heads
cu_cp:
amf:
addrs: 192.168.71.132
port: 38412
bind_addrs: 192.168.71.140
supported_tracking_areas:
- tac: 1
plmn_list:
- plmn: "20899"
tai_slice_support_list:
- sst: 1
ru_sdr:
device_driver: zmq
device_args: tx_port=tcp://0.0.0.0:4558,rx_port=tcp://192.168.71.150:4556
srate: 23.04
tx_gain: 25
rx_gain: 25
cell_cfg:
dl_arfcn: 632628
band: 78
channel_bandwidth_MHz: 20
common_scs: 30
plmn: "20899"
tac: 1
pci: 1
tdd_ul_dl_cfg:
dl_ul_tx_period: 5
nof_dl_slots: 3
nof_dl_symbols: 10
nof_ul_slots: 1
nof_ul_symbols: 2
csi:
csi_rs_enabled: false
pucch:
formats: f0_and_f2
nof_cell_csi_res: 0
log:
filename: gnb.log
all_level: debug
pcap:
mac_enable: false
mac_filename: /tmp/gnb_mac.pcap
ngap_enable: false
ngap_filename: /tmp/gnb_ngap.pcap

View File

@@ -1,164 +0,0 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
init: true
volumes:
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.71.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:v2.1.10
environment:
- TZ=Europe/paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.71.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.1.10
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.1.10
init: true
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.71.134
interface_name: eth0
traffic_net:
ipv4_address: 192.168.72.134
interface_name: eth1
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
init: true
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.72.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.72.134"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-gnb:
image: ${REGISTRY-oaisoftwarealliance/}${GNB_IMG:-oai-gnb}:${TAG:-develop}
container_name: rfsim5g-oai-gnb
cap_drop:
- ALL
cap_add:
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://0.0.0.0:4558,tcp://0.0.0.0:4559 --zmq.[0].rx_channels tcp://oai-nr-ue:4556,tcp://oai-nr-ue:4557
ASAN_OPTIONS: detect_leaks=0
depends_on:
- oai-ext-dn
networks:
public_net:
ipv4_address: 192.168.71.140
volumes:
- ../../conf_files/gnb.sa.band78.273prb.rfsim.2x2.conf:/opt/oai-gnb/etc/gnb.conf
healthcheck:
test: /bin/bash -c "pgrep nr-softmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: -r 273 --numerology 1 --band 78 -C 3450720000 --ssb 1518 --ue-nb-ant-tx 2 --uicc0.imsi 208990100001100 --ue-nb-ant-rx 2 --uecap_file /opt/oai-nr-ue/etc/uecap.xml --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://0.0.0.0:4556,tcp://0.0.0.0:4557 --zmq.[0].rx_channels tcp://oai-gnb:4558,tcp://oai-gnb:4559
depends_on:
- oai-gnb
networks:
public_net:
ipv4_address: 192.168.71.150
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports2.xml:/opt/oai-nr-ue/etc/uecap.xml
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"

View File

@@ -1,162 +0,0 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:8.0
init: true
volumes:
- ../5g_rfsimulator/oai_db.sql:/docker-entrypoint-initdb.d/oai_db.sql
- ../5g_rfsimulator/mysql-healthcheck.sh:/tmp/mysql-healthcheck.sh
environment:
- TZ=Europe/Paris
- MYSQL_DATABASE=oai_db
- MYSQL_USER=test
- MYSQL_PASSWORD=test
- MYSQL_ROOT_PASSWORD=linux
healthcheck:
test: /bin/bash -c "/tmp/mysql-healthcheck.sh"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 30
networks:
public_net:
ipv4_address: 192.168.71.131
oai-amf:
container_name: "rfsim5g-oai-amf"
image: oaisoftwarealliance/oai-amf:v2.1.10
environment:
- TZ=Europe/paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-amf/etc/config.yaml
depends_on:
- mysql
networks:
public_net:
ipv4_address: 192.168.71.132
oai-smf:
container_name: "rfsim5g-oai-smf"
image: oaisoftwarealliance/oai-smf:v2.1.10
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.1.10
init: true
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-upf/etc/config.yaml
depends_on:
- oai-smf
cap_add:
- NET_ADMIN
- SYS_ADMIN
cap_drop:
- ALL
privileged: true
networks:
public_net:
ipv4_address: 192.168.71.134
interface_name: eth0
traffic_net:
ipv4_address: 192.168.72.134
interface_name: eth1
oai-ext-dn:
privileged: true
container_name: rfsim5g-oai-ext-dn
image: oaisoftwarealliance/trf-gen-cn5g:focal
init: true
entrypoint: /bin/bash -c \
"iptables -t nat -A POSTROUTING -o eth0 -j MASQUERADE;"\
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:
traffic_net:
ipv4_address: 192.168.72.135
healthcheck:
test: /bin/bash -c "ping -c 2 192.168.72.134"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
ocudu-gnb:
image: ghcr.io/bpodrygajlo/ocudu/ocudu-avx2:7cecca5
container_name: ocudu-gnb
cap_drop:
- ALL
cap_add:
- SYS_NICE
depends_on:
- oai-ext-dn
networks:
public_net:
ipv4_address: 192.168.71.140
volumes:
- ./ocudu.yml:/ocudu.yml
healthcheck:
test: /bin/bash -c "pgrep gnb_split_8"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
command: ./usr/local/bin/gnb_split_8 -c /ocudu.yml
oai-nr-ue:
image: ${REGISTRY-oaisoftwarealliance/}${NRUE_IMG:-oai-nr-ue}:${TAG:-develop}
container_name: rfsim5g-oai-nr-ue
cap_drop:
- ALL
cap_add:
- NET_ADMIN # for interface bringup
- NET_RAW # for ping
- SYS_NICE
environment:
USE_ADDITIONAL_OPTIONS: -r 106 --numerology 1 --band 78 -C 3489420000 --ssb 42 --ue-nb-ant-tx 2 --uicc0.imsi 208990100001100 --ue-nb-ant-rx 2 --uecap_file /opt/oai-nr-ue/etc/uecap.xml --log_config.global_log_options level,nocolor,time --device.name oai_zmqdevif --zmq.[0].tx_channels tcp://0.0.0.0:4556,tcp://0.0.0.0:4557 --zmq.[0].rx_channels tcp://192.168.71.140:4558,tcp://192.168.71.140:4559
depends_on:
- ocudu-gnb
networks:
public_net:
ipv4_address: 192.168.71.150
devices:
- /dev/net/tun:/dev/net/tun
volumes:
- ../../../targets/PROJECTS/GENERIC-NR-5GC/CONF/uecap_ports2.xml:/opt/oai-nr-ue/etc/uecap.xml
- ../../conf_files/nrue.uicc.conf:/opt/oai-nr-ue/etc/nr-ue.conf
healthcheck:
test: /bin/bash -c "pgrep nr-uesoftmodem"
start_period: 10s
start_interval: 500ms
interval: 10s
timeout: 5s
retries: 5
networks:
public_net:
driver: bridge
name: rfsim5g-oai-public-net
ipam:
config:
- subnet: 192.168.71.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-public"
traffic_net:
driver: bridge
name: rfsim5g-oai-traffic-net
ipam:
config:
- subnet: 192.168.72.128/26
driver_opts:
com.docker.network.bridge.name: "rfsim5g-traffic"

View File

@@ -1,54 +0,0 @@
# SPDX-FileCopyrightText: Copyright (C) 2021-2026 Software Radio Systems Limited
# SPDX-License-Identifier: BSD-3-Clause-Open-MPI
# Refer to https://gitlab.com/ocudu/ocudu/-/raw/dev/LICENSE?ref_type=heads
cu_cp:
amf:
addrs: 192.168.71.132
port: 38412
bind_addrs: 192.168.71.140
supported_tracking_areas:
- tac: 1
plmn_list:
- plmn: "20899"
tai_slice_support_list:
- sst: 1
ru_sdr:
device_driver: zmq
device_args: tx_port=tcp://0.0.0.0:4558,tx_port=tcp://0.0.0.0:4559,rx_port=tcp://192.168.71.150:4556,rx_port=tcp://192.168.71.150:4557
srate: 61.44
tx_gain: 25
rx_gain: 25
cell_cfg:
dl_arfcn: 632628
band: 78
channel_bandwidth_MHz: 40
common_scs: 30
plmn: "20899"
tac: 1
pci: 1
nof_antennas_ul: 2
nof_antennas_dl: 2
tdd_ul_dl_cfg:
dl_ul_tx_period: 5
nof_dl_slots: 3
nof_dl_symbols: 10
nof_ul_slots: 1
nof_ul_symbols: 2
csi:
csi_rs_enabled: false
pucch:
formats: f0_and_f2
nof_cell_csi_res: 0
log:
filename: gnb.log
all_level: debug
pcap:
mac_enable: false
mac_filename: /tmp/gnb_mac.pcap
ngap_enable: false
ngap_filename: /tmp/gnb_ngap.pcap

View File

@@ -0,0 +1,7 @@
# SPDX-License-Identifier: MIT
set -e
sudo cpupower idle-set -E > /dev/null
sudo sysctl kernel.sched_rt_runtime_us=950000
sudo sysctl kernel.timer_migration=1
exit 0

View File

@@ -17,7 +17,7 @@ services:
devices:
- /dev/bus/usb/:/dev/bus/usb/
volumes:
- ../../conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma-deltaMCS.conf:/opt/oai-gnb/etc/gnb.conf
- ../../conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma.conf:/opt/oai-gnb/etc/gnb.conf
# for performance reasons, we use host mode: in bridge mode, we have
# unacceptable DL TCP performance. However, the whole point of
# containerization is to not be in host mode, so update this to macvlan

View File

@@ -68,7 +68,7 @@ Options:
--UE-conf-nvram [configuration file]
Specify conf_nvram_path (default \"$conf_nvram_path\")
-w | --hardware
USRP, BLADERF, LMSSDR, IRIS, SIMU, AW2SORI, AERIAL, ZMQ, None (Default)
USRP, BLADERF, LMSSDR, IRIS, SIMU, AW2SORI, AERIAL, None (Default)
Adds this RF board support (in external packages installation and in compilation)
-t | --transport
Selects the transport protocol type, options: None, Ethernet, oran_fhlib_5g, oran_fhlib_5g_mplane, WLS
@@ -223,7 +223,7 @@ function main() {
shift 2;;
-w | --hardware)
case "$2" in
"USRP" | "BLADERF" | "LMSSDR" | "IRIS" | "ZMQ")
"USRP" | "BLADERF" | "LMSSDR" | "IRIS")
HWs+=" OAI_"$2
TARGET_LIST="$TARGET_LIST oai_${2,,}devif" # ,, makes lowercase
CMAKE_CMD="$CMAKE_CMD -DOAI_$2=ON"

View File

@@ -84,7 +84,7 @@ function(run_asn1c ASN1C_GRAMMAR ASN1C_PREFIX)
set(LOGFILE "${CMAKE_CURRENT_BINARY_DIR}/${GRAMMAR_FILE}.log")
add_custom_command(OUTPUT ${ASN1C_OUTPUT}
COMMAND ASN1C_PREFIX=${ASN1C_PREFIX} ${ASN1C_EXEC} ${ASN1C_OPTIONS} -D ${CMAKE_CURRENT_BINARY_DIR} ${ASN1C_GRAMMAR} > ${LOGFILE} 2>&1 || ( cat ${LOGFILE} && false )
DEPENDS ${ASN1C_GRAMMAR} asn1c
DEPENDS ${ASN1C_GRAMMAR}
COMMENT "Generating ${ASN1C_COMMENT} from ${GRAMMAR_FILE}"
)
endfunction()

View File

@@ -4,9 +4,9 @@
# -------
#
# Finds the xran library. Note that the library number is as follows:
# - oran_e_maintenance_release_v1.5 -> 5.1.6
# - oran_f_release_v1.3 -> 6.1.4
# - oran_k_release_v1.1 -> 11.1.1
# - oran_bronze_release_v1.1 -> 2.1.1 (B = second letter)
# - oran_e_maintenance_release_v1.0 -> 5.1.0
# the version is currently hardcoded to 5.1.0
#
# Required options
# ^^^^^^^^^^^^^^^^
@@ -46,7 +46,13 @@
# ``xran_LIBRARY``
# The path to the xran library.
set(CACHE{xran_LOCATION} TYPE PATH HELP "directory of XRAN library" VALUE "")
option(xran_LOCATION "directory of XRAN library" "")
if (NOT xran_LOCATION)
message(FATAL_ERROR "xran_LOCATION required")
endif()
if (NOT EXISTS ${xran_LOCATION})
message(FATAL_ERROR "no such directory: ${xran_LOCATION}")
endif()
find_path(xran_INCLUDE_DIR
NAMES
@@ -67,26 +73,27 @@ find_library(xran_LIBRARY
PATH_SUFFIXES build api
NO_DEFAULT_PATH
)
set(xran_VERSION_FILE "${xran_LOCATION}/../app/src/common.h")
if(EXISTS ${xran_VERSION_FILE})
file(STRINGS ${xran_VERSION_FILE} xran_VERSION_LINE REGEX "^#define[ \t]+VERSIONX[ \t]+\"[a-z_.0-9]+\"$")
else()
set(xran_VERSION_LINE "UNKNOWN")
if (NOT xran_LIBRARY)
message(FATAL_ERROR "could not detect xran build artifacts at ${xran_LOCATION}/build")
endif()
set(xran_VERSION_FILE "${xran_LOCATION}/../app/src/common.h")
if(NOT EXISTS ${xran_VERSION_FILE})
message(FATAL_ERROR "could not find xran version file at ${xran_VERSION_FILE}")
endif()
file(STRINGS ${xran_VERSION_FILE} xran_VERSION_LINE REGEX "^#define[ \t]+VERSIONX[ \t]+\"[a-z_.0-9]+\"$")
string(REGEX REPLACE "^#define[ \t]+VERSIONX[ \t]+\"([a-z_.0-9]+)\"$" "\\1" xran_VERSION_STRING "${xran_VERSION_LINE}")
if (xran_VERSION_STRING MATCHES "^oran_e_maintenance_release_v")
string(REGEX REPLACE "oran_e_maintenance_release_v([0-9]+).([0-9]+)" "5.\\1.\\2" xran_VERSION ${xran_VERSION_STRING})
elseif(xran_VERSION_STRING MATCHES "^oran_f_release_v")
string(REGEX REPLACE "oran_f_release_v([0-9]+).([0-9]+)" "6.\\1.\\2" xran_VERSION ${xran_VERSION_STRING})
elseif(xran_VERSION_STRING MATCHES "^oran_k_release_v")
string(REGEX REPLACE "oran_k_release_v([0-9]+).([0-9]+)" "11.\\1.\\2" xran_VERSION ${xran_VERSION_STRING})
elseif(xran_VERSION_STRING MATCHES "^oran_bronze_release_v")
string(REGEX REPLACE "oran_bronze_release_v([0-9]+).([0-9]+)" "2.\\1.\\2" xran_VERSION ${xran_VERSION_STRING})
else()
set(xran_VERSION "UNKNOWN")
message(FATAL_ERROR "unrecognized xran version string: ${xran_VERSION_STRING}")
endif()
message(STATUS "Found xran release ${xran_VERSION_STRING} (v${xran_VERSION})")
unset(xran_VERSION_LINE)
unset(xran_VERSION_STRING)
unset(xran_VERSION_FILE)

View File

@@ -282,9 +282,12 @@ configmodule_interface_t *load_configmodule(int argc,
cfgmode = strdup(CONFIG_LIBCONFIGFILE);
}
}
static configmodule_interface_t *cfgptr = NULL;
if (cfgptr)
fprintf(stderr, "ERROR: Call load_configmodule more than one time\n");
// The macros are not thread safe print_params and similar
configmodule_interface_t *cfgptr = calloc(sizeof(configmodule_interface_t), 1);
cfgptr = calloc(sizeof(configmodule_interface_t), 1);
if (!cfgptr) {
fprintf(stderr, "ERROR: cannot allocate a memory for configuration\n");
return NULL;

View File

@@ -10,9 +10,6 @@
#ifndef INCLUDE_CONFIG_LOADCONFIGMODULE_H
#define INCLUDE_CONFIG_LOADCONFIGMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <stdlib.h>
@@ -120,8 +117,5 @@ void write_parsedcfg(configmodule_interface_t *cfg);
extern void free_configmodule(void);
#define CONFIG_PRINTF_ERROR(f, x... ) if (isLogInitDone ()) { LOG_E(ENB_APP,f,x);} else {printf(f,x);}; if ( !CONFIG_ISFLAGSET(CONFIG_NOABORTONCHKF) ) exit_fun("exit because configuration failed\n");
#ifdef __cplusplus
}
#endif
#endif /* INCLUDE_CONFIG_LOADCONFIGMODULE_H */

View File

@@ -181,11 +181,6 @@ typedef struct paramdef {
{ \
OPTNAME(name), HELPSTR(help), PARAMFLAG(flags), .u64ptr = ptr, .defuintval = defval, PARAMTYPE(TYPE_UINT64), .numelt = 0 \
}
#define STRINGLISTPARAM(name, help, flags, ptr, defval) \
{ \
OPTNAME(name), HELPSTR(help), PARAMFLAG(flags), .strptr = ptr, .defstrlistval = defval, PARAMTYPE(TYPE_STRINGLIST), \
.numelt = 0 \
}
typedef struct paramlist_def {
char listname[MAX_OPTNAME_SIZE];

View File

@@ -16,7 +16,7 @@ add_subdirectory(nr)
add_subdirectory(LOG)
add_subdirectory(threadPool)
add_subdirectory(time_manager)
add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c bits.c fsn.c)
add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c reverse_bits.c fsn.c)
target_include_directories(utils PUBLIC .)
target_link_libraries(utils PRIVATE ${T_LIB})
add_subdirectory(barrier)

View File

@@ -55,7 +55,7 @@ add_dependencies(T_headers T_IDs.h generate_T)
target_include_directories(T_headers INTERFACE ${CMAKE_CURRENT_BINARY_DIR} ${CMAKE_CURRENT_SOURCE_DIR})
if(T_TRACER)
add_library(T STATIC T.c local_tracer.c T_messages_creator.c T_IDs.h T_messages.txt.h)
add_library(T STATIC T.c local_tracer.c T_IDs.h T_messages.txt.h)
target_link_libraries(T PUBLIC rt)
target_link_libraries(T PRIVATE CONFIG_LIB T_headers)

View File

@@ -115,7 +115,7 @@ extern int T_stdout;
/* type used to send arbitrary buffer data */
typedef struct {
const void *addr;
void *addr;
int length;
} T_buffer;

View File

@@ -118,23 +118,23 @@ ID = GNB_PHY_PUCCH_PUSCH_IQ
ID = GNB_PHY_UL_FD_PUSCH_IQ
DESC = gNodeB UL PUSCH IQ Data in the frequency domain
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
ID = GNB_PHY_UL_FD_DMRS
DESC = gNodeB UL DMRS Signal in the frequency domain
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
ID = GNB_PHY_UL_FD_CHAN_EST_DMRS_POS
DESC = gNodeB channel estimation in the frequency domain based on DMRS and at DMRS pilots location without any further interploation.
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
ID = GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
DESC = gNodeB channel estimation in the frequency domain based on DMRS and interploation.
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,dataI
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,dataI
ID = GNB_PHY_UL_PAYLOAD_RX_BITS
DESC = gNodeB slot payload after decoder
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
ID = GNB_PHY_UL_FREQ_CHANNEL_ESTIMATE
DESC = gNodeB channel estimation in the frequency domain
GROUP = ALL:PHY:GRAPHIC:HEAVY:GNB
@@ -1474,11 +1474,11 @@ ID = UE_PHY_MEAS
ID = UE_PHY_UL_PAYLOAD_TX_BITS
DESC = UE UL Tx bits in one slot before decoder and scrambler
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
ID = UE_PHY_UL_SCRAMBLED_TX_BITS
DESC = UE UL Tx bits in one slot after scrambler
GROUP = ALL:PHY:GRAPHIC:HEAVY:UE
FORMAT = int,frame : int,slot : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm : int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot : int,Nid_cell : int,rnti : int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols : int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers : int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port : int,dmrs_nscid : int,nb_antennas_tx : int,number_of_bits : buffer,data
#for debug/test - not used
ID = first

View File

@@ -1,220 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*!
* \brief Helper functions for creating T-Tracer UL PHY trace messages, it is used by data recording application
*/
/*
* Each function logs one UL PHY trace message via the T() macro.
* All messages share the same field format (Data Collection Trace Messages Structure):
* int,frame : int,slot :
* int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
* int,Nid_cell : int,rnti :
* int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
* int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
* int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols : int,dmrs_port :
* int,dmrs_nscid : int,nb_antennas_rx : int,number_of_bits : buffer,data
*/
#include "T.h"
#include "T_messages_creator.h"
#if T_TRACER
// Internal helper: emit the common UL metadata + buffer via T()
// Note: The T() macro internally checks T_ACTIVE(trace_id) before sending,
// so callers do not need an additional T_ACTIVE() guard.
static inline void log_ul_common(int trace_id,
int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
int number_of_bits,
const void *data,
int data_len)
{
T(trace_id,
T_INT((int)frame),
T_INT((int)slot),
T_INT((int)frame_parms->frame_type),
T_INT((int)frame_parms->freq_range),
T_INT((int)rel15_ul->subcarrier_spacing),
T_INT((int)rel15_ul->cyclic_prefix),
T_INT((int)frame_parms->symbols_per_slot),
T_INT((int)frame_parms->Nid_cell),
T_INT((int)rel15_ul->rnti),
T_INT((int)rel15_ul->rb_size),
T_INT((int)rel15_ul->rb_start),
T_INT((int)rel15_ul->start_symbol_index),
T_INT((int)rel15_ul->nr_of_symbols),
T_INT((int)rel15_ul->qam_mod_order),
T_INT((int)rel15_ul->mcs_index),
T_INT((int)rel15_ul->mcs_table),
T_INT((int)rel15_ul->nrOfLayers),
T_INT((int)rel15_ul->transform_precoding),
T_INT((int)rel15_ul->dmrs_config_type),
T_INT((int)rel15_ul->ul_dmrs_symb_pos),
T_INT((int)number_dmrs_symbols),
T_INT((int)dmrs_port),
T_INT((int)rel15_ul->scid),
T_INT((int)frame_parms->nb_antennas_rx),
T_INT((int)number_of_bits),
T_BUFFER(data, data_len));
}
void log_ul_fd_dmrs(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len)
{
log_ul_common(T_GNB_PHY_UL_FD_DMRS,
frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port, 0, data, data_len);
}
void log_ul_fd_chan_est_dmrs_pos(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len)
{
log_ul_common(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port, 0, data, data_len);
}
void log_ul_fd_pusch_iq(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len)
{
log_ul_common(T_GNB_PHY_UL_FD_PUSCH_IQ,
frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port, 0, data, data_len);
}
void log_ul_fd_chan_est_dmrs_interpl(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len)
{
log_ul_common(T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL,
frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port, 0, data, data_len);
}
void log_ul_payload_rx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int tb_size)
{
log_ul_common(T_GNB_PHY_UL_PAYLOAD_RX_BITS,
frame, slot, frame_parms, rel15_ul,
number_dmrs_symbols, dmrs_port,
tb_size << 3, data, tb_size);
}
// UE-side: uses nfapi_nr_ue_pusch_pdu_t, derives subcarrier_spacing index
// from frame_parms, and logs nb_antennas_tx instead of nb_antennas_rx.
void log_ul_payload_tx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int tb_size)
{
int subcarrier_spacing_index = frame_parms->subcarrier_spacing / 15000 - 1;
T(T_UE_PHY_UL_PAYLOAD_TX_BITS,
T_INT((int)frame),
T_INT((int)slot),
T_INT((int)frame_parms->frame_type),
T_INT((int)frame_parms->freq_range),
T_INT((int)subcarrier_spacing_index),
T_INT((int)pusch_pdu->cyclic_prefix),
T_INT((int)frame_parms->symbols_per_slot),
T_INT((int)frame_parms->Nid_cell),
T_INT((int)pusch_pdu->rnti),
T_INT((int)pusch_pdu->rb_size),
T_INT((int)pusch_pdu->rb_start),
T_INT((int)pusch_pdu->start_symbol_index),
T_INT((int)pusch_pdu->nr_of_symbols),
T_INT((int)pusch_pdu->qam_mod_order),
T_INT((int)pusch_pdu->mcs_index),
T_INT((int)pusch_pdu->mcs_table),
T_INT((int)pusch_pdu->nrOfLayers),
T_INT((int)pusch_pdu->transform_precoding),
T_INT((int)pusch_pdu->dmrs_config_type),
T_INT((int)pusch_pdu->ul_dmrs_symb_pos),
T_INT((int)number_dmrs_symbols),
T_INT((int)dmrs_port),
T_INT((int)pusch_pdu->scid),
T_INT((int)frame_parms->nb_antennas_tx),
T_INT((int)(tb_size << 3)),
T_BUFFER(data, tb_size));
}
void log_ul_scrambled_tx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int number_of_bits)
{
int subcarrier_spacing_index = frame_parms->subcarrier_spacing / 15000 - 1;
T(T_UE_PHY_UL_SCRAMBLED_TX_BITS,
T_INT((int)frame),
T_INT((int)slot),
T_INT((int)frame_parms->frame_type),
T_INT((int)frame_parms->freq_range),
T_INT((int)subcarrier_spacing_index),
T_INT((int)pusch_pdu->cyclic_prefix),
T_INT((int)frame_parms->symbols_per_slot),
T_INT((int)frame_parms->Nid_cell),
T_INT((int)pusch_pdu->rnti),
T_INT((int)pusch_pdu->rb_size),
T_INT((int)pusch_pdu->rb_start),
T_INT((int)pusch_pdu->start_symbol_index),
T_INT((int)pusch_pdu->nr_of_symbols),
T_INT((int)pusch_pdu->qam_mod_order),
T_INT((int)pusch_pdu->mcs_index),
T_INT((int)pusch_pdu->mcs_table),
T_INT((int)pusch_pdu->nrOfLayers),
T_INT((int)pusch_pdu->transform_precoding),
T_INT((int)pusch_pdu->dmrs_config_type),
T_INT((int)pusch_pdu->ul_dmrs_symb_pos),
T_INT((int)number_dmrs_symbols),
T_INT((int)dmrs_port),
T_INT((int)pusch_pdu->scid),
T_INT((int)frame_parms->nb_antennas_tx),
T_INT((int)number_of_bits),
T_BUFFER(data, number_of_bits / 8));
}
#endif /* T_TRACER */

View File

@@ -1,195 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
/*!
* \brief Helper functions for creating T-Tracer UL PHY trace messages, it is used by data recording application
*/
#ifndef T_MESSAGES_CREATOR_H
#define T_MESSAGES_CREATOR_H
#if T_TRACER
#include <stdint.h>
#include "common/platform_types.h"
#include "common/utils/nr/nr_common.h"
#include "PHY/defs_nr_common.h"
#include "nfapi_nr_interface_scf.h"
#include "fapi_nr_ue_interface.h"
/**
* @brief Log PUSCH UL DMRS using T-Tracer
*
* This function creates and sends a T_GNB_PHY_UL_FD_DMRS message containing
* PUSCH DMRS symbols in frequency domain.
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param rel15_ul Pointer to PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to DMRS data buffer (c16_t complex samples)
* @param data_len Size of the data buffer in bytes
*/
void log_ul_fd_dmrs(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len);
/**
* @brief Log PUSCH UL channel estimates at DMRS positions using T-Tracer
*
* This function creates and sends a T_GNB_PHY_UL_FD_CHAN_EST_DMRS_POS message
* containing channel estimates at DMRS positions in frequency domain.
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param rel15_ul Pointer to PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to channel estimate data buffer (c16_t complex samples)
* @param data_len Size of the data buffer in bytes
*/
void log_ul_fd_chan_est_dmrs_pos(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len);
/**
* @brief Log PUSCH UL IQ data using T-Tracer
*
* This function creates and sends a T_GNB_PHY_UL_FD_PUSCH_IQ message
* containing PUSCH IQ samples in frequency domain.
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param rel15_ul Pointer to PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to PUSCH IQ data buffer (c16_t complex samples)
* @param data_len Size of the data buffer in bytes
*/
void log_ul_fd_pusch_iq(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len);
/**
* @brief Log PUSCH UL interpolated channel estimates using T-Tracer
*
* This function creates and sends a T_GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL message
* containing interpolated channel estimates across all subcarriers in frequency domain.
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param rel15_ul Pointer to PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to interpolated channel estimate data buffer (c16_t complex samples)
* @param data_len Size of the data buffer in bytes
*/
void log_ul_fd_chan_est_dmrs_interpl(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const c16_t *data,
int data_len);
/**
* @brief Log PUSCH UL received payload bits using T-Tracer
*
* This function creates and sends a T_GNB_PHY_UL_PAYLOAD_RX_BITS message
* containing the decoded transport block payload data.
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param rel15_ul Pointer to PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to decoded transport block data (uint8_t bytes)
* @param tb_size Transport block size in bytes
*/
void log_ul_payload_rx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_pusch_pdu_t *rel15_ul,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int tb_size);
/**
* @brief Log UE PUSCH UL transmitted payload bits using T-Tracer
*
* This function creates and sends a T_UE_PHY_UL_PAYLOAD_TX_BITS message
* containing the transport block payload data before encoding.
* Note: subcarrier_spacing is derived from frame_parms->subcarrier_spacing
* and nb_antennas_tx is used (UE transmit side).
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param pusch_pdu Pointer to UE PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to transport block payload data (uint8_t bytes)
* @param tb_size Transport block size in bytes
*/
void log_ul_payload_tx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int tb_size);
/**
* @brief Log UE PUSCH UL scrambled transmit bits using T-Tracer
*
* This function creates and sends a T_UE_PHY_UL_SCRAMBLED_TX_BITS message
* containing the scrambled codeword bits after scrambling.
* Note: subcarrier_spacing is derived from frame_parms->subcarrier_spacing
* and nb_antennas_tx is used (UE transmit side).
*
* @param frame Frame number
* @param slot Slot number
* @param frame_parms Pointer to frame parameters structure
* @param pusch_pdu Pointer to UE PUSCH PDU structure
* @param number_dmrs_symbols Number of DMRS symbols
* @param dmrs_port DMRS port number
* @param data Pointer to scrambled codeword data (uint8_t bytes)
* @param number_of_bits Number of scrambled bits
*/
void log_ul_scrambled_tx_bits(int frame,
int slot,
const NR_DL_FRAME_PARMS *frame_parms,
const nfapi_nr_ue_pusch_pdu_t *pusch_pdu,
int number_dmrs_symbols,
int dmrs_port,
const uint8_t *data,
int number_of_bits);
#endif /* T_TRACER */
#endif /* T_MESSAGES_CREATOR_H */

View File

@@ -18,13 +18,10 @@
#ifndef _SHARED_MEMORY_CONFIG_H_
#define _SHARED_MEMORY_CONFIG_H_
#define NUM_MESSAGES_PER_SLOT 5
#define SHMSIZE ((122.88e6 / (100 * 20)) * 100 * NUM_MESSAGES_PER_SLOT * 8) // ~229 MiB
// 122.88e6: sample rate at 100 MHz BW of 5G NR
// 100 * 20: 2000 slots/s (20 slots/frame, 100 frames/s) at SCS 30 kHz
// 100: number of slots to capture
// NUM_MESSAGES_PER_SLOT: messages captured per slot
// 8: bytes per complex I+Q sample
#define SHMSIZE ((122.88e6 / (100 * 20)) * 1000 * 8) // Assume capture in 1000, each I+Q represented by 8 byts
// 122.88e6: 10ms at 100 MHz BW of 5G NR
// 20: 20 slots in 10ms
// subcarrier spacing: 30 KHz
// for gNB T Tracer App
#define GETKEYDIR1_gNB ("/tmp/gnb_app1")

View File

@@ -3,7 +3,7 @@
*/
/*!
* \brief T-Tracer gnb service to capture trace Messages from gNB, it is used by data recording application
* \brief T-Tracer gnb service to capture tracee Messages from gNB, it is used by data recording application
*/
#include <stdio.h>
@@ -34,21 +34,6 @@
// the buffer in the stack from the previous record
#define DISCARD_RECORD_DURATION_MS 10
// Sentinel values for field_descriptor: source is e.sending_time
#define FIELD_SENDING_TIME_SEC -2
#define FIELD_SENDING_TIME_NSEC -3
// State machine constants for shared memory protocol
#define STATE_WAIT 0
#define STATE_CONFIG 1
#define STATE_RECORD 2
#define STATE_STOP 3
// Visual separator for state machine transitions in console output
#define STATE_SEPARATOR "========================================"
#define PRINT_STATE_BANNER(state_name) \
printf("\n%s\n [STATE: %s]\n%s\n", STATE_SEPARATOR, state_name, STATE_SEPARATOR)
// Combine bytes in - little-endian format
int combine_bytes(const uint8_t *bytes, size_t num_bytes)
{
@@ -59,11 +44,19 @@ int combine_bytes(const uint8_t *bytes, size_t num_bytes)
return result;
}
// Check if a message ID is present in a given list of message IDs
bool is_message_in_list(int msg_id_list[], int n_msgs, int msg_id)
// Convert an integer to an array of bytes - little-endian format
void int_to_bytes(int num, uint8_t *bytes, size_t num_bytes)
{
for (int i = 0; i < n_msgs; i++) {
if (msg_id == msg_id_list[i]) {
for (size_t i = 0; i < num_bytes; ++i) {
bytes[i] = (num >> (i * 8)) & 0xFF;
}
}
// Check if the message is in the list of bits messages
bool is_bits_messages(int traces_bits_support_data_Collection_format_idx[], int n_bits_msgs, int msg_id)
{
for (int i = 0; i < n_bits_msgs; i++) {
if (msg_id == traces_bits_support_data_Collection_format_idx[i]) {
return true;
}
}
@@ -78,6 +71,12 @@ struct timespec get_current_time()
return time;
}
// Function to convert timespec to microseconds
long long timespec_to_microseconds(struct timespec time)
{
return (time.tv_sec * 1000000LL) + (time.tv_nsec / 1000);
}
// Calculate the time difference in milliseconds
double calculate_time_difference(struct timespec start, struct timespec end)
{
@@ -89,7 +88,7 @@ double calculate_time_difference(struct timespec start, struct timespec end)
// Get Time Stamp in microseconds in YYYYMMDDHHMMSSmmmuuu format
char *get_time_stamp_usec(char time_stamp_str[])
{
// initialization to measure time stamp
// initialization to measure time stamp --This part should be moved to inilization part
time_t my_time;
struct tm *timeinfo;
time(&my_time);
@@ -105,12 +104,31 @@ char *get_time_stamp_usec(char time_stamp_str[])
uint8_t hour = timeinfo->tm_hour;
uint8_t min = timeinfo->tm_min;
uint8_t sec = timeinfo->tm_sec;
uint32_t usec = (tv.tv_usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06u", year, mon, mday, hour, min, sec, usec);
uint16_t usec = (tv.tv_usec);
// printf ("Time stamp: %d_%d_%d_%d_%d_%d_%d \n",year,mon,mday,hour,min,sec,usec);
// sprintf(time_stamp_str, "%d_%d_%d_%d_%d_%d_%d",year,mon,mday,hour,min,sec,usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06d", year, mon, mday, hour, min, sec, usec);
return time_stamp_str;
}
// Convert timestamp string to integer
int convert_time_stamp_to_int(const char *timestamp)
{
return atoi(timestamp);
}
// Split timestamp string and convert to integer
int split_time_stamp_and_convert_to_int(char time_stamp_str[], int shift, int length)
{
char time_part[length + 1]; // Buffer to hold the date part YYYYMMDD or HHMMSSmmm
// Copy the first 8 or 9 characters (YYYYMMDD) to HHMMSSmmm
strncpy(time_part, time_stamp_str + shift, length);
time_part[length] = '\0'; // Null-terminate the string
// Convert timestamp string to integer
return convert_time_stamp_to_int(time_part);
}
void err_exit(char *buf)
{
fprintf(stderr, "%s\n", buf);
@@ -135,7 +153,7 @@ int create_shm(char **addrN, const char *shm_path, int projectId)
shm_id = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0664);
if (shm_id == -1) {
if (errno == EEXIST) {
printf("Error: shared memory already exists\n");
printf("Error: shared memeory already exist\n");
shm_id = shmget(key, 0, 0);
printf("reference shm_id = %d\n", shm_id);
} else {
@@ -192,28 +210,75 @@ void usage(void)
exit(1);
}
// class to store the message and the action to enable capture selected data
// 0: do not record message
// 1: record message
struct trace_struct {
char on_off_name[100];
int on_off_action;
};
// struct for trace message based on Data Collection Trace Messages Structure
// you need to define the variables of each message
// you need to define the vararibles of each message
typedef struct {
/* Data Collection Trace Message Structure */
int frame;
int slot;
int datetime_yyyymmdd;
int datetime_hhmmssmmm;
int frame_type, freq_range, subcarrier_spacing, cyclic_prefix, symbols_per_slot;
int Nid_cell, rnti;
int rb_size, rb_start, start_symbol_index, nr_of_symbols;
int qam_mod_order, mcs_index, mcs_table, nrOfLayers, transform_precoding;
int dmrs_config_type, ul_dmrs_symb_pos, number_dmrs_symbols;
int dmrs_port, dmrs_nscid, nb_antennas_rx, number_of_bits;
int data;
} event_trace_msg_ul_data;
int data_size, data;
} event_trace_msg_data;
void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
{
database_event_format f;
int i;
// Initialize all fields to -1 (marks unset indices)
memset(d, -1, sizeof(*d));
/* Data Collection Trace Message Structure */
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_rx : int,number_of_bits : buffer,data
// Initialize the data structure
d->frame = -1;
d->slot = -1;
d->datetime_yyyymmdd = -1;
d->datetime_hhmmssmmm = -1;
d->frame_type = -1;
d->freq_range = -1;
d->subcarrier_spacing = -1;
d->cyclic_prefix = -1;
d->symbols_per_slot = -1;
d->Nid_cell = -1;
d->rnti = -1;
d->rb_size = -1;
d->rb_start = -1;
d->start_symbol_index = -1;
d->nr_of_symbols = -1;
d->qam_mod_order = -1;
d->mcs_index = -1;
d->mcs_table = -1;
d->nrOfLayers = -1;
d->transform_precoding = -1;
d->dmrs_config_type = -1;
d->ul_dmrs_symb_pos = -1;
d->number_dmrs_symbols = -1;
d->dmrs_port = -1;
d->dmrs_nscid = -1;
d->nb_antennas_rx = -1;
d->number_of_bits = -1;
d->data = -1;
/* this macro looks for a particular element and checks its type */
#define G(var_name, var_type, var) \
@@ -226,14 +291,35 @@ void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
continue; \
}
// Get a template of any UL message based on Data Collection Trace Messages Structure
/* ------------------------------*/
/* Create Macro for Data Collection Trace Message */
/* ------------------------------*/
// Data Collection Trace Message Structure
// Example: GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID,
// GNB_PHY_UL_FD_CHAN_EST_DMRS_POS, GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
// GNB_PHY_UL_PAYLOAD_RX_BITS
// UE_PHY_UL_SCRAMBLED_TX_BITS
// UE_PHY_UL_PAYLOAD_TX_BITS
// Get a template of any message based on Data Collection Trace Messages Structure
int Trace_MSG_ID = event_id_from_name(database, "GNB_PHY_UL_FD_PUSCH_IQ");
f = get_format(database, Trace_MSG_ID);
// Map each field name to its index in the event structure
/* get the elements of the trace
* the value is an index in the event, see below */
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_rx : int,number_of_bits : buffer,data
for (i = 0; i < f.count; i++) {
G("frame", "int", d->frame)
G("slot", "int", d->slot)
G("datetime_yyyymmdd", "int", d->datetime_yyyymmdd)
G("datetime_hhmmssmmm", "int", d->datetime_hhmmssmmm)
G("frame_type", "int", d->frame_type)
G("freq_range", "int", d->freq_range)
G("subcarrier_spacing", "int", d->subcarrier_spacing)
@@ -259,106 +345,20 @@ void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
G("number_of_bits", "int", d->number_of_bits)
G("data", "buffer", d->data)
}
// if (d->frame == -1 || d->slot == -1) goto error;
#undef G
return;
}
// -------------------------------------------------------------------
// Table-driven metadata shared memory writer and debug printer
// -------------------------------------------------------------------
// Descriptor for one metadata field to write to shared memory
typedef struct {
int field_idx; // index into event e.e[] array
size_t wire_size; // number of bytes to write to shm
} field_descriptor;
// Number of UL metadata fields written to shared memory (excludes msg_id and buffer)
#define N_UL_METADATA_FIELDS 27
// Build UL field descriptor array from a populated event_trace_msg_ul_data struct.
// Must be called after setup_trace_msg_ul_data() so that field indices are populated.
void build_ul_field_descriptors(field_descriptor *fields, const event_trace_msg_ul_data *d)
// Function to check if a value is in the array
int isValueInArray(int value, int arr[], int size)
{
int i = 0;
fields[i++] = (field_descriptor){ d->frame, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->slot, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ FIELD_SENDING_TIME_SEC, sizeof(uint32_t) };
fields[i++] = (field_descriptor){ FIELD_SENDING_TIME_NSEC, sizeof(uint32_t) };
fields[i++] = (field_descriptor){ d->frame_type, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->freq_range, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->subcarrier_spacing, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->cyclic_prefix, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->symbols_per_slot, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->Nid_cell, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rnti, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rb_size, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rb_start, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->start_symbol_index, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->nr_of_symbols, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->qam_mod_order, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->mcs_index, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->mcs_table, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->nrOfLayers, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->transform_precoding, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->dmrs_config_type, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->ul_dmrs_symb_pos, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->number_dmrs_symbols, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->dmrs_port, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->dmrs_nscid, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->nb_antennas_rx, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->number_of_bits, sizeof(uint32_t) };
}
// Print UL metadata fields for debug output
void print_ul_metadata_debug(event e, const event_trace_msg_ul_data *d, void *database)
{
printf("\nUnix TS: %ld.%09ld", e.sending_time.tv_sec, e.sending_time.tv_nsec);
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
printf("frame %d, slot %d, unix_ts %ld.%09ld\n",
e.e[d->frame].i, e.e[d->slot].i,
e.sending_time.tv_sec, e.sending_time.tv_nsec);
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
e.e[d->frame_type].i, e.e[d->freq_range].i,
e.e[d->subcarrier_spacing].i, e.e[d->cyclic_prefix].i,
e.e[d->symbols_per_slot].i);
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
e.e[d->Nid_cell].i, e.e[d->rnti].i,
e.e[d->rb_size].i, e.e[d->rb_start].i,
e.e[d->start_symbol_index].i, e.e[d->nr_of_symbols].i);
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
e.e[d->qam_mod_order].i, e.e[d->mcs_index].i,
e.e[d->mcs_table].i, e.e[d->nrOfLayers].i,
e.e[d->transform_precoding].i);
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
e.e[d->dmrs_config_type].i, e.e[d->ul_dmrs_symb_pos].i,
e.e[d->number_dmrs_symbols].i, e.e[d->dmrs_port].i,
e.e[d->dmrs_nscid].i);
printf("nb_antennas_rx %d, number_of_bits %d, data size %d\n",
e.e[d->nb_antennas_rx].i, e.e[d->number_of_bits].i,
e.e[d->data].bsize);
}
// Write metadata fields to shared memory using field descriptor table.
// Writes msg_id first, then loops through all fields.
void write_metadata_to_shm(char *addr_wr, unsigned int *bufIdx_wr, event e,
const field_descriptor *fields, int n_fields)
{
// Write message ID
memcpy(&addr_wr[*bufIdx_wr], &e.type, sizeof(uint16_t));
*bufIdx_wr += sizeof(uint16_t);
// Write all metadata fields
for (int i = 0; i < n_fields; i++) {
if (fields[i].field_idx == FIELD_SENDING_TIME_SEC) {
uint32_t ts_sec = (uint32_t)e.sending_time.tv_sec;
memcpy(&addr_wr[*bufIdx_wr], &ts_sec, fields[i].wire_size);
} else if (fields[i].field_idx == FIELD_SENDING_TIME_NSEC) {
uint32_t ts_nsec = (uint32_t)e.sending_time.tv_nsec;
memcpy(&addr_wr[*bufIdx_wr], &ts_nsec, fields[i].wire_size);
} else {
memcpy(&addr_wr[*bufIdx_wr], &e.e[fields[i].field_idx].i, fields[i].wire_size);
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
return 1; // Value found
}
*bufIdx_wr += fields[i].wire_size;
}
return 0; // Value not found
}
void reestablish_connection(int *socket, char *ip, int port, int number_of_events, int *is_on)
@@ -386,24 +386,40 @@ int main(int n, char **v)
// trace_msgs_support_data_Collection_format
// it is used to parse the requested messages if it is based
// on Data Collection Trace Messages Structure and supported tracer messages indices
char *traces_ul_support_data_Collection_format[] = {"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
"GNB_PHY_UL_PAYLOAD_RX_BITS",
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"};
// extra number of records to simplify sync between base station and UE synchronization records.
char *traces_iq_support_data_Collection_format[] = {"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"};
char *traces_bits_support_data_Collection_format[] = {"GNB_PHY_UL_PAYLOAD_RX_BITS",
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"};
// extra number of records to simlify Sync between base station and UE synchronization records.
// if you have network delay, you can increase the number of records to capture
int max_sync_offset = 6; // 6 frames ~ 60 ms
// Calculate the size of the message array
int n_ul_msgs = sizeof(traces_ul_support_data_Collection_format) / sizeof(traces_ul_support_data_Collection_format[0]);
// all supported messages
// Calculate the size of the combined array
int n_iq_msgs = sizeof(traces_iq_support_data_Collection_format) / sizeof(traces_iq_support_data_Collection_format[0]);
int n_bits_msgs = sizeof(traces_bits_support_data_Collection_format) / sizeof(traces_bits_support_data_Collection_format[0]);
int n_msgs_based_data_Collection_format = n_iq_msgs + n_bits_msgs;
// Create the combined array
char *traces_support_data_Collection_format[n_msgs_based_data_Collection_format];
// Copy IQ messages to the combined array
for (int i = 0; i < n_iq_msgs; i++) {
traces_support_data_Collection_format[i] = traces_iq_support_data_Collection_format[i];
}
// Copy Bits messages to the combined array
for (int i = 0; i < n_bits_msgs; i++) {
traces_support_data_Collection_format[i + n_iq_msgs] = traces_bits_support_data_Collection_format[i];
}
uint16_t msg_id = 0;
uint16_t start_frame_number = 0;
uint32_t number_records = 0; // number of records to capture, it is number of slots
// array to store the requested tracer messages indices
int req_tracer_msgs_indices[100] = {0};
// define variables --> to do: add all of them to be class of pointers
char *database_filename = NULL;
void *database;
char *ip = DEFAULT_REMOTE_IP;
@@ -416,19 +432,18 @@ int main(int n, char **v)
char trace_time_stamp_str[30];
// data structure for the trace messages based on Data Collection Trace Messages Structure
event_trace_msg_ul_data trace_msg_ul_data;
event_trace_msg_data trace_msg_data;
// initialization variables
// initlization variables
unsigned int bufIdx_wr = 0;
unsigned int bufIdx_rd = 0;
uint8_t num_req_tracer_msgs = 0;
// initialize shared memory
// initilaze shared memory
char *addr_wr, *addr_rd;
PRINT_STATE_BANNER("INIT");
printf(" Data Collection Service: Initializing shared memory ...\n");
printf(" Directory 1: %s, Directory 2: %s\n", GETKEYDIR1_gNB, GETKEYDIR2_gNB);
printf(" Project ID: %d\n", PROJECTID_gNB);
printf("\n Data Collection Service: Initializing shared memory ...");
printf("\n Directory 1: %s, Directory 2: %s", GETKEYDIR1_gNB, GETKEYDIR2_gNB);
printf("\n Project ID: %d\n", PROJECTID_gNB);
int shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_gNB, PROJECTID_gNB);
int shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_gNB, PROJECTID_gNB);
del_shm(addr_wr, shm_id_wr);
@@ -485,18 +500,20 @@ int main(int n, char **v)
addr_rd[0] = 0; // important to check if we have new requested messages
// read requested tracer msg indices from memory
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for config message ...\n");
printf("\n Data Collection Service: Waiting for messages request ...\n");
// 0: Wait
// 1: config
// 2: record
// 3: stop
// Wait for Action Config
while (1) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
if ((uint8_t)(addr_rd[0]) == 0) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// config state
else if ((uint8_t)(addr_rd[0]) == STATE_CONFIG) {
else if ((uint8_t)(addr_rd[0]) == 1) {
get_time_stamp_usec(trace_time_stamp_str);
PRINT_STATE_BANNER("CONFIG");
printf(" Received config message. Time Stamp: %s\n", trace_time_stamp_str);
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
// get the IP address length in bytes
bufIdx_rd = 1;
@@ -513,11 +530,11 @@ int main(int n, char **v)
// get the array bytes of the port number : 2 bytes
uint8_t port_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd + 1]};
bufIdx_rd += 2; // + 2 bytes = port number
bufIdx_rd += 2; // + 2 bytes = start frame number
port = combine_bytes(port_number_bytes, 2);
printf(" Parameters: IP Address length: %d, IP Address: %s, Port Number: %d\n", ip_address_length, ip_address, port);
addr_rd[0] = STATE_WAIT; // reset memory : to wait for record action
printf("\n Parameters: IP Address length: %d, IP Address: %s, Port Number: %d \n", ip_address_length, ip_address, port);
addr_rd[0] = 0; // reset memory : to wait for record action
break;
}
}
@@ -528,39 +545,37 @@ int main(int n, char **v)
/* connect to the nr-softmodem */
socket = connect_to(ip, port);
printf(" Connected to nr-softmodem\n");
printf("\n Connected to nr-softmodem");
// Read Action record or stop
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for record/stop message ...\n");
printf("\n Data Collection Service: Waiting for record message ...\n");
while (1) {
// wait for Action record
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
if ((uint8_t)(addr_rd[0]) == 0) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// quit state
else if ((uint8_t)(addr_rd[0]) == STATE_STOP) {
PRINT_STATE_BANNER("STOP");
printf(" Received 'stop' command. Exiting...\n");
else if ((uint8_t)(addr_rd[0]) == 3) {
printf("\n Received 'stop' command. Exiting...");
printf("\n ");
// Clean up and exit
break;
}
// record state
else if ((uint8_t)(addr_rd[0]) == STATE_RECORD) {
else if ((uint8_t)(addr_rd[0]) == 2) {
// clear remote buffer if there is
clear_remote_config();
get_time_stamp_usec(trace_time_stamp_str);
PRINT_STATE_BANNER("RECORD");
printf(" Received record message. Time Stamp: %s\n", trace_time_stamp_str);
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
// read number of requested messages
bufIdx_rd = 1;
num_req_tracer_msgs = addr_rd[bufIdx_rd];
bufIdx_rd += 1;
printf(" Number of requested tracer messages: %d\n", num_req_tracer_msgs);
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
// reset memory : action to wait for next record action
addr_rd[0] = STATE_WAIT;
addr_rd[0] = 0;
// read tracer msg IDs - every message ID has been stored in two bytes
for (uint8_t msg_n = 0; msg_n < num_req_tracer_msgs; msg_n++) {
@@ -592,36 +607,40 @@ int main(int n, char **v)
printf("start_frame: %d\n", start_frame_number);
/* activate the trace in this array */
printf(" Activating tracer messages:\n");
printf("\n Activate Tracer messages in on_off array: ");
for (i = 0; i < num_req_tracer_msgs; i++) {
char *on_off_name = event_name_from_id(database, req_tracer_msgs_indices[i]);
on_off(database, on_off_name, is_on, 1);
printf(" %d: %s\n", req_tracer_msgs_indices[i], on_off_name);
printf("%d, %s, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
}
// Build UL message ID array for generic UL dispatch
int ul_msg_ids[n_ul_msgs];
for (int i = 0; i < n_ul_msgs; i++) {
ul_msg_ids[i] = event_id_from_name(database, traces_ul_support_data_Collection_format[i]);
// get the event IDs for the bit messages
int traces_bits_support_data_Collection_format_idx[n_bits_msgs];
for (int i = 0; i < n_bits_msgs; i++) {
traces_bits_support_data_Collection_format_idx[i] =
event_id_from_name(database, traces_bits_support_data_Collection_format[i]);
}
// all supported messages
int traces_support_data_Collection_format_idx[n_msgs_based_data_Collection_format];
for (int i = 0; i < n_msgs_based_data_Collection_format; i++) {
traces_support_data_Collection_format_idx[i] = event_id_from_name(database, traces_support_data_Collection_format[i]);
}
// setup data for the trace messages
setup_trace_msg_ul_data(&trace_msg_ul_data, database);
printf(" Setup UL trace data done\n");
// Build field descriptor table for table-driven shared memory writes
field_descriptor ul_fields[N_UL_METADATA_FIELDS];
build_ul_field_descriptors(ul_fields, &trace_msg_ul_data);
setup_trace_msg_data(&trace_msg_data, database);
printf("\n Setup Trace Data Done");
// Get the start time
struct timespec start_time = get_current_time();
/* activate the traces in the nr-softmodem */
/* activate the tracee in the nr-softmodem */
activate_traces(socket, number_of_events, is_on);
printf(" Activated traces in nr-softmodem\n");
printf("\n Activated Traces in nr-softmodem");
/* a buffer needed to receive events from the nr-softmodem */
OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL};
OBUF ebuf = {osize : 0, omaxsize : 0, obuf : NULL};
/* read events */
int nrecord_idx = 0;
@@ -636,8 +655,8 @@ int main(int n, char **v)
int current_frame = 0, prev_frame = 0, current_slot = 0, prev_slot = 0;
// offset to sync between base station and UE synchronization records or power measurements
int sync_offset_index = 0; // increase the index only if the index of frame changes after getting all records
// since we will use the frame difference to do extra records, we should be sure that the last slot is recorded completely
printf("\n Data Collection Service: Start reading messages ...\n");
// since we will use the frame differece to do extra records, we should be sure that the last slot is recorded completely
printf("\n\n Data Collection Service: Start reading messages ...");
struct pollfd event_poll_fd;
event_poll_fd.fd = socket;
event_poll_fd.events = POLLIN;
@@ -657,24 +676,27 @@ int main(int n, char **v)
reestablish_connection(&socket, ip, port, number_of_events, is_on);
continue; // Skip further processing and retry
}
//-------------------------
// GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID, GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
// UE_PHY_UL_SCRAMBLED_TX_BITS, GNB_PHY_UL_PAYLOAD_RX_BITS, UE_PHY_UL_PAYLOAD_TX_BITS
//-------------------------
// is it a requested message
if (is_message_in_list(req_tracer_msgs_indices, num_req_tracer_msgs, e.type)) {
if (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
// is it based on Data Collection Trace Messages Structure
if (is_message_in_list(ul_msg_ids, n_ul_msgs, e.type)) {
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
// Start recording from the next slot to mitigate capturing partial data
// check if the current frame and slot are different from the previous frame and slot
// Then, increase the record index
if (start_recording == false) {
if (got_ref_frame_slot == false) {
ref_frame = e.e[trace_msg_ul_data.frame].i;
ref_slot = e.e[trace_msg_ul_data.slot].i;
ref_frame = e.e[trace_msg_data.frame].i;
ref_slot = e.e[trace_msg_data.slot].i;
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
got_ref_frame_slot = true;
}
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_data.slot].i;
current_frame = e.e[trace_msg_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
frame_difference = (current_frame - ref_frame + MAX_FRAME_INDEX + 1) % (MAX_FRAME_INDEX + 1);
slot_difference = (current_slot - ref_slot + MAX_SLOT_INDEX + 1) % (MAX_SLOT_INDEX + 1);
printf("\n First frame.slot: %d.%d, current frame.slot: %d.%d, diff frame.slot: %d.%d",
@@ -687,30 +709,129 @@ int main(int n, char **v)
if ((ref_frame != current_frame) || (ref_slot != current_slot)) {
start_recording = true;
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_ul_data.frame].i, e.e[trace_msg_ul_data.slot].i);
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_data.frame].i, e.e[trace_msg_data.slot].i);
}
}
// start recording from the next frame to mitigate capturing partial data
if (start_recording == true) {
/* this is how to access the elements of the Data Collection trace messages.
* we use e.e[<element>] and then the correct suffix, here
* it's .i for the integer and .b for the buffer and .bsize for the buffer size
* see in event.h the structure event_arg
*/
unsigned char *buf = e.e[trace_msg_data.data].b;
printf("\n\nRecord number: %d", nrecord_idx);
#ifdef DEBUG_BUFFER
printf("\nBuffer index in bytes: %d", bufIdx_wr);
#endif
// --- UL IQ/Bits handler ---
// Write metadata + buffer to shared memory
write_metadata_to_shm(addr_wr, &bufIdx_wr, e, ul_fields, N_UL_METADATA_FIELDS);
// Write buffer: size (uint32_t) + raw data
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_ul_data.data].bsize, sizeof(uint32_t));
// add general message header: message ID,
// T-Tracer Message format
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_rx : int,number_of_bits : buffer,data
memcpy(&addr_wr[bufIdx_wr], &e.type, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.slot].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_yyyymmdd].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
memcpy(&addr_wr[bufIdx_wr], e.e[trace_msg_ul_data.data].b, e.e[trace_msg_ul_data.data].bsize);
bufIdx_wr += e.e[trace_msg_ul_data.data].bsize;
#ifdef DEBUG_T_Tracer
print_ul_metadata_debug(e, &trace_msg_ul_data, database);
#endif
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_hhmmssmmm].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame_type].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.freq_range].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.subcarrier_spacing].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.cyclic_prefix].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.symbols_per_slot].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.Nid_cell].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rnti].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_size].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_start].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.start_symbol_index].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nr_of_symbols].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.qam_mod_order].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_index].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_table].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nrOfLayers].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.transform_precoding].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_config_type].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.ul_dmrs_symb_pos].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_dmrs_symbols].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_port].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_nscid].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nb_antennas_rx].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_of_bits].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
printf("\nTime Stamp: %d_%d", e.e[trace_msg_data.datetime_yyyymmdd].i, e.e[trace_msg_data.datetime_hhmmssmmm].i);
// add message body: length in bytes + recorded data
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.data].bsize, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
// read data from buffer and convert from unsigned char * array to int 16 using the right endianness
// T-tracer: Little Endian
// For BITS Messages, example: UE_PHY_UL_SCRAMBLED_TX_BITS: data in bytes
if (is_bits_messages(traces_bits_support_data_Collection_format_idx, n_bits_msgs, e.type)) {
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx += 1) {
// printf("%d, ", buf[byte_idx]);
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
}
} else {
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx += 2) {
// For a little-endian system:
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx + 1], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
}
}
/*
for (int byte_idx_i = 0; byte_idx_i < e.e[d.nr_ul_fd_dmrs_data].bsize; byte_idx_i+=4) {
int16_t I = buf[byte_idx_i] | (buf[byte_idx_i+1] << 8);
int16_t Q = buf[byte_idx_i+2] | (buf[byte_idx_i+3] << 8);
printf ("idx %d, ", byte_idx_i);
printf ("\n%d", I);
printf ("\n%d", Q);
}
*/
// check if the current frame and slot are different from the previous frame and slot
// Then, increase the record index
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_data.slot].i;
current_frame = e.e[trace_msg_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
// increase sync offset index if the current frame is different from the previous frame
if ((current_frame != prev_frame) && nrecord_idx >= number_records) {
@@ -723,7 +844,43 @@ int main(int n, char **v)
prev_frame = current_frame;
prev_slot = current_slot;
}
#ifdef DEBUG_T_Tracer
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
printf("frame %d, slot %d, datetime %d_%d\n",
e.e[trace_msg_data.frame].i,
e.e[trace_msg_data.slot].i,
e.e[trace_msg_data.datetime_yyyymmdd].i,
e.e[trace_msg_data.datetime_hhmmssmmm].i);
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
e.e[trace_msg_data.frame_type].i,
e.e[trace_msg_data.freq_range].i,
e.e[trace_msg_data.subcarrier_spacing].i,
e.e[trace_msg_data.cyclic_prefix].i,
e.e[trace_msg_data.symbols_per_slot].i);
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
e.e[trace_msg_data.Nid_cell].i,
e.e[trace_msg_data.rnti].i,
e.e[trace_msg_data.rb_size].i,
e.e[trace_msg_data.rb_start].i,
e.e[trace_msg_data.start_symbol_index].i,
e.e[trace_msg_data.nr_of_symbols].i);
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
e.e[trace_msg_data.qam_mod_order].i,
e.e[trace_msg_data.mcs_index].i,
e.e[trace_msg_data.mcs_table].i,
e.e[trace_msg_data.nrOfLayers].i,
e.e[trace_msg_data.transform_precoding].i);
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
e.e[trace_msg_data.dmrs_config_type].i,
e.e[trace_msg_data.ul_dmrs_symb_pos].i,
e.e[trace_msg_data.number_dmrs_symbols].i,
e.e[trace_msg_data.dmrs_port].i,
e.e[trace_msg_data.dmrs_nscid].i);
printf("nb_antennas_rx %d, number_of_bits %d, data size %d\n",
e.e[trace_msg_data.nb_antennas_rx].i,
e.e[trace_msg_data.number_of_bits].i,
e.e[trace_msg_data.data].bsize);
#endif
} // End of start recording flag
} // end of if statement for the supported messages based on Data Collection Trace Messages Structure
else {
@@ -734,28 +891,29 @@ int main(int n, char **v)
} // end while loop of reading events
else {
// No data, just loop and check time
usleep(50); // optional: avoid busy-waiting
usleep(100); // optional: avoid busy-waiting
}
} // End of while loop to read events
// de-activate the traces in the nr-softmodem
printf("\n De-activating tracer messages\n");
// de-activate the tracee in the nr-softmodem
printf("\n De-activated Tracer message:\n");
for (i = 0; i < num_req_tracer_msgs; i++) {
char *on_off_name = event_name_from_id(database, req_tracer_msgs_indices[i]);
on_off(database, on_off_name, is_on, 0);
// printf("\n %d, %s, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
}
// De-activate the tracee in the nr-softmodem
activate_traces(socket, number_of_events, is_on);
printf(" De-activated traces\n");
printf("\n De-activated Traces");
// Get the end time
struct timespec end_time = get_current_time();
// Calculate the time difference
double time_diff = calculate_time_difference(start_time, end_time);
printf(" Total time: %.2f ms\n", time_diff);
printf(" Time per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
printf("Total Time difference: %.2f ms\n", time_diff);
printf("Time difference per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
//-----------------------------
// discard stale or previous record data for the first DISCARD_RECORD_DURATION_MS
//-----------------------------
struct timespec record_start, record_now;
clock_gettime(CLOCK_MONOTONIC, &record_start);
@@ -776,11 +934,12 @@ int main(int n, char **v)
}
} else {
// No data, just loop and check time
usleep(50); // optional: avoid busy-waiting
usleep(100); // optional: avoid busy-waiting
}
} // End of while loop to discard stale records
}
} // End of while loop for record/stop
} // End a while loop to check for the "stop" command
// de-activate the tracee in the nr-softmodem
free_database(database);
free(is_on);

View File

@@ -3,7 +3,7 @@
*/
/*!
* \brief T-Tracer UE service to capture trace Messages from UE, it is used by data recording application
* \brief T-Tracer UE service to capture tracee Messages from UE, it is used by data recording application
*/
#include <stdio.h>
@@ -32,21 +32,7 @@
#define DEBUG_BUFFER
// Duration to discard recording in milliseconds to mitigate stale or previous record data
#define DISCARD_RECORD_DURATION_MS 10
// Sentinel values for field_descriptor: source is e.sending_time
#define FIELD_SENDING_TIME_SEC -2
#define FIELD_SENDING_TIME_NSEC -3
// State machine constants for shared memory protocol
#define STATE_WAIT 0
#define STATE_CONFIG 1
#define STATE_RECORD 2
#define STATE_STOP 3
// Visual separator for state machine transitions in console output
#define STATE_SEPARATOR "========================================"
#define PRINT_STATE_BANNER(state_name) \
printf("\n%s\n [STATE: %s]\n%s\n", STATE_SEPARATOR, state_name, STATE_SEPARATOR)
// #include <linux/time.h>
// Combine bytes in little-endian format
int combine_bytes(const uint8_t *bytes, size_t num_bytes)
@@ -58,11 +44,19 @@ int combine_bytes(const uint8_t *bytes, size_t num_bytes)
return result;
}
// Check if a message ID is present in an array of message IDs (generic array lookup)
bool is_message_in_list(int msg_id_list[], int list_size, int msg_id)
// Convert an integer to an array of bytes in little-endian format
void int_to_bytes(int num, uint8_t *bytes, size_t num_bytes)
{
for (int i = 0; i < list_size; i++) {
if (msg_id == msg_id_list[i]) {
for (size_t i = 0; i < num_bytes; ++i) {
bytes[i] = (num >> (i * 8)) & 0xFF;
}
}
// Check if the message is in the list of bits messages
bool is_bits_messages(int traces_bits_support_data_Collection_format_idx[], int n_bits_msgs, int msg_id)
{
for (int i = 0; i < n_bits_msgs; i++) {
if (msg_id == traces_bits_support_data_Collection_format_idx[i]) {
return true;
}
}
@@ -77,6 +71,12 @@ struct timespec get_current_time()
return time;
}
// Function to convert timespec to microseconds
long long timespec_to_microseconds(struct timespec time)
{
return (time.tv_sec * 1000000LL) + (time.tv_nsec / 1000);
}
// Calculate the time difference in milliseconds
double calculate_time_difference(struct timespec start, struct timespec end)
{
@@ -88,7 +88,7 @@ double calculate_time_difference(struct timespec start, struct timespec end)
// Get Time Stamp in microseconds in YYYYMMDDHHMMSSmmmuuu format
char *get_time_stamp_usec(char time_stamp_str[])
{
// initialization to measure time stamp
// initialization to measure time stamp --This part should be moved to inilization part
time_t my_time;
struct tm *timeinfo;
time(&my_time);
@@ -104,12 +104,31 @@ char *get_time_stamp_usec(char time_stamp_str[])
uint8_t hour = timeinfo->tm_hour;
uint8_t min = timeinfo->tm_min;
uint8_t sec = timeinfo->tm_sec;
uint32_t usec = (tv.tv_usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06u", year, mon, mday, hour, min, sec, usec);
uint16_t usec = (tv.tv_usec);
// printf ("Time stamp: %d_%d_%d_%d_%d_%d_%d \n",year,mon,mday,hour,min,sec,usec);
// sprintf(time_stamp_str, "%d_%d_%d_%d_%d_%d_%d",year,mon,mday,hour,min,sec,usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06d", year, mon, mday, hour, min, sec, usec);
return time_stamp_str;
}
// Convert timestamp string to integer
int convert_time_stamp_to_int(const char *timestamp)
{
return atoi(timestamp);
}
// Split timestamp string and convert to integer
int split_time_stamp_and_convert_to_int(char time_stamp_str[], int shift, int length)
{
char time_part[length + 1]; // Buffer to hold the date part YYYYMMDD or HHMMSSmmm
// Copy the first 8 or 9 characters (YYYYMMDD) to HHMMSSmmm
strncpy(time_part, time_stamp_str + shift, length);
time_part[length] = '\0'; // Null-terminate the string
// Convert timestamp string to integer
return convert_time_stamp_to_int(time_part);
}
void err_exit(char *buf)
{
fprintf(stderr, "%s\n", buf);
@@ -134,7 +153,7 @@ int create_shm(char **addrN, const char *shm_path, int projectId)
shm_id = shmget(key, SHMSIZE, IPC_CREAT | IPC_EXCL | 0664);
if (shm_id == -1) {
if (errno == EEXIST) {
printf("Error: shared memory already exists\n");
printf("Error: shared memeory already exist\n");
shm_id = shmget(key, 0, 0);
printf("reference shm_id = %d\n", shm_id);
} else {
@@ -191,28 +210,75 @@ void usage(void)
exit(1);
}
// class to store the message and the action to enable capture selected data
// 0: do not record message
// 1: record message
struct trace_struct {
char on_off_name[100];
int on_off_action;
};
// struct for trace message based on Data Collection Trace Messages Structure
// you need to define the variables of each message
// you need to define the vararibles of each message
typedef struct {
/* Data Collection Trace Message Structure */
int frame;
int slot;
int datetime_yyyymmdd;
int datetime_hhmmssmmm;
int frame_type, freq_range, subcarrier_spacing, cyclic_prefix, symbols_per_slot;
int Nid_cell, rnti;
int rb_size, rb_start, start_symbol_index, nr_of_symbols;
int qam_mod_order, mcs_index, mcs_table, nrOfLayers, transform_precoding;
int dmrs_config_type, ul_dmrs_symb_pos, number_dmrs_symbols;
int dmrs_port, dmrs_nscid, nb_antennas_tx, number_of_bits;
int data;
} event_trace_msg_ul_data;
int data_size, data;
} event_trace_msg_data;
void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
{
database_event_format f;
int i;
// Initialize all fields to -1 (marks unset indices)
memset(d, -1, sizeof(*d));
/* Data Collection Trace Message Structure */
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_tx : int,number_of_bits : buffer,data
// Initialize the data structure
d->frame = -1;
d->slot = -1;
d->datetime_yyyymmdd = -1;
d->datetime_hhmmssmmm = -1;
d->frame_type = -1;
d->freq_range = -1;
d->subcarrier_spacing = -1;
d->cyclic_prefix = -1;
d->symbols_per_slot = -1;
d->Nid_cell = -1;
d->rnti = -1;
d->rb_size = -1;
d->rb_start = -1;
d->start_symbol_index = -1;
d->nr_of_symbols = -1;
d->qam_mod_order = -1;
d->mcs_index = -1;
d->mcs_table = -1;
d->nrOfLayers = -1;
d->transform_precoding = -1;
d->dmrs_config_type = -1;
d->ul_dmrs_symb_pos = -1;
d->number_dmrs_symbols = -1;
d->dmrs_port = -1;
d->dmrs_nscid = -1;
d->nb_antennas_tx = -1;
d->number_of_bits = -1;
d->data = -1;
/* this macro looks for a particular element and checks its type */
#define G(var_name, var_type, var) \
@@ -225,14 +291,35 @@ void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
continue; \
}
// Get a template of any UL message based on Data Collection Trace Messages Structure
/* ------------------------------*/
/* Create Macro for Data Collection Trace Message */
/* ------------------------------*/
// Data Collection Trace Message Structure
// Example: GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID,
// GNB_PHY_UL_FD_CHAN_EST_DMRS_POS, GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
// GNB_PHY_UL_PAYLOAD_RX_BITS
// UE_PHY_UL_SCRAMBLED_TX_BITS
// UE_PHY_UL_PAYLOAD_TX_BITS
// Get a template of any message based on Data Collection Trace Messages Structure
int Trace_MSG_ID = event_id_from_name(database, "GNB_PHY_UL_FD_PUSCH_IQ");
f = get_format(database, Trace_MSG_ID);
// Map each field name to its index in the event structure
/* get the elements of the trace
* the value is an index in the event, see below */
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_tx : int,number_of_bits : buffer,data
for (i = 0; i < f.count; i++) {
G("frame", "int", d->frame)
G("slot", "int", d->slot)
G("datetime_yyyymmdd", "int", d->datetime_yyyymmdd)
G("datetime_hhmmssmmm", "int", d->datetime_hhmmssmmm)
G("frame_type", "int", d->frame_type)
G("freq_range", "int", d->freq_range)
G("subcarrier_spacing", "int", d->subcarrier_spacing)
@@ -260,108 +347,20 @@ void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
G("number_of_bits", "int", d->number_of_bits)
G("data", "buffer", d->data)
}
// if (d->frame == -1 || d->slot == -1) goto error;
#undef G
return;
}
// -------------------------------------------------------------------
// Table-driven UL metadata shared memory writer and debug printer
// -------------------------------------------------------------------
// Number of UL metadata fields written to shared memory (excludes msg_id and buffer)
#define N_UL_METADATA_FIELDS 27
// Descriptor for one metadata field to write to shared memory
typedef struct {
int field_idx; // index into event e.e[] array
size_t wire_size; // number of bytes to write to shm
} field_descriptor;
// Build UL field descriptor array from a populated event_trace_msg_ul_data struct.
// Must be called after setup_trace_msg_ul_data() so that field indices are populated.
// Note: nb_antennas_tx maps to database field "nb_antennas_rx" (see G macro workaround above)
void build_ul_field_descriptors(field_descriptor *fields, const event_trace_msg_ul_data *d)
// Function to check if a value is in the array
int isValueInArray(int value, int arr[], int size)
{
int i = 0;
fields[i++] = (field_descriptor){ d->frame, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->slot, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ FIELD_SENDING_TIME_SEC, sizeof(uint32_t) };
fields[i++] = (field_descriptor){ FIELD_SENDING_TIME_NSEC, sizeof(uint32_t) };
fields[i++] = (field_descriptor){ d->frame_type, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->freq_range, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->subcarrier_spacing, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->cyclic_prefix, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->symbols_per_slot, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->Nid_cell, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rnti, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rb_size, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->rb_start, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->start_symbol_index, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->nr_of_symbols, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->qam_mod_order, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->mcs_index, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->mcs_table, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->nrOfLayers, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->transform_precoding, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->dmrs_config_type, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->ul_dmrs_symb_pos, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->number_dmrs_symbols, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->dmrs_port, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->dmrs_nscid, sizeof(uint16_t) };
fields[i++] = (field_descriptor){ d->nb_antennas_tx, sizeof(uint8_t) };
fields[i++] = (field_descriptor){ d->number_of_bits, sizeof(uint32_t) };
}
// Print UL metadata fields for debug output
// Note: nb_antennas_tx is the UE transmit antenna count (maps to database field "nb_antennas_rx")
void print_ul_metadata_debug(event e, const event_trace_msg_ul_data *d, void *database)
{
printf("\nUnix TS: %ld.%09ld", e.sending_time.tv_sec, e.sending_time.tv_nsec);
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
printf("frame %d, slot %d, unix_ts %ld.%09ld\n",
e.e[d->frame].i, e.e[d->slot].i,
e.sending_time.tv_sec, e.sending_time.tv_nsec);
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
e.e[d->frame_type].i, e.e[d->freq_range].i,
e.e[d->subcarrier_spacing].i, e.e[d->cyclic_prefix].i,
e.e[d->symbols_per_slot].i);
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
e.e[d->Nid_cell].i, e.e[d->rnti].i,
e.e[d->rb_size].i, e.e[d->rb_start].i,
e.e[d->start_symbol_index].i, e.e[d->nr_of_symbols].i);
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
e.e[d->qam_mod_order].i, e.e[d->mcs_index].i,
e.e[d->mcs_table].i, e.e[d->nrOfLayers].i,
e.e[d->transform_precoding].i);
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
e.e[d->dmrs_config_type].i, e.e[d->ul_dmrs_symb_pos].i,
e.e[d->number_dmrs_symbols].i, e.e[d->dmrs_port].i,
e.e[d->dmrs_nscid].i);
printf("nb_antennas_tx %d, number_of_bits %d, data size %d\n",
e.e[d->nb_antennas_tx].i, e.e[d->number_of_bits].i,
e.e[d->data].bsize);
}
// Write metadata fields to shared memory using field descriptor table.
// Writes msg_id first, then loops through all fields.
void write_metadata_to_shm(char *addr_wr, unsigned int *bufIdx_wr, event e,
const field_descriptor *fields, int n_fields)
{
// Write message ID
memcpy(&addr_wr[*bufIdx_wr], &e.type, sizeof(uint16_t));
*bufIdx_wr += sizeof(uint16_t);
// Write all metadata fields
for (int i = 0; i < n_fields; i++) {
if (fields[i].field_idx == FIELD_SENDING_TIME_SEC) {
uint32_t ts_sec = (uint32_t)e.sending_time.tv_sec;
memcpy(&addr_wr[*bufIdx_wr], &ts_sec, fields[i].wire_size);
} else if (fields[i].field_idx == FIELD_SENDING_TIME_NSEC) {
uint32_t ts_nsec = (uint32_t)e.sending_time.tv_nsec;
memcpy(&addr_wr[*bufIdx_wr], &ts_nsec, fields[i].wire_size);
} else {
memcpy(&addr_wr[*bufIdx_wr], &e.e[fields[i].field_idx].i, fields[i].wire_size);
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
return 1; // Value found
}
*bufIdx_wr += fields[i].wire_size;
}
return 0; // Value not found
}
void reestablish_connection(int *socket, char *ip, int port, int number_of_events, int *is_on)
@@ -389,24 +388,40 @@ int main(int n, char **v)
// trace_msgs_support_data_Collection_format
// it is used to parse the requested messages if it is based
// on Data Collection Trace Messages Structure and supported tracer messages indices
char *traces_ul_support_data_Collection_format[] = {"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
"GNB_PHY_UL_PAYLOAD_RX_BITS",
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"};
// extra number of records to simplify sync between base station and UE synchronization records.
char *traces_iq_support_data_Collection_format[] = {"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"};
char *traces_bits_support_data_Collection_format[] = {"GNB_PHY_UL_PAYLOAD_RX_BITS",
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"};
// extra number of records to simlify Sync between base station and UE synchronization records.
// if you have network delay, you can increase the number of records to capture
int max_sync_offset = 6; // 6 frames ~ 60 ms
// Calculate the size of the UL message array
int n_ul_msgs = sizeof(traces_ul_support_data_Collection_format) / sizeof(traces_ul_support_data_Collection_format[0]);
// all supported messages
// Calculate the size of the combined array
int n_iq_msgs = sizeof(traces_iq_support_data_Collection_format) / sizeof(traces_iq_support_data_Collection_format[0]);
int n_bits_msgs = sizeof(traces_bits_support_data_Collection_format) / sizeof(traces_bits_support_data_Collection_format[0]);
int n_msgs_based_data_Collection_format = n_iq_msgs + n_bits_msgs;
// Create the combined array
char *traces_support_data_Collection_format[n_msgs_based_data_Collection_format];
// Copy IQ messages to the combined array
for (int i = 0; i < n_iq_msgs; i++) {
traces_support_data_Collection_format[i] = traces_iq_support_data_Collection_format[i];
}
// Copy Bits messages to the combined array
for (int i = 0; i < n_bits_msgs; i++) {
traces_support_data_Collection_format[i + n_iq_msgs] = traces_bits_support_data_Collection_format[i];
}
uint16_t msg_id = 0;
uint16_t start_frame_number = 0;
uint32_t number_records = 0; // number of records to capture, it is number of slots
// array to store the requested tracer messages indices
int req_tracer_msgs_indices[100] = {0};
// define variables --> to do: add all of them to be class of pointers
char *database_filename = NULL;
void *database;
char *ip = DEFAULT_REMOTE_IP;
@@ -419,19 +434,18 @@ int main(int n, char **v)
char trace_time_stamp_str[30];
// data structure for the trace messages based on Data Collection Trace Messages Structure
event_trace_msg_ul_data trace_msg_ul_data;
event_trace_msg_data trace_msg_data;
// initialization variables
// initlization variables
unsigned int bufIdx_wr = 0;
unsigned int bufIdx_rd = 0;
uint8_t num_req_tracer_msgs = 0;
// initialize shared memory
// initilaze shared memory
char *addr_wr, *addr_rd;
PRINT_STATE_BANNER("INIT");
printf(" Data Collection Service: Initializing shared memory ...\n");
printf(" Directory 1: %s, Directory 2: %s\n", GETKEYDIR1_UE, GETKEYDIR2_UE);
printf(" Project ID: %d\n", PROJECTID_UE);
printf("\n Data Collection Service: Initializing shared memory ...");
printf("\n Directory 1: %s, Directory 2: %s", GETKEYDIR1_UE, GETKEYDIR2_UE);
printf("\n Project ID: %d\n", PROJECTID_UE);
int shm_id_wr = create_shm(&addr_wr, GETKEYDIR1_UE, PROJECTID_UE);
int shm_id_rd = create_shm(&addr_rd, GETKEYDIR2_UE, PROJECTID_UE);
del_shm(addr_wr, shm_id_wr);
@@ -488,18 +502,20 @@ int main(int n, char **v)
addr_rd[0] = 0; // important to check if we have new requested messages
// read requested tracer msg indices from memory
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for config message ...\n");
printf("\n Data Collection Service: Waiting for messages request ...\n");
// 0: Wait
// 1: config
// 2: record
// 3: stop
// Wait for Action Config
while (1) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
if ((uint8_t)(addr_rd[0]) == 0) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// config state
else if ((uint8_t)(addr_rd[0]) == STATE_CONFIG) {
else if ((uint8_t)(addr_rd[0]) == 1) {
get_time_stamp_usec(trace_time_stamp_str);
PRINT_STATE_BANNER("CONFIG");
printf(" Received config message. Time Stamp: %s\n", trace_time_stamp_str);
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
// get the IP address length in bytes
bufIdx_rd = 1;
@@ -516,11 +532,11 @@ int main(int n, char **v)
// get the array bytes of the port number : 2 bytes
uint8_t port_number_bytes[2] = {addr_rd[bufIdx_rd], addr_rd[bufIdx_rd + 1]};
bufIdx_rd += 2; // + 2 bytes = port number
bufIdx_rd += 2; // + 2 bytes = start frame number
port = combine_bytes(port_number_bytes, 2);
printf(" Parameters: IP Address length: %d, IP Address: %s, Port Number: %d\n", ip_address_length, ip_address, port);
addr_rd[0] = STATE_WAIT; // reset memory : to wait for record action
printf("\n Parameters: IP Address length: %d, IP Address: %s, Port Number: %d \n", ip_address_length, ip_address, port);
addr_rd[0] = 0; // reset memory : to wait for record action
break;
}
}
@@ -531,39 +547,37 @@ int main(int n, char **v)
/* connect to the nr-softmodem */
socket = connect_to(ip, port);
printf(" Connected to nr-uesoftmodem\n");
printf("\n Connected to nr-UEsoftmodem");
// Read Action record or stop
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for record/stop message ...\n");
printf("\n Data Collection Service: Waiting for record message ...\n");
while (1) {
// wait for Action record
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
if ((uint8_t)(addr_rd[0]) == 0) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// quit state
else if ((uint8_t)(addr_rd[0]) == STATE_STOP) {
PRINT_STATE_BANNER("STOP");
printf(" Received 'stop' command. Exiting...\n");
else if ((uint8_t)(addr_rd[0]) == 3) {
printf("\n Received 'stop' command. Exiting...");
printf("\n ");
// Clean up and exit
break;
}
// record state
else if ((uint8_t)(addr_rd[0]) == STATE_RECORD) {
else if ((uint8_t)(addr_rd[0]) == 2) {
// clear remote buffer if there is
clear_remote_config();
get_time_stamp_usec(trace_time_stamp_str);
PRINT_STATE_BANNER("RECORD");
printf(" Received record message. Time Stamp: %s\n", trace_time_stamp_str);
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
// read number of requested messages
bufIdx_rd = 1;
num_req_tracer_msgs = addr_rd[bufIdx_rd];
bufIdx_rd += 1;
printf(" Number of requested tracer messages: %d\n", num_req_tracer_msgs);
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
// reset memory : action to wait for next record action
addr_rd[0] = STATE_WAIT;
addr_rd[0] = 0;
// read tracer msg IDs - every message ID has been stored in two bytes
for (uint8_t msg_n = 0; msg_n < num_req_tracer_msgs; msg_n++) {
@@ -595,36 +609,40 @@ int main(int n, char **v)
printf("start_frame: %d\n", start_frame_number);
/* activate the trace in this array */
printf(" Activating tracer messages:\n");
printf("\n Activate Tracer messages in on_off array: \n");
for (i = 0; i < num_req_tracer_msgs; i++) {
char *on_off_name = event_name_from_id(database, req_tracer_msgs_indices[i]);
on_off(database, on_off_name, is_on, 1);
printf(" %d: %s\n", req_tracer_msgs_indices[i], on_off_name);
printf("%d, %s, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
}
// Build UL message ID array for generic UL dispatch
int ul_msg_ids[n_ul_msgs];
for (int i = 0; i < n_ul_msgs; i++) {
ul_msg_ids[i] = event_id_from_name(database, traces_ul_support_data_Collection_format[i]);
// get the event IDs for the bit messages
int traces_bits_support_data_Collection_format_idx[n_bits_msgs];
for (int i = 0; i < n_bits_msgs; i++) {
traces_bits_support_data_Collection_format_idx[i] =
event_id_from_name(database, traces_bits_support_data_Collection_format[i]);
}
// all supported messages
int traces_support_data_Collection_format_idx[n_msgs_based_data_Collection_format];
for (int i = 0; i < n_msgs_based_data_Collection_format; i++) {
traces_support_data_Collection_format_idx[i] = event_id_from_name(database, traces_support_data_Collection_format[i]);
}
// setup data for the trace messages
setup_trace_msg_ul_data(&trace_msg_ul_data, database);
printf(" Setup UL trace data done\n");
// Build field descriptor table for table-driven shared memory writes
field_descriptor ul_fields[N_UL_METADATA_FIELDS];
build_ul_field_descriptors(ul_fields, &trace_msg_ul_data);
setup_trace_msg_data(&trace_msg_data, database);
printf("\n Setup Trace Data Done");
// Get the start time
struct timespec start_time = get_current_time();
/* activate the traces in the nr-uesoftmodem */
/* activate the tracee in the nr-softmodem */
activate_traces(socket, number_of_events, is_on);
printf(" Activated traces in nr-uesoftmodem\n");
printf("\n Activated Traces in nr-UEsoftmodem");
/* a buffer needed to receive events from the nr-softmodem */
OBUF ebuf = {.osize = 0, .omaxsize = 0, .obuf = NULL};
OBUF ebuf = {osize : 0, omaxsize : 0, obuf : NULL};
/* read events */
int nrecord_idx = 0;
bufIdx_wr = 0;
@@ -638,8 +656,8 @@ int main(int n, char **v)
int current_frame = 0, prev_frame = 0, current_slot = 0, prev_slot = 0;
// offset to sync between base station and UE synchronization records or power measurements
int sync_offset_index = 0; // increase the index only if the index of frame changes after getting all records
// since we will use the frame difference to do extra records, we should be sure that the last slot is recorded completely
printf("\n Data Collection Service: Start reading messages ...\n");
// since we will use the frame differece to do extra records, we should be sure that the last slot is recorded completely
printf("\n\n Data Collection Service: Start reading messages ...");
struct pollfd event_poll_fd;
event_poll_fd.fd = socket;
event_poll_fd.events = POLLIN;
@@ -659,23 +677,27 @@ int main(int n, char **v)
continue; // Skip further processing and retry
}
//-------------------------
// GNB_PHY_UL_FD_PUSCH_IQ, GNB_PHY_UL_FD_DMRS_ID, GNB_PHY_UL_FD_CHAN_EST_DMRS_POS,
// UE_PHY_UL_SCRAMBLED_TX_BITS, GNB_PHY_UL_PAYLOAD_RX_BITS, UE_PHY_UL_PAYLOAD_TX_BITS
//-------------------------
// is it a requested message
if (is_message_in_list(req_tracer_msgs_indices, num_req_tracer_msgs, e.type)) {
if (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
// is it based on Data Collection Trace Messages Structure
if (is_message_in_list(ul_msg_ids, n_ul_msgs, e.type)) {
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
// Start recording from the next slot to mitigate capturing partial data
// check if the current frame and slot are different from the previous frame and slot
// Then, increase the record index
if (start_recording == false) {
if (got_ref_frame_slot == false) {
ref_frame = e.e[trace_msg_ul_data.frame].i;
ref_slot = e.e[trace_msg_ul_data.slot].i;
ref_frame = e.e[trace_msg_data.frame].i;
ref_slot = e.e[trace_msg_data.slot].i;
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
got_ref_frame_slot = true;
}
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_data.slot].i;
current_frame = e.e[trace_msg_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
frame_difference = (current_frame - ref_frame + MAX_FRAME_INDEX + 1) % (MAX_FRAME_INDEX + 1);
slot_difference = (current_slot - ref_slot + MAX_SLOT_INDEX + 1) % (MAX_SLOT_INDEX + 1);
printf("\n First frame.slot: %d.%d, current frame.slot: %d.%d, diff frame.slot: %d.%d",
@@ -688,30 +710,130 @@ int main(int n, char **v)
if ((ref_frame != current_frame) || (ref_slot != current_slot)) {
start_recording = true;
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_ul_data.frame].i, e.e[trace_msg_ul_data.slot].i);
printf("\n Start recording from frame: %d, slot: %d ", e.e[trace_msg_data.frame].i, e.e[trace_msg_data.slot].i);
}
}
// start recording from the next frame to mitigate capturing partial data
if (start_recording == true) {
/* this is how to access the elements of the Data Collection trace messages.
* we use e.e[<element>] and then the correct suffix, here
* it's .i for the integer and .b for the buffer and .bsize for the buffer size
* see in event.h the structure event_arg
*/
unsigned char *buf = e.e[trace_msg_data.data].b;
printf("\n\nRecord number: %d", nrecord_idx);
#ifdef DEBUG_BUFFER
printf("\nBuffer index in bytes: %d", bufIdx_wr);
#endif
// --- UL IQ/Bits handler ---
// Write metadata + buffer to shared memory
write_metadata_to_shm(addr_wr, &bufIdx_wr, e, ul_fields, N_UL_METADATA_FIELDS);
// Write buffer: size (uint32_t) + raw data
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_ul_data.data].bsize, sizeof(uint32_t));
// add general message header: message ID,
// T-Tracer Message format
// FORMAT = int,frame : int,slot : int,datetime_yyyymmdd : int,datetime_hhmmssmmm :
// int,frame_type : int,freq_range : int,subcarrier_spacing : int,cyclic_prefix : int,symbols_per_slot :
// int,Nid_cell : int,rnti :
// int,rb_size : int,rb_start : int,start_symbol_index : int,nr_of_symbols :
// int,qam_mod_order : int,mcs_index : int,mcs_table : int,nrOfLayers :
// int,transform_precoding : int,dmrs_config_type : int,ul_dmrs_symb_pos : int,number_dmrs_symbols :
// int,dmrs_port : int,dmrs_nscid :
// int,nb_antennas_tx : int,number_of_bits : buffer,data
// printf("\nTX : %d", e.e[trace_msg_data.nb_antennas_tx].i);
memcpy(&addr_wr[bufIdx_wr], &e.type, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.slot].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_yyyymmdd].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
memcpy(&addr_wr[bufIdx_wr], e.e[trace_msg_ul_data.data].b, e.e[trace_msg_ul_data.data].bsize);
bufIdx_wr += e.e[trace_msg_ul_data.data].bsize;
#ifdef DEBUG_T_Tracer
print_ul_metadata_debug(e, &trace_msg_ul_data, database);
#endif
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.datetime_hhmmssmmm].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.frame_type].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.freq_range].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.subcarrier_spacing].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.cyclic_prefix].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.symbols_per_slot].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.Nid_cell].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rnti].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_size].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.rb_start].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.start_symbol_index].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nr_of_symbols].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.qam_mod_order].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_index].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.mcs_table].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nrOfLayers].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.transform_precoding].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_config_type].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.ul_dmrs_symb_pos].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_dmrs_symbols].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_port].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.dmrs_nscid].i, sizeof(uint16_t));
bufIdx_wr += sizeof(uint16_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.nb_antennas_tx].i, sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.number_of_bits].i, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
printf("\nTime Stamp: %d_%d", e.e[trace_msg_data.datetime_yyyymmdd].i, e.e[trace_msg_data.datetime_hhmmssmmm].i);
// add message body: length in bytes + recorded data
memcpy(&addr_wr[bufIdx_wr], &e.e[trace_msg_data.data].bsize, sizeof(uint32_t));
bufIdx_wr += sizeof(uint32_t);
// read data from buffer and convert from unsigned char * array to int 16 using the right endianness
// T-tracer: Little Endian
// For BITS Messages, example: UE_PHY_UL_SCRAMBLED_TX_BITS: data in bytes
if (is_bits_messages(traces_bits_support_data_Collection_format_idx, n_bits_msgs, e.type)) {
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx += 1) {
// printf("%d, ", buf[byte_idx]);
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
}
} else {
for (int byte_idx = 0; byte_idx < e.e[trace_msg_data.data].bsize; byte_idx += 2) {
// For a little-endian system:
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
memcpy(&addr_wr[bufIdx_wr], &buf[byte_idx + 1], sizeof(uint8_t));
bufIdx_wr += sizeof(uint8_t);
}
}
/*
for (int byte_idx_i = 0; byte_idx_i < e.e[d.nr_ul_fd_dmrs_data].bsize; byte_idx_i+=4) {
int16_t I = buf[byte_idx_i] | (buf[byte_idx_i+1] << 8);
int16_t Q = buf[byte_idx_i+2] | (buf[byte_idx_i+3] << 8);
printf ("idx %d, ", byte_idx_i);
printf ("\n%d", I);
printf ("\n%d", Q);
}
*/
// check if the current frame and slot are different from the previous frame and slot
// Then, increase the record index
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_data.slot].i;
current_frame = e.e[trace_msg_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
// increase sync offset index if the current frame is different from the previous frame
if ((current_frame != prev_frame) && nrecord_idx >= number_records) {
@@ -724,6 +846,43 @@ int main(int n, char **v)
prev_frame = current_frame;
prev_slot = current_slot;
}
#ifdef DEBUG_T_Tracer
printf("\nMessage Info: msg_id %s (%d) \n", event_name_from_id(database, e.type), e.type);
printf("frame %d, slot %d, datetime %d_%d\n",
e.e[trace_msg_data.frame].i,
e.e[trace_msg_data.slot].i,
e.e[trace_msg_data.datetime_yyyymmdd].i,
e.e[trace_msg_data.datetime_hhmmssmmm].i);
printf("frame_type %d, freq_range %d, subcarrier_spacing %d, cyclic_prefix %d, symbols_per_slot %d\n",
e.e[trace_msg_data.frame_type].i,
e.e[trace_msg_data.freq_range].i,
e.e[trace_msg_data.subcarrier_spacing].i,
e.e[trace_msg_data.cyclic_prefix].i,
e.e[trace_msg_data.symbols_per_slot].i);
printf("Nid_cell %d, rnti %d, rb_size %d, rb_start %d, start_symbol_index %d, nr_of_symbols %d\n",
e.e[trace_msg_data.Nid_cell].i,
e.e[trace_msg_data.rnti].i,
e.e[trace_msg_data.rb_size].i,
e.e[trace_msg_data.rb_start].i,
e.e[trace_msg_data.start_symbol_index].i,
e.e[trace_msg_data.nr_of_symbols].i);
printf("qam_mod_order %d, mcs_index %d, mcs_table %d, nrOfLayers %d, transform_precoding %d\n",
e.e[trace_msg_data.qam_mod_order].i,
e.e[trace_msg_data.mcs_index].i,
e.e[trace_msg_data.mcs_table].i,
e.e[trace_msg_data.nrOfLayers].i,
e.e[trace_msg_data.transform_precoding].i);
printf("dmrs_config_type %d, ul_dmrs_symb_pos %d, number_dmrs_symbols %d, dmrs_port %d, dmrs_nscid %d\n",
e.e[trace_msg_data.dmrs_config_type].i,
e.e[trace_msg_data.ul_dmrs_symb_pos].i,
e.e[trace_msg_data.number_dmrs_symbols].i,
e.e[trace_msg_data.dmrs_port].i,
e.e[trace_msg_data.dmrs_nscid].i);
printf("nb_antennas_tx %d, number_of_bits %d, data size %d\n",
e.e[trace_msg_data.nb_antennas_tx].i,
e.e[trace_msg_data.number_of_bits].i,
e.e[trace_msg_data.data].bsize);
#endif
} // End of start recording flag
} // end of if statement for the supported messages based on Data Collection Trace Messages Structure
else {
@@ -734,25 +893,28 @@ int main(int n, char **v)
} // end while loop of reading events
else {
// No data, just loop and check time
usleep(50); // optional: avoid busy-waiting
usleep(100); // optional: avoid busy-waiting
}
} // End of while loop to read messages
// de-activate the traces in the nr-uesoftmodem
printf("\n De-activating tracer messages\n");
// de-activate the tracee in the nr-softmodem
printf("\n De-activated Tracer message:\n");
for (i = 0; i < num_req_tracer_msgs; i++) {
char *on_off_name = event_name_from_id(database, req_tracer_msgs_indices[i]);
on_off(database, on_off_name, is_on, 0);
// printf("\n %d, %s, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
}
// De-activate the tracee in the nr-softmodem
activate_traces(socket, number_of_events, is_on);
printf(" De-activated traces\n");
printf("\n De-activated Traces");
// Get the end time
struct timespec end_time = get_current_time();
// Calculate the time difference
double time_diff = calculate_time_difference(start_time, end_time);
printf(" Total time: %.2f ms\n", time_diff);
printf(" Time per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
printf("Total Time difference: %.2f ms\n", time_diff);
printf("Time difference per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
// discard stale or previous record data for the first DISCARD_RECORD_DURATION_MS
struct timespec record_start, record_now;
@@ -779,9 +941,10 @@ int main(int n, char **v)
}
} // End of while loop to discard stale records
}
} // End of while loop for record/stop
} // End a while loop to check for the "stop" command
// de-activate the tracee in the nr-softmodem
// free_database(database); // Done on gNB App only
// free_database(database); //Do on one app, for example on gNB App
free(is_on);
close(socket);

View File

@@ -1,76 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef BITS_H_
#define BITS_H_
#include <stddef.h>
#include <stdint.h>
#include <string.h>
#include "assertions.h"
uint64_t reverse_bits(uint64_t in, int n_bits);
void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out);
static inline int get_last_bit_index(const uint32_t *arr, int sz)
{
AssertFatal(sz > 0, "Invalid size %d to get the first bit of array\n", sz);
for (int i = sz - 1; i >= 0; i--) {
if (arr[i] != 0) {
// 31 - clz gives the index of the highest bit (0-31)
return (i * 32) + (31 - __builtin_clz(arr[i]));
}
}
return -1;
}
static uint32_t bit_mask(int from_bit, int num_bits)
{
int total = from_bit + num_bits;
uint32_t right_mask = total < 32 ? (0xFFFFFFFF >> (32 - total)) : 0xFFFFFFFF;
return (0xFFFFFFFF << from_bit) & right_mask;
}
static inline int get_first_bit_index_mask(const uint32_t *arr, int sz, int from_bit, int num_bits)
{
// start i from first_bit
int i = from_bit / 32;
from_bit %= 32;
for (; i < sz && num_bits > 0; i++) {
uint32_t a = arr[i] & bit_mask(from_bit, num_bits);
if (a != 0) {
// Find the first set bit in this 32-bit word
return (i * 32) + __builtin_ctz(a);
}
num_bits -= 32 - from_bit;
from_bit = 0;
}
return -1;
}
static inline int get_first_bit_index(const uint32_t *arr, int sz)
{
return get_first_bit_index_mask(arr, sz, 0, sz * 32);
}
static inline int count_bits(const uint32_t *arr, int sz)
{
int ret = 0;
// sz is the number of uint32_t elements
for (int i = 0; i < sz; i++)
ret += __builtin_popcount(arr[i]);
return ret;
}
static __attribute__((always_inline)) inline int count_bits64(uint64_t v)
{
return __builtin_popcountll(v);
}
static __attribute__((always_inline)) inline int count_bits64_with_mask(uint64_t v, int start, int num)
{
uint64_t mask = ((1LL << num) - 1) << start;
return count_bits64(v & mask);
}
#endif /* BITS_H_ */

View File

@@ -42,59 +42,43 @@
"t_tracer_message_definition_file": "../T/T_messages.txt",
"parameter_map_file": "config/wireless_link_parameter_map.yaml",
"start_frame_number": 5,
"base_station": {
"requested_tracer_messages": [
"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
"GNB_PHY_UL_PAYLOAD_RX_BITS"
],
"meta_data":{
"num_rx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 30.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "328AB35"
}
},
"user_equipment": {
"requested_tracer_messages": [
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"
],
"meta_data":{
"num_tx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 40.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "323F75F"
}
},
"common_meta_data": {
"sample_rate": 61440000.0,
"bandwidth": 40000000.0,
"clock_reference": "external"
},
"nodes": [
{
"id": "gnb_0",
"type": "gnb",
"requested_tracer_messages": [
"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
"GNB_PHY_UL_PAYLOAD_RX_BITS"],
"tracer_service_address": "127.0.0.1:2021",
"meta_data": {
"num_tx_antennas": 1,
"num_rx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 30.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "328AB35"
},
"shared_mem_config": {
"read_path": "/tmp/gnb_app1",
"write_path": "/tmp/gnb_app2",
"project_id": 2335
}
},
{
"id": "ue_0",
"type": "ue",
"requested_tracer_messages": [
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"],
"tracer_service_address": "127.0.0.1:2023",
"meta_data": {
"num_tx_antennas": 1,
"num_rx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 30.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "323F75F"
},
"shared_mem_config": {
"read_path": "/tmp/ue_app1",
"write_path": "/tmp/ue_app2",
"project_id": 2336
}
}
]
"tracer_service_baseStation_address": "127.0.0.1:2021",
"tracer_service_userEquipment_address": "127.0.0.1:2023"
}
}
}

View File

@@ -0,0 +1,803 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief main application of synchronized real-time data recording
import sysv_ipc as ipc
import struct
import time
from datetime import datetime
from termcolor import colored
import numpy as np
import json
import concurrent.futures
# from concurrent import futures
import threading
# import related functions
from lib import sigmf_interface
# import library functions
from lib import sync_service
from lib import data_recording_messages_def
from lib import common_utils
from lib import config_interface
DEBUG_WIRELESS_RECORDED_DATA = True
DEBUG_BUFFER_READING = False
# globally applicable metadata
global_info = {
"author": "Abdo Gaber",
"description": "Synchronized Real-Time Data Recording",
"timestamp": 0,
"collection_file_prefix": "data-collection", # collection file name prefix "deap-rx + str(...)"
"collection_file": "", # Reserved to be created in the code: “data-collection_rec-0_TIME-STAMP”
"datetime_offset": "", # datetime offset between current location and UTC/Zulu timezone
# Example: "+01:00" for Berlin, Germany
"save_config_data_recording_app_json": True,
"waveform_generator": "5gnr_oai",
"extensions": {},
}
# Supported OAI Trace messages
# UL receiver messages
# gNB IQ Msgs: "GNB_PHY_UL_FD_PUSCH_IQ", "GNB_PHY_UL_FD_DMRS", "GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
# "GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"
# gNB BITS Msgs: "GNB_PHY_UL_PAYLOAD_RX_BITS"
# UE BITS Msgs: "UE_PHY_UL_SCRAMBLED_TX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
supported_oai_tracer_messages = {
# gNB messages
"GNB_PHY_UL_FD_PUSCH_IQ": {
"file_name_prefix": "rx-fd-data",
"scope": "gNB",
"description": "Frequency-domain RX data",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_DMRS": {
"file_name_prefix": "tx-pilots-fd-data",
"scope": "gNB",
"description": "Frequency-domain TX PUSCH DMRS data",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS": {
"file_name_prefix": "raw-ce-fd-data",
"scope": "gNB",
"description": "Frequency-domain raw channel estimates (at DMRS positions)",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL": {
"file_name_prefix": "raw-inter-ce-fd-data",
"scope": "gNB",
"description": "Interpolcated Frequency-domain raw channel estimates",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_PAYLOAD_RX_BITS": {
"file_name_prefix": "rx-payload-bits",
"scope": "gNB",
"description": "Received PUSCH payload bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
# UE messages
"UE_PHY_UL_SCRAMBLED_TX_BITS": {
"file_name_prefix": "tx-scrambled-bits",
"scope": "UE",
"description": "Transmitted scrambled PUSCH bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
"UE_PHY_UL_PAYLOAD_TX_BITS": {
"file_name_prefix": "tx-payload-bits",
"scope": "UE",
"description": "Transmitted PUSCH payload bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
}
# -------------------------------------------
# System configuration: gNB
project_id_gnb = 2335
read_shm_path_gnb = "/tmp/gnb_app1"
write_shm_path_gnb = "/tmp/gnb_app2"
# System configuration: UE
project_id_ue = 2336
read_shm_path_ue = "/tmp/ue_app1"
write_shm_path_ue = "/tmp/ue_app2"
# initialize shared memory
def attach_shm(shm_path, project_id):
key = ipc.ftok(shm_path, project_id)
shm = ipc.SharedMemory(key, 0, 0)
# I found if we do not attach ourselves
# it will attach as ReadOnly.
shm.attach(0, 0)
return shm
def detach_shm(shm):
try:
shm.detach()
print("Shared memory detached successfully.")
except ipc.ExistentialError:
print("Shared memory segment does not exist.")
def remove_shm(shm):
try:
shm.remove()
print("Shared memory removed successfully.")
except ipc.ExistentialError:
print("Shared memory segment does not exist.")
# check data if avalible in the shared memory
def is_data_available_in_memory(shm, bufIdx, general_message_header_length, timeout=20):
start_time = time.time()
while True:
buf = shm.read(bufIdx + general_message_header_length)
n_bytes = sum(buf)
print("Data Recording App: Waiting for Measurements!")
if n_bytes > 0:
print("There is data in memory, n_bytes: ", n_bytes)
return True
if (time.time() - start_time) > timeout:
break
time.sleep(1)
return False
# Read data from Shared memory based Data Conversion Service message structure
def read_data_from_shm(shm, bufIdx, tracer_msgs_identities):
# print buffer index
if DEBUG_BUFFER_READING:
print("Buffer Index: ", bufIdx)
# get general message header list
general_msg_header_list, general_message_header_length = data_recording_messages_def.get_general_msg_header_list()
buf = shm.read(bufIdx + general_message_header_length)
n_bytes = sum(buf)
if n_bytes == 0:
raise Exception('ERROR: No data available in memory')
msg_id = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("msg_id")])[0]
bufIdx += general_msg_header_list.get("msg_id")
frame = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("frame")])[0]
bufIdx += general_msg_header_list.get("frame")
slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("slot")])[0]
bufIdx += general_msg_header_list.get("slot")
# get time stamp: yyyy mm dd hh mm ss msec
nr_trace_time_stamp_yyymmdd = \
struct.unpack('<i', buf[bufIdx:bufIdx+general_msg_header_list.get("datetime_yyyymmdd")])[0]
bufIdx += general_msg_header_list.get("datetime_yyyymmdd")
nr_trace_time_stamp_hhmmssmmm = \
struct.unpack('<i', buf[bufIdx:bufIdx+general_msg_header_list.get("datetime_hhmmssmmm")])[0]
bufIdx += general_msg_header_list.get("datetime_hhmmssmmm")
time_stamp_milli_sec = str(nr_trace_time_stamp_yyymmdd)+"_"+str(nr_trace_time_stamp_hhmmssmmm)
# get frame type
frame_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("frame_type")])[0]
bufIdx += general_msg_header_list.get("frame_type")
# get frequency range
freq_range = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("freq_range")])[0]
bufIdx += general_msg_header_list.get("freq_range")
# get subcarrier spacing
subcarrier_spacing = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("subcarrier_spacing")])[0]
bufIdx += general_msg_header_list.get("subcarrier_spacing")
# get cyclic prefix
cyclic_prefix = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("cyclic_prefix")])[0]
bufIdx += general_msg_header_list.get("cyclic_prefix")
# get symbols per slot
symbols_per_slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("symbols_per_slot")])[0]
bufIdx += general_msg_header_list.get("symbols_per_slot")
# get Nid cell
Nid_cell = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("Nid_cell")])[0]
bufIdx += general_msg_header_list.get("Nid_cell")
# get rnti
rnti = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rnti")])[0]
bufIdx += general_msg_header_list.get("rnti")
# get rb size
rb_size = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_size")])[0]
bufIdx += general_msg_header_list.get("rb_size")
# get rb start
rb_start = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_start")])[0]
bufIdx += general_msg_header_list.get("rb_start")
# get start symbol index
start_symbol_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("start_symbol_index")])[0]
bufIdx += general_msg_header_list.get("start_symbol_index")
# get number of symbols
nr_of_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nr_of_symbols")])[0]
bufIdx += general_msg_header_list.get("nr_of_symbols")
# get qam modulation order
qam_mod_order = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("qam_mod_order")])[0]
bufIdx += general_msg_header_list.get("qam_mod_order")
# get mcs index
mcs_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_index")])[0]
bufIdx += general_msg_header_list.get("mcs_index")
# get mcs table
mcs_table = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_table")])[0]
bufIdx += general_msg_header_list.get("mcs_table")
# get number of layers
nrOfLayers = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nrOfLayers")])[0]
bufIdx += general_msg_header_list.get("nrOfLayers")
# get transform precoding
transform_precoding = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("transform_precoding")])[0]
bufIdx += general_msg_header_list.get("transform_precoding")
# get dmrs config type
dmrs_config_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_config_type")])[0]
bufIdx += general_msg_header_list.get("dmrs_config_type")
# get ul dmrs symb pos
ul_dmrs_symb_pos = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("ul_dmrs_symb_pos")])[0]
bufIdx += general_msg_header_list.get("ul_dmrs_symb_pos")
# get number dmrs symbols
number_dmrs_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("number_dmrs_symbols")])[0]
bufIdx += general_msg_header_list.get("number_dmrs_symbols")
# get dmrs port
dmrs_port = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_port")])[0]
bufIdx += general_msg_header_list.get("dmrs_port")
# get dmrs scid
dmrs_scid = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_scid")])[0]
bufIdx += general_msg_header_list.get("dmrs_scid")
# get nb antennas rx for gNB or nb antennas tx for UE
nb_antennas = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nb_antennas")])[0]
bufIdx += general_msg_header_list.get("nb_antennas")
# get number of bits
number_of_bits = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("number_of_bits")])[0]
bufIdx += general_msg_header_list.get("number_of_bits")
# get length of bytes
length_bytes = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("length_bytes")])[0]
bufIdx += general_msg_header_list.get("length_bytes")
# print all captured data
if DEBUG_WIRELESS_RECORDED_DATA:
print(" ")
print(f"Time stamp: {time_stamp_milli_sec}")
print(f"MSG ID: {msg_id:<5} MSG Name: {tracer_msgs_identities[msg_id]}")
print(f"Frame: {frame:<5} Slot: {slot:<5}")
print(f"Frame Type: {frame_type:<5} Frequency Range: {freq_range:<5}"
f"Subcarrier Spacing: {subcarrier_spacing:<5} Cyclic Prefix: {cyclic_prefix:<5} "
f"Symbols per Slot: {symbols_per_slot:<5}")
print(f"Nid Cell: {Nid_cell:<5} RNTI: {rnti:<5}")
print(f"RB Size: {rb_size:<5} RB Start: {rb_start:<5} Start Symbol Index: {start_symbol_index:<5} "
f"Number of Symbols: {nr_of_symbols:<5}")
print(f"QAM Modulation Order: {qam_mod_order:<5} MCS Index: {mcs_index:<5} "
f"MCS Table: {mcs_table:<5}")
print(f"Number of Layers: {nrOfLayers:<5} Transform Precoding: {transform_precoding:<5}")
print(f"DMRS Config Type: {dmrs_config_type:<5} UL DMRS Symbol Position: {ul_dmrs_symb_pos:<5} "
f"Number of DMRS Symbols: {number_dmrs_symbols:<5}")
print(f"DMRS Port: {dmrs_port:<5} DMRS SCID: {dmrs_scid:<5} "
f"Number of Antennas: {nb_antennas:<5}")
print(f"Number of bits: {number_of_bits:<5} Length of bytes: {length_bytes:<5}")
# raise exception if time stamp is zero, it means that the data is not recorded yet
if nr_trace_time_stamp_yyymmdd == 0 and nr_trace_time_stamp_hhmmssmmm == 0:
raise Exception("ERROR: Time stamp is zero, data is not recorded yet or something wrong, check logs!")
# get recorded data
buf = shm.read(bufIdx + length_bytes)
# bit_msg_index = get_index_of_id(tracer_msgs_identities, "GNB_PHY_UL_PAYLOAD_RX_BITS")
captured_data = {}
# If message is bit message, store data in bytes
# then the field number_of_bits should be not zero
if "_BITS" in tracer_msgs_identities[msg_id]:
# recorded_data = buf[bufIdx:bufIdx + length_bytes]
recorded_data = struct.unpack("<" + int(length_bytes) * 'B', buf[bufIdx:bufIdx + length_bytes])
bufIdx += length_bytes
# convert data in bytes to bits
bits_vector = []
for byte in recorded_data:
bits_vector.extend([int(bit) for bit in format(int(byte), '08b')])
captured_data["sigmf_data_type"] = "ri8_le"
# convert to uint8
captured_data["recorded_data"] = np.asarray(bits_vector).astype(np.uint8)
# recorded_data_formated = recorded_data.astype(np.complex64) # convert to complex64
else:
recorded_data = struct.unpack("<" + int(length_bytes/2) * 'h', buf[bufIdx:bufIdx + length_bytes])
bufIdx += length_bytes
# print("IQ data I/Q: ", recorded_data)
# Convert real data to complext data
# converting list to array
recorded_data = np.asarray(recorded_data)
# recorded_data_complex = recorded_data
recorded_data_complex = common_utils.real_to_complex(recorded_data)
captured_data["sigmf_data_type"] = "cf32_le"
# convert to complex64
captured_data["recorded_data"] = recorded_data_complex.astype(np.complex64)
# print("Recorded Data: ", captured_data["recorded_data"])
# store data in dictonary
captured_data["message_id"] = msg_id
captured_data["message_type"] = tracer_msgs_identities[msg_id]
captured_data["frame"] = frame
captured_data["slot"] = slot
captured_data["time_stamp"] = time_stamp_milli_sec
captured_data["frame_type"] = frame_type
captured_data["freq_range"] = freq_range
captured_data["subcarrier_spacing"] = subcarrier_spacing
captured_data["cyclic_prefix"] = cyclic_prefix
# captured_data["symbols_per_slot"] = symbols_per_slot ... not used
captured_data["Nid_cell"] = Nid_cell
captured_data["rnti"] = rnti
captured_data["rb_size"] = rb_size
captured_data["rb_start"] = rb_start
captured_data["start_symbol_index"] = start_symbol_index
captured_data["nr_of_symbols"] = nr_of_symbols
captured_data["qam_mod_order"] = qam_mod_order
captured_data["mcs_index"] = mcs_index
captured_data["mcs_table"] = mcs_table
captured_data["nrOfLayers"] = nrOfLayers
captured_data["transform_precoding"] = transform_precoding
captured_data["dmrs_config_type"] = dmrs_config_type
captured_data["ul_dmrs_symb_pos"] = ul_dmrs_symb_pos
captured_data["number_dmrs_symbols"] = number_dmrs_symbols
captured_data["dmrs_port"] = dmrs_port
captured_data["dmrs_scid"] = dmrs_scid
captured_data["nb_antennas"] = nb_antennas
captured_data["number_of_bits"] = number_of_bits
return captured_data, bufIdx
# Synchronize data between gNB and UE
def sync_data_conversion_service(
shm_reading_gnb, shm_reading_ue, sync_info, config_meta_data, gnb_args, ue_args):
# Initialize variables
record_idx = 0
prev_frame = -1
prev_slot = -1
ue_bufIdx = 0
gnb_bufIdx = 0
gnb_args.num_requested_tracer_msgs = len(
config_meta_data["data_recording_config"]["base_station"][
"requested_tracer_messages"])
ue_args.num_requested_tracer_msgs = len(
config_meta_data["data_recording_config"]["user_equipment"][
"requested_tracer_messages"])
tracer_msgs_identities = config_meta_data["data_recording_config"][
"tracer_msgs_identities"]
global_info = config_meta_data["data_recording_config"]["global_info"]
# Get UE data based on the sync data
bufIdx = 0
timeout_sync = time.time() + 5 # 5 seconds if no sync data found, stop the process
while True:
# wait for the next record
# To do: check if we need to add exta waiting times between different events in case of
# data streaming via network such as on UE side or gNB side
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
ue_bufIdx = bufIdx
captured_data, bufIdx = read_data_from_shm(shm_reading_ue, bufIdx, tracer_msgs_identities)
if (captured_data["frame"] == sync_info["frame"]
and captured_data["slot"] == sync_info["slot"]):
break
if time.time() > timeout_sync:
raise Exception(
"ERROR: Data Recording NO Sync Found, check Tracer Services if they are connected!")
# Get gNB data based on the sync data
bufIdx = 0
while True:
time.sleep(0.0035)
gnb_bufIdx = bufIdx
captured_data, bufIdx = read_data_from_shm(
shm_reading_gnb, bufIdx, tracer_msgs_identities)
if (captured_data["frame"] == sync_info["frame"]
and captured_data["slot"] == sync_info["slot"]):
break
# Read Synchronized data between gNB and UE
while True: # read all records
print("\nRecord number: ", record_idx)
if DEBUG_BUFFER_READING:
print(f"Buffer Index gNB: {gnb_bufIdx}, Buffer Index UE: {ue_bufIdx}")
# wait for the next record
# To do: check if we need to add exta waiting times between different events in case of
# data streaming via network such as on UE side or gNB side
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
collected_metafiles = []
# Read data from gNB T-tracer Application
for idx in range(gnb_args.num_requested_tracer_msgs):
time.sleep(0.0015)
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
captured_data, gnb_bufIdx = read_data_from_shm(
shm_reading_gnb, gnb_bufIdx, tracer_msgs_identities)
# drive the collection file time stamp from the first message per record
if idx == 0:
# Get time stamp
time_stamp_ms, time_stamp_ms_file_name = (
sigmf_interface.time_stamp_formating(
captured_data["time_stamp"], global_info["datetime_offset"]))
global_info["collection_file"] = (
global_info["collection_file_prefix"]
+ "-rec-"
+ str(record_idx)
+ "-"
+ str(time_stamp_ms_file_name)
)
global_info["timestamp"] = time_stamp_ms
# Write data into files with the given format
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
collected_metafiles.append(
sigmf_interface.write_recorded_data_to_sigmf(
captured_data, config_meta_data, global_info, record_idx))
# Read data from UE T-tracer Application
for idx in range(ue_args.num_requested_tracer_msgs):
time.sleep(0.0015)
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
captured_data, ue_bufIdx = read_data_from_shm(
shm_reading_ue, ue_bufIdx, tracer_msgs_identities)
# Write data into files with the given format
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
collected_metafiles.append(
sigmf_interface.write_recorded_data_to_sigmf(
captured_data, config_meta_data, global_info, record_idx))
# generate SigMF collection file
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
data_storage_path = config_meta_data["data_recording_config"][
"data_storage_path"]
description = global_info["description"]
sigmf_interface.save_sigmf_collection(
collected_metafiles, global_info, description, data_storage_path)
frame = captured_data["frame"]
slot = captured_data["slot"]
# Check for changes in frame or slot
if frame != prev_frame or slot != prev_slot:
record_idx += 1
# We have reached the end of the data. Break the loop
if record_idx >= config_meta_data["data_recording_config"]["num_records"]:
break
# Update previous frame and slot
prev_frame = frame
prev_slot = slot
# data conversion service
def data_conversion_service(shm_reading, config_meta_data, args, sync_info, do_sync):
# Initialize variables
record_idx = 0
prev_frame = -1
prev_slot = -1
bufIdx = 0
# Read data from T-tracer Application
print("Data Conversion Service: Reading data from T-tracer Application")
print("Requested Tracer Messages: ", args.num_requested_tracer_msgs)
if args.num_requested_tracer_msgs > 0:
num_requested_tracer_msgs = args.num_requested_tracer_msgs
else:
raise Exception("ERROR: No requested tracer messages found!")
tracer_msgs_identities = config_meta_data["data_recording_config"][
"tracer_msgs_identities"]
global_info = config_meta_data["data_recording_config"]["global_info"]
if do_sync:
print(" Find Memory Index of NR MSGs based on Sync info")
while True:
time.sleep(0.0035)
station_bufIdx = bufIdx
captured_data, bufIdx = read_data_from_shm(
shm_reading, bufIdx, tracer_msgs_identities)
print("*Sync Status: NR Captured Data (Frame, slot): (", captured_data["frame"],
", ", captured_data["slot"], "), Sync Info (Frame, slot): (", sync_info["frame"],
", ", sync_info["slot"]," )")
if (captured_data["frame"] == sync_info["frame"]
and captured_data["slot"] == sync_info["slot"]):
print("*sync Pass")
break
print("*sync Fail")
bufIdx = station_bufIdx
# Read data from T-tracer Application
while True: # read all records
print("\nRecord number: ", record_idx)
if DEBUG_BUFFER_READING:
print(f"Buffer Index: {bufIdx}")
# wait for the next record
# To do: check if we need to add exta waiting times between different events in case of
# data streaming via network such as on UE side or gNB side
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
collected_metafiles = []
for idx in range(num_requested_tracer_msgs):
time.sleep(0.0015)
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"\nRecord number: {record_idx}, Reading MSG data ", idx)
captured_data, bufIdx = read_data_from_shm(
shm_reading, bufIdx, tracer_msgs_identities)
# derive the collection file time stamp from the first message per record
if idx == 0:
# Get time stamp
time_stamp_ms, time_stamp_ms_file_name = (
sigmf_interface.time_stamp_formating(
captured_data["time_stamp"], global_info["datetime_offset"]))
global_info["collection_file"] = (
global_info["collection_file_prefix"]
+ "-rec-"
+ str(record_idx)
+ "-"
+ str(time_stamp_ms_file_name))
global_info["timestamp"] = time_stamp_ms
# Write data into files with the given format
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
collected_metafiles.append(
sigmf_interface.write_recorded_data_to_sigmf(
captured_data, config_meta_data, global_info, record_idx))
frame = captured_data["frame"]
slot = captured_data["slot"]
# generate SigMF collection file
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
data_storage_path = config_meta_data["data_recording_config"][
"data_storage_path"]
description = global_info["description"]
sigmf_interface.save_sigmf_collection(
collected_metafiles, global_info, description, data_storage_path)
# Check for changes in frame or slot
if frame != prev_frame or slot != prev_slot:
record_idx += 1
# We have reached the end of the data. Break the loop
if record_idx >= config_meta_data["data_recording_config"]["num_records"]:
break
# Update previous frame and slot
prev_frame = frame
prev_slot = slot
# Write Tracer Control Message
def write_shm(shm, args):
# Note: Big Endian >, Little Endian <
# Note: unsigned char B, signed char b, short h, int I, long long q
# Note: float f, double d, string s, char c, bool ?
# 1: config
# 2: record
# 3: quit
print("Write Shared Memory: ", args.action)
if args.action == "config":
# Determine the length of the IP address
ip_length = len(args.bytes_IPaddress) + 1 # String terminator
# Construct the format string dynamically
format_string = f"{ip_length}s"
shm.write(
# Config action
struct.pack("<B", 1) +
struct.pack("<B", ip_length) +
struct.pack(format_string, args.bytes_IPaddress) +
struct.pack("<h", int(args.port))
)
print("T-Tracer Config IP: ", args.bytes_IPaddress, " port: ", args.port)
elif args.action == "record":
shm.write(
# Record action
struct.pack("<B", int(2)) +
struct.pack("<B", args.num_requested_tracer_msgs) +
struct.pack(
"<{}h".format(len(args.req_tracer_msgs_indices)),
*args.req_tracer_msgs_indices,) +
struct.pack("<I", args.num_records) +
struct.pack("<h", args.start_frame_number)
)
print("T-Tracer Record: N Messags: ", args.num_requested_tracer_msgs,
", Msg IDs: ", args.req_tracer_msgs_indices,
", Num records: ", args.num_records,
", Start Frame: ", args.start_frame_number,
)
elif args.action == "quit":
shm.write(struct.pack("<B", int(3))) # Quit action
print("T-Tracer Quit")
else:
print("Unknown action for data recording system!")
# write shared memory task
def write_shm_task(barrier, shm_id, args):
# Wait for threads to be ready
barrier.wait()
# send request to related T-Tracer Application
write_shm(shm_id, args)
if __name__ == "__main__":
# -------------------------------------------
# ------------- Configuration --------------
# ------------------------------------------
# Data Recording Configuration
data_recording_config_file = "config/config_data_recording.json"
# -------------------------------------------
# Configuration
# -------------------------------------------
# First: get the configuration mode either local or remote
# Second: get data recording configuration
# Read and parse the JSON file
with open(data_recording_config_file, "r") as file:
config_meta_data = json.load(file)
# get Configuration parameters
config_meta_data, gnb_args, ue_args = config_interface.get_data_recording_config(config_meta_data)
# check if lists of requested tracer messages IDs are not empty
if not gnb_args.requested_tracer_messages and \
not ue_args.requested_tracer_messages:
raise Exception("ERROR: No requested tracer messages are provided")
# check if gnb_requested_tracer_messages is not empty, attach to the shared memory
if gnb_args.requested_tracer_messages:
# attach to the shared memory
shm_writing_gnb = attach_shm(write_shm_path_gnb, project_id_gnb)
shm_reading_gnb = attach_shm(read_shm_path_gnb, project_id_gnb)
# check if ue_requested_tracer_messages is not empty, attach to the shared memory
if ue_args.requested_tracer_messages:
# attach to the shared memory
shm_writing_ue = attach_shm(write_shm_path_ue, project_id_ue)
shm_reading_ue = attach_shm(read_shm_path_ue, project_id_ue)
# get general message header list
general_msg_header_list, general_message_header_length = \
data_recording_messages_def.get_general_msg_header_list()
# Add supported OAI Tracer Messages
config_meta_data["data_recording_config"]["supported_oai_tracer_messages"] = supported_oai_tracer_messages
# Add global info
config_meta_data["data_recording_config"]["global_info"] = global_info
# -------------------------------------------
# Initialization
# -------------------------------------------
# -------------------------------------------
# send Tracer Control Message request to T-Tracers Apps
# -------------------------------------------
# It consists of the following fields:
# Config action
# IP address length
# IP address
# Port Number
# check if gnb_requested_tracer_messages is not empty, config T-Tracer gNB via shared memory
if gnb_args.requested_tracer_messages:
# Config T-Tracer via shared memory
gnb_args.action = "config"
write_shm(shm_writing_gnb, gnb_args)
# check if ue_requested_tracer_messages is not empty, config T-Tracer UE via shared memory
if ue_args.requested_tracer_messages:
# Config T-Tracer via shared memory
ue_args.action = "config"
write_shm(shm_writing_ue, ue_args)
time.sleep(0.5) # wait for the config to be applied
# -------------------------------------------
# Execution
# -------------------------------------------
# send Tracer Control Message request to T-Tracers Apps
# -------------------------------------------
# It consists of the following fields:
# Record action
# Number of requested Tracer Messages
# Requested Tracer Messages ID 1, …, ID N
# Number of records to be recorded in slots
# Start SFN: Frame Index to start data collection from it, useful for future
# data sync between gNB and UE but not yet used
print("Args:")
if gnb_args.requested_tracer_messages:
gnb_args.action = "record"
print("gnb_args: ", gnb_args)
if ue_args.requested_tracer_messages:
ue_args.action = "record"
print("ue_args: ", ue_args)
start_time = time.time()
print("Send data logging request us:", datetime.now().strftime("%Y%m%d-%H%M%S%f"))
# if requested: gNB and UE Tracer Messages
# gNB + UE Tracer Messages
if gnb_args.requested_tracer_messages and ue_args.requested_tracer_messages:
# Create a barrier to synchronize the threads
barrier = threading.Barrier(2)
with concurrent.futures.ThreadPoolExecutor() as executor:
tracer_ue = executor.submit(write_shm_task, barrier, shm_writing_ue, ue_args)
tracer_gnb = executor.submit(write_shm_task, barrier, shm_writing_gnb, gnb_args)
# Wait for both functions to complete
concurrent.futures.wait([tracer_ue, tracer_gnb])
# gNB Tracer Messages
elif gnb_args.requested_tracer_messages:
write_shm(shm_writing_gnb, gnb_args)
# UE Tracer Messages
elif ue_args.requested_tracer_messages:
write_shm(shm_writing_ue, ue_args)
else:
raise Exception("ERROR: No requested tracer messages IDs are provided")
# -------------------------------------------
# Read data from gNB and UE T-tracer Application
# -------------------------------------------
# Check if data is available in memory
# Initialize variables
bufIdx = 0
timeout = 10 # 10 seconds from now
# If gNB MSGs are requested
if gnb_args.requested_tracer_messages:
# Check if data is available in gNB memory
is_gnb_data_in_memory = is_data_available_in_memory(
shm_reading_gnb, bufIdx, general_message_header_length, timeout)
# Report the status of gNB T-Tracer APP locally
if not is_gnb_data_in_memory:
print("Error: gNB: Check t-Tracer APP of gNB, check IPs and Ports")
print("Error: gNB: If IPs and Ports are correct, re-run the hanging app.")
print("It seems the socket was not closed properly")
raise Exception("ERROR: Time out, check if gNB T-Tracer APP connected to stack")
# If UE MSGs are requested
if ue_args.requested_tracer_messages:
# Check if data is available in UE memory
is_ue_data_in_memory = is_data_available_in_memory(
shm_reading_ue, bufIdx, general_message_header_length, timeout)
# Report the status of UE T-Tracer APP locally
if not is_ue_data_in_memory:
print("Error: UE: Check t-Tracer APP of UE, check IPs and Ports")
print("Error: UE: If IPs and Ports are correct, re-run the hanging app.")
print("It seems the socket was not closed properly")
raise Exception("ERROR: Time out, check if UE T-Tracer APP connected to stack")
# -------------------------------------------
# Sync data between gNB and UE
# -------------------------------------------
# write JSON file
common_utils.write_config_data_recording_app_json(config_meta_data)
sync_info = {}
if gnb_args.requested_tracer_messages and ue_args.requested_tracer_messages:
# Sync data between gNB and UE
sync_info = sync_service.sync_gnb_ue_captured_data(shm_reading_gnb, shm_reading_ue)
print("\n***Sync data between gNB and UE: ", sync_info)
# Read data from gNB and UE T-tracer Applications
sync_data_conversion_service(
shm_reading_gnb, shm_reading_ue, sync_info, config_meta_data, gnb_args, ue_args)
elif gnb_args.requested_tracer_messages:
# Read data from gNB T-tracer Application
data_conversion_service(
shm_reading_gnb, config_meta_data, gnb_args, sync_info, do_sync=False)
elif ue_args.requested_tracer_messages:
# Read data from UE T-tracer Application
data_conversion_service(
shm_reading_ue, config_meta_data, ue_args, sync_info, do_sync=False)
else:
raise Exception("ERROR: No requested tracer messages IDs are provided")
# measure Elapsed time
time_elapsed = time.time() - start_time
time_elapsed_ms = int(time_elapsed * 1000)
print(
"Elapsed time of getting Requested Messages and writing data and meta data files:",
colored(time_elapsed_ms, "yellow"), "ms",)
# Stop T-Tracer Application function
if gnb_args.requested_tracer_messages:
gnb_args.action = "quit"
write_shm(shm_writing_gnb, gnb_args)
# Add Sleep time to ensure that the message sent to the UE T-tracer application is received
# before the shared memory is detached
time.sleep(0.5)
# Clean shared memory
detach_shm(shm_reading_gnb)
detach_shm(shm_writing_gnb)
remove_shm(shm_reading_gnb)
remove_shm(shm_writing_gnb)
if ue_args.requested_tracer_messages:
ue_args.action = "quit"
write_shm(shm_writing_ue, ue_args)
# Add Sleep time to ensure that the message sent to the UE T-tracer application is received
# before the shared memory is detached
time.sleep(0.5)
# Clean shared memory
detach_shm(shm_reading_ue)
detach_shm(shm_writing_ue)
remove_shm(shm_reading_ue)
remove_shm(shm_writing_ue)
print("End of the RF Data Recording API")
pass

View File

@@ -1,523 +0,0 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief main application of synchronized real-time data recording
import atexit
import signal
import sys
import time
from datetime import datetime
import json
import concurrent.futures
from termcolor import colored
import threading
# import library functions
from lib import sigmf_interface
from lib import sync_service
from lib import data_recording_messages_def
from lib import common_utils
from lib import config_interface
from lib import shared_memory_interface as shm_interface
DEBUG_WIRELESS_RECORDED_DATA = True
DEBUG_BUFFER_READING = False
# globally applicable metadata
global_info = {
"author": "Abdo Gaber",
"description": "Synchronized Real-Time Data Recording",
"timestamp": 0,
"collection_file_prefix": "data-collection", # collection file name prefix
"collection_file": "", # Reserved to be created in the code: “data-collection_rec-0_TIME-STAMP”
"datetime_offset": "", # datetime offset between current location and UTC/Zulu timezone
# Example: "+01:00" for Berlin, Germany
"save_config_data_recording_app_json": True,
"waveform_generator": "5gnr_oai",
"extensions": {},
}
# Supported OAI Trace messages
# UL receiver messages
# gNB IQ Msgs: "GNB_PHY_UL_FD_PUSCH_IQ", "GNB_PHY_UL_FD_DMRS", "GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
# "GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL"
# gNB BITS Msgs: "GNB_PHY_UL_PAYLOAD_RX_BITS"
# UE BITS Msgs: "UE_PHY_UL_SCRAMBLED_TX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
supported_oai_tracer_messages = {
# gNB messages
"GNB_PHY_UL_FD_PUSCH_IQ": {
"file_name_prefix": "rx-fd-data",
"scope": "gNB",
"description": "Frequency-domain RX data",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_DMRS": {
"file_name_prefix": "tx-pilots-fd-data",
"scope": "gNB",
"description": "Frequency-domain TX PUSCH DMRS data",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS": {
"file_name_prefix": "raw-ce-fd-data",
"scope": "gNB",
"description": "Frequency-domain raw channel estimates (at DMRS positions)",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL": {
"file_name_prefix": "raw-inter-ce-fd-data",
"scope": "gNB",
"description": "Interpolcated Frequency-domain raw channel estimates",
"serialization_scheme": ["subcarriers", "ofdm_symbols"],
},
"GNB_PHY_UL_PAYLOAD_RX_BITS": {
"file_name_prefix": "rx-payload-bits",
"scope": "gNB",
"description": "Received PUSCH payload bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
# UE messages
"UE_PHY_UL_SCRAMBLED_TX_BITS": {
"file_name_prefix": "tx-scrambled-bits",
"scope": "UE",
"description": "Transmitted scrambled PUSCH bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
"UE_PHY_UL_PAYLOAD_TX_BITS": {
"file_name_prefix": "tx-payload-bits",
"scope": "UE",
"description": "Transmitted PUSCH payload bits",
"serialization_scheme": ["bits", "subcarriers", "ofdm_symbols"],
},
}
def read_and_store_tracer_messages(shm_reading, bufIdx, num_messages, config_meta_data,
global_info, update_timestamp=False):
"""
Read tracer messages from shared memory while they belong to the same frame and slot.
Continuously reads messages until a different frame/slot is encountered.
Args:
shm_reading: Shared memory reading handle
bufIdx: Current buffer index
num_messages: Maximum number of messages to read (used as safeguard)
config_meta_data: Configuration metadata
global_info: Global information dict (for timestamp on first message)
update_timestamp: If True, update global_info timestamp from first message
Returns:
tuple: (updated_bufIdx, collected_metafiles, last_captured_data)
"""
tracer_msgs_identities = config_meta_data["data_recording_config"]["tracer_msgs_identities"]
collected_metafiles = []
captured_data = None
# Get sync header to check frame and slot
sync_header_msg, sync_header_msg_length = data_recording_messages_def.get_sync_header_msg_list()
# Read first message to establish the reference frame and slot
timeout = 10 # 10 seconds timeout
is_data_in_memory = shm_interface.is_data_available_in_memory(
shm_reading, bufIdx, sync_header_msg_length, timeout)
if not is_data_in_memory:
if DEBUG_WIRELESS_RECORDED_DATA:
print("Warning: No data available in shared memory")
return bufIdx, collected_metafiles, global_info
# Get reference frame and slot from first message
ref_frame, ref_slot = shm_interface.get_frame_slot_start(
shm_reading, bufIdx, sync_header_msg, sync_header_msg_length)
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"\nRecord number: {global_info['record_idx']}, Reference Frame: {ref_frame}, Slot: {ref_slot}")
idx = 0
while idx < num_messages:
time.sleep(0.0015)
# Check if data is available
is_data_in_memory = shm_interface.is_data_available_in_memory(
shm_reading, bufIdx, sync_header_msg_length, timeout)
if not is_data_in_memory:
break
# Get frame and slot of current message before reading
current_frame, current_slot = shm_interface.get_frame_slot_start(
shm_reading, bufIdx, sync_header_msg, sync_header_msg_length)
# Check if we're still on the same frame and slot
if current_frame != ref_frame or current_slot != ref_slot:
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"Frame/Slot changed: ({ref_frame}, {ref_slot}) -> ({current_frame}, {current_slot}). Stopping at message {idx}")
# Keep buffer index pointing to this new frame/slot message (don't increment)
break
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"Reading MSG {idx}: Frame={current_frame}, Slot={current_slot}")
# Read the message
captured_data, bufIdx = shm_interface.read_data_from_shm(
shm_reading, bufIdx, tracer_msgs_identities)
# Update timestamp from the first message if requested
if idx == 0 and update_timestamp:
time_stamp, time_stamp_file_name = (
sigmf_interface.time_stamp_formating(
captured_data["unix_capture_ts_sec"],
captured_data["unix_capture_ts_nsec"],
global_info["datetime_offset"]))
global_info["collection_file"] = (
f"{global_info['collection_file_prefix']}-rec-{global_info['record_idx']}-{time_stamp_file_name}")
global_info["timestamp"] = time_stamp
global_info["frame"] = captured_data["frame"]
global_info["slot"]= captured_data["slot"]
# Write data into files with the given format
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
collected_metafiles.append(sigmf_interface.write_recorded_data_to_sigmf(
captured_data, config_meta_data, global_info, global_info['record_idx']))
idx += 1
if DEBUG_WIRELESS_RECORDED_DATA:
print(f"Total messages read: {idx} for Frame {ref_frame}, Slot {ref_slot}")
return bufIdx, collected_metafiles, global_info
def find_sync_buffer_index(shm_reading, sync_info, tracer_msgs_identities, timeout_sec=None):
"""
Find buffer index that matches sync frame and slot.
Args:
shm_reading: Shared memory reading handle
sync_info: Dictionary with 'frame' and 'slot' keys
tracer_msgs_identities: Tracer message identities
timeout_sec: Timeout in seconds (None for no timeout)
Returns:
bufIdx: Buffer index where sync match was found (points to the matching message)
Raises:
Exception if timeout occurs before sync is found
"""
bufIdx = 0
timeout_sync = time.time() + timeout_sec if timeout_sec else None
while True:
time.sleep(0.0035) # 2.3 ms = latency of T tracer to capture data from the RAN
saved_bufIdx = bufIdx # Save index BEFORE read
captured_data, bufIdx = shm_interface.read_data_from_shm(
shm_reading, bufIdx, tracer_msgs_identities)
print(f" [NR Data Sync] NR Data (Frame:{captured_data['frame']}, Slot:{captured_data['slot']}) | "
f"Target (Frame:{sync_info['frame']}, Slot:{sync_info['slot']})")
if (captured_data["frame"] == sync_info["frame"] and
captured_data["slot"] == sync_info["slot"]):
print(" [NR Data Sync] Sync pass")
return saved_bufIdx # Return index pointing to the matching message
if timeout_sync and time.time() > timeout_sync:
raise Exception(
"ERROR: Data Recording NO Sync Found, check Tracer Services if they are connected!")
def send_5g_nr_msgs_request(barrier, shm_writing, shm_reading, args):
# send Tracer Control Message request to T-Tracers Apps
# -------------------------------------------
# It consists of the following fields:
# Record action
# Number of requested Tracer Messages
# Requested Tracer Messages ID 1, …, ID N
# Number of records to be recorded in slots
# Start SFN: Frame Index to start data collection from it, but not yet used
shm_interface.write_shm_task(barrier,shm_writing, args)
def generic_data_conversion_service(node_shm, config_meta_data, tracer_nodes, sync_info=None):
"""Generic data conversion service for N tracer nodes.
Args:
node_shm: dict {node_id: {"reading": shm, "writing": shm}}
config_meta_data: Config metadata dict
tracer_nodes: dict {node_id: args} for nodes with tracer messages
sync_info: Optional sync info dict with 'frame' and 'slot'. If provided,
each node's buffer index is synced to this frame/slot.
"""
record_idx = 0
prev_frame = -1
prev_slot = -1
tracer_msgs_identities = config_meta_data["data_recording_config"]["tracer_msgs_identities"]
global_info = config_meta_data["data_recording_config"]["global_info"]
# Find sync buffer indices for each node if sync_info provided
node_buf_idx = {}
for node_id in tracer_nodes:
if sync_info:
print(f" [NR Data Sync] Finding memory index for node {node_id}")
node_buf_idx[node_id] = find_sync_buffer_index(
node_shm[node_id]["reading"], sync_info, tracer_msgs_identities, timeout_sec=5)
else:
node_buf_idx[node_id] = 0
# Determine num_records from first tracer node
num_records = next(iter(tracer_nodes.values())).num_records
print("\n--- Data Conversion: Reading data from T-tracer Applications ---")
for node_id, args in tracer_nodes.items():
print(f" [Data Conversion] {node_id}: Requested tracer messages: {args.num_requested_tracer_msgs}")
# Read data from all tracer nodes
while True:
if DEBUG_WIRELESS_RECORDED_DATA:
print("\nRecord number: ", record_idx)
if DEBUG_BUFFER_READING:
for nid in tracer_nodes:
print(f" Buffer Index {nid}: {node_buf_idx[nid]}")
time.sleep(0.0035)
global_info["record_idx"] = record_idx
all_metafiles = []
for i, (node_id, args) in enumerate(tracer_nodes.items()):
buf_idx, metafiles, global_info = read_and_store_tracer_messages(
node_shm[node_id]["reading"], node_buf_idx[node_id],
args.num_requested_tracer_msgs, config_meta_data, global_info,
update_timestamp=(i == 0))
node_buf_idx[node_id] = buf_idx
all_metafiles.extend(metafiles)
# Generate SigMF collection file
if config_meta_data["data_recording_config"]["enable_saving_tracer_messages_sigmf"]:
data_storage_path = config_meta_data["data_recording_config"]["data_storage_path"]
description = global_info["description"]
sigmf_interface.save_sigmf_collection(
all_metafiles, global_info, description, data_storage_path)
frame = global_info["frame"]
slot = global_info["slot"]
if frame != prev_frame or slot != prev_slot:
record_idx += 1
if record_idx >= num_records:
break
prev_frame = frame
prev_slot = slot
# -------------------------------------------
# Cleanup — ensures shared memory and resources are released even on Ctrl+C
# -------------------------------------------
# Module-level dict populated at runtime with resources that need cleanup.
# Each key is optional; the cleanup function checks before acting.
_cleanup_state = {"done": False}
def cleanup_resources():
"""Release T-tracer shared memory resources."""
if _cleanup_state["done"]:
return
_cleanup_state["done"] = True
print("\nCleaning up resources...")
tracer_nodes = _cleanup_state.get("tracer_nodes", {})
node_shm = _cleanup_state.get("node_shm", {})
for node_id in tracer_nodes:
try:
tracer_nodes[node_id].action = "quit"
shm_interface.write_shm(node_shm[node_id]["writing"], tracer_nodes[node_id])
time.sleep(0.5)
shm_interface.detach_shm(node_shm[node_id]["reading"])
shm_interface.detach_shm(node_shm[node_id]["writing"])
shm_interface.remove_shm(node_shm[node_id]["reading"])
shm_interface.remove_shm(node_shm[node_id]["writing"])
except Exception as e:
print(f"Warning: {node_id} shared memory cleanup error: {e}")
def register_cleanup():
"""Register cleanup_resources with atexit and signal handlers."""
atexit.register(cleanup_resources)
def _signal_handler(signum, frame):
print(f"\nSignal {signum} received, shutting down...")
cleanup_resources()
sys.exit(130)
signal.signal(signal.SIGINT, _signal_handler)
signal.signal(signal.SIGTERM, _signal_handler)
if __name__ == "__main__":
# -------------------------------------------
# Data Control Service
## -------------------------------------------
# ------------- Configuration --------------
# ------------------------------------------
# Data Recording Configuration
data_recording_config_file = "config/config_data_recording.json"
# -------------------------------------------
# Configuration
# -------------------------------------------
# Read and parse the JSON file
with open(data_recording_config_file, "r") as file:
config_meta_data = json.load(file)
# -------------------------------------------
# Generic node-based configuration
# -------------------------------------------
config_meta_data, all_node_args = config_interface.get_data_recording_config(config_meta_data)
_nodes_cfg = config_meta_data["data_recording_config"]["nodes"]
# Build tracer_nodes: only nodes with requested tracer messages
tracer_nodes = {nid: args for nid, args in all_node_args.items()
if args.requested_tracer_messages}
# Validate: at least one node must have tracer messages
any_tracer = bool(tracer_nodes)
if not any_tracer:
raise Exception("ERROR: No requested tracer messages IDs are provided")
# -------------------------------------------
# Attach shared memory for each tracer node
# -------------------------------------------
node_shm = {} # {node_id: {"reading": shm, "writing": shm}}
for node_id, args in tracer_nodes.items():
node_cfg = next(n for n in _nodes_cfg if n["id"] == node_id)
shm_cfg = node_cfg.get("shared_mem_config", {})
read_path = shm_cfg.get("read_path")
write_path = shm_cfg.get("write_path")
proj_id = shm_cfg.get("project_id")
if not read_path or not write_path or proj_id is None:
raise Exception(
f"ERROR: Node '{node_id}' has tracer messages but missing 'shared_mem_config' "
f"(read_path, write_path, project_id) in config.")
shm_writing = shm_interface.attach_shm(write_path, proj_id)
shm_reading = shm_interface.attach_shm(read_path, proj_id)
node_shm[node_id] = {"reading": shm_reading, "writing": shm_writing}
# get general message header list
general_msg_header_list, general_message_header_length = \
data_recording_messages_def.get_general_msg_header_list()
# Add supported OAI Tracer Messages
config_meta_data["data_recording_config"]["supported_oai_tracer_messages"] = supported_oai_tracer_messages
# Add global info
config_meta_data["data_recording_config"]["global_info"] = global_info
# -------------------------------------------
# Initialization
# -------------------------------------------
# send Tracer Control Message request to T-Tracers Apps
# It consists of the following fields:
# Config action
# IP address length
# IP address
# Port Number
# Configure T-Tracers via shared memory for each tracer node
for node_id, args in tracer_nodes.items():
args.action = "config"
shm_interface.write_shm(node_shm[node_id]["writing"], args)
time.sleep(0.5) # wait for the config to be applied
# -------------------------------------------
# Execution
# -------------------------------------------
# send Tracer Control Message request to T-Tracers Apps
# -------------------------------------------
# It consists of the following fields:
# Record action
# Number of requested Tracer Messages
# Requested Tracer Messages ID 1, …, ID N
# Number of records to be recorded in slots
# Start SFN: Frame Index to start data collection from it, useful for future
# data sync between gNB and UE but not yet used
# -------------------------------------------
# -------- Data Collection Service ----------
# -------------------------------------------
print("Args:")
for node_id, args in tracer_nodes.items():
args.action = "record"
print(f" {node_id} args: ", args)
# Pre-create thread pool if user would like to create a for loop for N iterations
# Outside the loop to avoid initialization overhead.
# max_workers and barrier_parties are kept identical: every thread that
# enters the pool must also call barrier.wait(), so pool size == barrier size.
total_thread_count = len(tracer_nodes)
thread_pool = concurrent.futures.ThreadPoolExecutor(max_workers=total_thread_count)
barrier = threading.Barrier(total_thread_count) if total_thread_count > 0 else None
# Populate cleanup state and register signal handlers
_cleanup_state.update({
"tracer_nodes": tracer_nodes,
"node_shm": node_shm,
})
register_cleanup()
# Start data recording iterations
start_time = time.time()
print("Send data logging request us:", datetime.now().strftime("%Y%m%d-%H%M%S%f"))
# -------------------------------------------
# Submit NR tracer threads for all tracer nodes
# -------------------------------------------
futures = []
for node_id, args in tracer_nodes.items():
shm = node_shm[node_id]
tracer_thread = thread_pool.submit(
send_5g_nr_msgs_request, barrier, shm["writing"], shm["reading"], args)
futures.append(tracer_thread)
# Wait for all threads to complete
concurrent.futures.wait(futures)
# -------------------------------------------
# Check data availability for each tracer node
# -------------------------------------------
bufIdx = 0
timeout = 10 # 10 seconds from now
for node_id in tracer_nodes:
is_data_in_memory = shm_interface.is_data_available_in_memory(
node_shm[node_id]["reading"], bufIdx, general_message_header_length, timeout)
if not is_data_in_memory:
print(f"Error: {node_id}: Check T-Tracer APP, check IPs and Ports")
print(f"Error: {node_id}: If IPs and Ports are correct, re-run the hanging app. "
"It seems the socket was not closed properly")
raise Exception(
f"ERROR: Time out, check if {node_id} T-Tracer APP connected to stack")
# -------------------------------------------
# Sync and convert data
# -------------------------------------------
common_utils.write_config_data_recording_app_json(config_meta_data)
sync_info = {}
# Step 1: Sync NR tracer nodes (find latest common frame/slot across all NR sources)
if len(tracer_nodes) > 1:
node_shm_readings = {nid: node_shm[nid]["reading"] for nid in tracer_nodes}
sync_info = sync_service.sync_multiple_nr_nodes(node_shm_readings)
print(f"\n--- NR Node Sync: Sync point: frame={sync_info['frame']}, slot={sync_info['slot']} ---")
# Step 2: Read and store NR tracer data
if tracer_nodes:
do_sync = bool(sync_info)
generic_data_conversion_service(
node_shm, config_meta_data, tracer_nodes, sync_info if do_sync else None)
# measure Elapsed time
time_elapsed = time.time() - start_time
time_elapsed_ms = int(time_elapsed * 1000)
print(
"Elapsed time of getting Requested Messages and writing data and meta data files:",
colored(time_elapsed_ms, "yellow"),
"ms",)
# Shutdown thread pool after all iterations complete
thread_pool.shutdown(wait=True)
# Run cleanup (also registered with atexit and signal handlers for abnormal exits)
cleanup_resources()
print("End of the RF Data Recording API")

View File

@@ -1,11 +1,12 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Common utilities of Data Recording App
# brief Data Recording common utilities
import os
import json
def real_to_complex(real_vector):
# Ensure the length of the real vector is even
if len(real_vector) % 2 != 0:
@@ -33,7 +34,7 @@ def write_config_data_recording_app_json(config_meta_data):
# Specify the file name
output_file = (
config_meta_data["data_recording_config"]["data_storage_path"]
+ "config_data_recording_app_extended.json"
+ "config_data_recording_app.json"
)
# Ensure the directory exists
os.makedirs(os.path.dirname(output_file), exist_ok=True)
@@ -42,6 +43,6 @@ def write_config_data_recording_app_json(config_meta_data):
with open(output_file, "w") as file:
try:
json.dump(config_meta_data, file, indent=4)
print(f" [Config] JSON file saved: {output_file}")
print(f"JSON file created successfully at {output_file}")
except Exception as e:
print(f" [Config] Failed to create JSON file: {e}")
print(f"Failed to create JSON file: {e}")

View File

@@ -1,17 +1,18 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Configuration Interface of Data Recording App
# brief Data Recording App Configuration Interface
import yaml
import json
import argparse
# Function to read the main configuration file in JSON format
def read_main_config_file_json(main_config_file: str) -> dict:
"""
Reads main config file.
"""
# read general parameter set from yaml config file
with open(main_config_file, "r") as file:
main_config = json.load(file)
@@ -19,11 +20,22 @@ def read_main_config_file_json(main_config_file: str) -> dict:
return main_config
# Function to read the main configuration file in YAML format
def read_main_config_file(main_config_file: str) -> dict:
"""
Reads main config file.
"""
# read general parameter set from yaml config file
with open(main_config_file, "r") as file:
main_config = yaml.load(file, Loader=yaml.Loader)
return main_config
# Function to parse the OAI T_messages file and get the index of a given string
def parse_message_file(file_path):
with open(file_path, "r") as file:
content = file.readlines()
# Extract lines that start with 'ID' and remove the 'ID = ' prefix
tracer_msgs_identities = [
line.strip().replace("ID = ", "")
@@ -53,18 +65,12 @@ def get_requested_tracer_msgs_indices(requested_tracer_messages, tracer_msgs_ide
return req_tracer_msgs_indices
# Helper: return the first node in the nodes list whose type matches the given type string
def get_node_by_type(nodes, node_type):
for node in nodes:
if node.get("type") == node_type:
return node
return None
# Function to get the data recording configuration.
# Returns (config_meta_data, node_args_dict) where node_args_dict is
# { node_id: Namespace } with one entry per node defined in the config.
# Function to get the data recording configuration
def get_data_recording_config(config_meta_data):
parser = argparse.ArgumentParser(description="request messages IDs")
ue_args = parser.parse_args()
gnb_args = parser.parse_args()
# get Tracer Messages IDs from the T-Tracer Messages file
# MSG list order should be similar to the T-Tracer Messages txt file
# Be sure that you are using the same T_messages file that is used in the OAI project
@@ -75,33 +81,44 @@ def get_data_recording_config(config_meta_data):
config_meta_data["data_recording_config"]["t_tracer_message_definition_file"])
config_meta_data["data_recording_config"]["tracer_msgs_identities"] = tracer_msgs_identities
nodes = config_meta_data["data_recording_config"]["nodes"]
# get requested tracer messages indices for gNB
gnb_args.requested_tracer_messages = \
config_meta_data["data_recording_config"]["base_station"]["requested_tracer_messages"]
# get requested tracer messages indices for UE
ue_args.requested_tracer_messages = \
config_meta_data["data_recording_config"]["user_equipment"]["requested_tracer_messages"]
# Build per-node args generically
node_args_dict = {}
for node in nodes:
parser = argparse.ArgumentParser(description="request messages IDs")
node_args = parser.parse_args()
# check if gnb_requested_tracer_messages is not empty
if gnb_args.requested_tracer_messages:
config_meta_data["data_recording_config"]["base_station"][
"req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
gnb_args.requested_tracer_messages, tracer_msgs_identities)
node_id = node.get("id")
node_args.node_id = node_id
node_args.type = node.get("type")
node_args.requested_tracer_messages = node.get("requested_tracer_messages", [])
# get gNB Trace Messages
gnb_args.num_records = config_meta_data["data_recording_config"]["num_records"]
gnb_args.start_frame_number = config_meta_data["data_recording_config"]["start_frame_number"]
gnb_args.req_tracer_msgs_indices = \
config_meta_data["data_recording_config"]["base_station"]["req_tracer_msgs_indices"]
gnb_args.num_requested_tracer_msgs = len(gnb_args.req_tracer_msgs_indices)
# Split the string into IP and port
gnb_args.IPaddress, gnb_args.port = config_meta_data["data_recording_config"][
"tracer_service_baseStation_address"].split(":")
gnb_args.bytes_IPaddress = bytes(gnb_args.IPaddress, "utf-8")
if node_args.requested_tracer_messages:
node["req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
node_args.requested_tracer_messages, tracer_msgs_identities)
# check if ue_requested_tracer_messages is not empty
if ue_args.requested_tracer_messages:
config_meta_data["data_recording_config"]["user_equipment"][
"req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
ue_args.requested_tracer_messages, tracer_msgs_identities)
node_args.num_records = config_meta_data["data_recording_config"]["num_records"]
node_args.start_frame_number = config_meta_data["data_recording_config"]["start_frame_number"]
node_args.req_tracer_msgs_indices = node["req_tracer_msgs_indices"]
node_args.num_requested_tracer_msgs = len(node_args.req_tracer_msgs_indices)
# Split the string into IP and port
tracer_addr = node.get("tracer_service_address", "")
if tracer_addr:
node_args.IPaddress, node_args.port = tracer_addr.split(":")
node_args.bytes_IPaddress = bytes(node_args.IPaddress, "utf-8")
# get UE Trace Messages
ue_args.num_records = config_meta_data["data_recording_config"]["num_records"]
ue_args.start_frame_number = config_meta_data["data_recording_config"]["start_frame_number"]
ue_args.req_tracer_msgs_indices = \
config_meta_data["data_recording_config"]["user_equipment"]["req_tracer_msgs_indices"]
ue_args.num_requested_tracer_msgs = len(ue_args.req_tracer_msgs_indices)
ue_args.IPaddress, ue_args.port = config_meta_data["data_recording_config"][
"tracer_service_userEquipment_address"].split(":")
ue_args.bytes_IPaddress = bytes(ue_args.IPaddress, "utf-8")
node_args_dict[node_id] = node_args
return config_meta_data, node_args_dict
return config_meta_data, gnb_args, ue_args

View File

@@ -3,32 +3,6 @@
# ---------------------------------------------------------------------
# brief defination of captured data recording messages
# Get Common Sync Header - number of bytes
def get_sync_header_msg_list():
"""
shared memory layout written from the app:
=================================
msg_id (uint8) message type ID
frame (uint16)
slot (uint8)
unix_capture_ts_sec (uint32) Unix epoch seconds
unix_capture_ts_nsec (uint32) nanoseconds [0, 999999999]
"""
# Get Common Sync Header - number of bytes
sync_header_msg = {
"msg_id": 2,
"frame": 2,
"slot": 1,
"unix_capture_ts_sec": 4,
"unix_capture_ts_nsec": 4,
}
# initial number of bytes to read to get data
sync_header_msg_length = 0
for key, value in sync_header_msg.items():
sync_header_msg_length = sync_header_msg_length + value
return sync_header_msg, sync_header_msg_length
# Data Collection Trace Messages - General message structure - number of bytes
def get_general_msg_header_list():
"""
@@ -37,8 +11,8 @@ def get_general_msg_header_list():
msg_id (uint8) message type ID
frame (uint16)
slot (uint8)
unix_capture_ts_sec (uint32) Unix epoch seconds
unix_capture_ts_nsec (uint32) nanoseconds [0, 999999999]
datetime_yyyymmdd (uint32)
datetime_hhmmssmmm (uint32)
frame_type (uint8)
freq_range (uint8)
subcarrier_spacing (uint8)
@@ -72,8 +46,8 @@ def get_general_msg_header_list():
"msg_id": 2,
"frame": 2,
"slot": 1,
"unix_capture_ts_sec": 4,
"unix_capture_ts_nsec": 4,
"datetime_yyyymmdd": 4,
"datetime_hhmmssmmm": 4,
"frame_type": 1,
"freq_range": 1,
"subcarrier_spacing": 1,

View File

@@ -1,348 +0,0 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Shared Memory Interface for Data Recording App
import sysv_ipc as ipc
import time
import struct
from lib import data_recording_messages_def
import numpy as np
from lib import common_utils
DEBUG_WIRELESS_RECORDED_DATA = True
DEBUG_BUFFER_READING = False
# initialize shared memory
def attach_shm(shm_path, project_id):
key = ipc.ftok(shm_path, project_id)
shm = ipc.SharedMemory(key, 0, 0)
shm.attach(0, 0)
return shm
def detach_shm(shm):
try:
shm.detach()
print("Shared memory detached successfully.")
except ipc.ExistentialError:
print("Shared memory segment does not exist.")
def remove_shm(shm):
try:
shm.remove()
print("Shared memory removed successfully.")
except ipc.ExistentialError:
print("Shared memory segment does not exist.")
# Write Tracer Control Message
def write_shm(shm, args):
# Note: Big Endian >, Little Endian <
# Note: unsigned char B, signed char b, short h, int I, long long q
# Note: float f, double d, string s, char c, bool ?
# 1: config
# 2: record
# 3: quit
print(f" [Shared Memory] Write action: {args.action}")
if args.action == "config":
# Determine the length of the IP address
ip_length = len(args.bytes_IPaddress) + 1 # String terminator
# Construct the format string dynamically
format_string = f"{ip_length}s"
shm.write(
# Config action
struct.pack("<B", 1) +
struct.pack("<B", ip_length) +
struct.pack(format_string, args.bytes_IPaddress) +
struct.pack("<h", int(args.port))
)
print(f" [T-Tracer] Config: IP={args.bytes_IPaddress}, port={args.port}")
elif args.action == "record":
shm.write(
# Record action
struct.pack("<B", int(2)) +
struct.pack("<B", args.num_requested_tracer_msgs) +
struct.pack("<{}h".format(len(args.req_tracer_msgs_indices)),
*args.req_tracer_msgs_indices,) +
struct.pack("<I", args.num_records) +
struct.pack("<h", args.start_frame_number)
)
print(f" [T-Tracer] Record: num_msgs={args.num_requested_tracer_msgs}, "
f"msg_ids={args.req_tracer_msgs_indices}, "
f"num_records={args.num_records}, "
f"start_frame={args.start_frame_number}")
elif args.action == "quit":
shm.write(struct.pack("<B", int(3))) # Quit action
print(" [T-Tracer] Quit")
else:
print("Unknown action for data recording system!")
# write shared memory task
def write_shm_task(barrier, shm_id, args):
# Wait for threads to be ready
barrier.wait()
# send request to related T-Tracer Application
write_shm(shm_id, args)
# check data if avalible in the shared memory
def is_data_available_in_memory(shm, bufIdx, general_message_header_length, timeout=10):
start_time = time.time()
while True:
buf = shm.read(bufIdx + general_message_header_length)
# Only check bytes in the range [bufIdx, bufIdx + header_length),
# not the entire buffer from 0 — earlier records would make sum > 0
n_bytes = sum(buf[bufIdx:bufIdx + general_message_header_length])
if n_bytes > 0:
if DEBUG_BUFFER_READING:
print("Data in memory: ", n_bytes, " bytes")
return True
if (time.time() - start_time) > timeout:
break
print("Data Recording App: Waiting for Measurements!")
time.sleep(1)
return False
# Read data from gNB T-tracer Application
def get_frame_slot_start(shm_reading, bufIdx, general_msg_header_list,
general_message_header_length):
buf = shm_reading.read(bufIdx + general_message_header_length)
msg_id = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("msg_id")])[0]
bufIdx += general_msg_header_list.get("msg_id")
frame = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("frame")])[0]
bufIdx += general_msg_header_list.get("frame")
slot = struct.unpack("B", buf[bufIdx: bufIdx + general_msg_header_list.get("slot")])[0]
return frame, slot
# get MSG header Info
def get_msg_header(shm_reading, bufIdx, sync_header_msg, sync_header_length):
"""Read the sync header from shared memory and return msg_id, frame, slot, and timestamp.
Args:
shm_reading: Shared memory handle.
bufIdx: Current buffer index.
sync_header_msg: Dict with field names and byte sizes (from get_sync_header_msg_list).
sync_header_length: Total byte length of the sync header.
Returns:
tuple: (msg_id, frame, slot, unix_capture_ts_sec, unix_capture_ts_nsec)
"""
buf = shm_reading.read(bufIdx + sync_header_length)
offset = bufIdx
msg_id = struct.unpack('<H', buf[offset:offset + sync_header_msg["msg_id"]])[0]
offset += sync_header_msg["msg_id"]
frame = struct.unpack('<H', buf[offset:offset + sync_header_msg["frame"]])[0]
offset += sync_header_msg["frame"]
slot = struct.unpack('B', buf[offset:offset + sync_header_msg["slot"]])[0]
offset += sync_header_msg["slot"]
unix_capture_ts_sec = struct.unpack('<I', buf[offset:offset + sync_header_msg["unix_capture_ts_sec"]])[0]
offset += sync_header_msg["unix_capture_ts_sec"]
unix_capture_ts_nsec = struct.unpack('<I', buf[offset:offset + sync_header_msg["unix_capture_ts_nsec"]])[0]
return msg_id, frame, slot, unix_capture_ts_sec, unix_capture_ts_nsec
# Get MSG ID from Shared memory
def get_msg_id_from_shm(shm_reading, bufIdx, msg_id_length):
buf = shm_reading.read(bufIdx + msg_id_length)
msg_id = struct.unpack("<H", buf[bufIdx: bufIdx + msg_id_length])[0]
return msg_id
# Read data from Shared memory based Data Conversion Service message structure
def read_msg_data_from_shm(shm, bufIdx, tracer_msgs_identities):
# read sync header to move buffer index
# get general message header list
general_msg_header_list, general_message_header_length = \
data_recording_messages_def.get_general_msg_header_list()
buf = shm.read(bufIdx + general_message_header_length)
n_bytes = sum(buf)
if n_bytes == 0:
raise Exception('ERROR: No data available in memory')
msg_id = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("msg_id")])[0]
bufIdx += general_msg_header_list.get("msg_id")
frame = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("frame")])[0]
bufIdx += general_msg_header_list.get("frame")
slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("slot")])[0]
bufIdx += general_msg_header_list.get("slot")
# get Unix capture timestamp (sec, nsec) — Unix epoch 1970
unix_capture_ts_sec = \
struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("unix_capture_ts_sec")])[0]
bufIdx += general_msg_header_list.get("unix_capture_ts_sec")
unix_capture_ts_nsec = \
struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("unix_capture_ts_nsec")])[0]
bufIdx += general_msg_header_list.get("unix_capture_ts_nsec")
# Derive timestamp in seconds and nanoseconds (already in Unix epoch)
timestamp_seconds = unix_capture_ts_sec
timestamp_nseconds = unix_capture_ts_nsec
# get frame type
frame_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("frame_type")])[0]
bufIdx += general_msg_header_list.get("frame_type")
# get frequency range
freq_range = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("freq_range")])[0]
bufIdx += general_msg_header_list.get("freq_range")
# get subcarrier spacing
subcarrier_spacing = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("subcarrier_spacing")])[0]
bufIdx += general_msg_header_list.get("subcarrier_spacing")
# get cyclic prefix
cyclic_prefix = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("cyclic_prefix")])[0]
bufIdx += general_msg_header_list.get("cyclic_prefix")
# get symbols per slot
symbols_per_slot = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("symbols_per_slot")])[0]
bufIdx += general_msg_header_list.get("symbols_per_slot")
# get Nid cell
Nid_cell = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("Nid_cell")])[0]
bufIdx += general_msg_header_list.get("Nid_cell")
# get rnti
rnti = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rnti")])[0]
bufIdx += general_msg_header_list.get("rnti")
# get rb size
rb_size = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_size")])[0]
bufIdx += general_msg_header_list.get("rb_size")
# get rb start
rb_start = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("rb_start")])[0]
bufIdx += general_msg_header_list.get("rb_start")
# get start symbol index
start_symbol_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("start_symbol_index")])[0]
bufIdx += general_msg_header_list.get("start_symbol_index")
# get number of symbols
nr_of_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nr_of_symbols")])[0]
bufIdx += general_msg_header_list.get("nr_of_symbols")
# get qam modulation order
qam_mod_order = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("qam_mod_order")])[0]
bufIdx += general_msg_header_list.get("qam_mod_order")
# get mcs index
mcs_index = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_index")])[0]
bufIdx += general_msg_header_list.get("mcs_index")
# get mcs table
mcs_table = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("mcs_table")])[0]
bufIdx += general_msg_header_list.get("mcs_table")
# get number of layers
nrOfLayers = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nrOfLayers")])[0]
bufIdx += general_msg_header_list.get("nrOfLayers")
# get transform precoding
transform_precoding = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("transform_precoding")])[0]
bufIdx += general_msg_header_list.get("transform_precoding")
# get dmrs config type
dmrs_config_type = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_config_type")])[0]
bufIdx += general_msg_header_list.get("dmrs_config_type")
# get ul dmrs symb pos
ul_dmrs_symb_pos = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("ul_dmrs_symb_pos")])[0]
bufIdx += general_msg_header_list.get("ul_dmrs_symb_pos")
# get number dmrs symbols
number_dmrs_symbols = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("number_dmrs_symbols")])[0]
bufIdx += general_msg_header_list.get("number_dmrs_symbols")
# get dmrs port
dmrs_port = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_port")])[0]
bufIdx += general_msg_header_list.get("dmrs_port")
# get dmrs scid
dmrs_scid = struct.unpack('<H', buf[bufIdx:bufIdx+general_msg_header_list.get("dmrs_scid")])[0]
bufIdx += general_msg_header_list.get("dmrs_scid")
# get nb antennas rx for gNB or nb antennas tx for UE
nb_antennas = struct.unpack('B', buf[bufIdx:bufIdx+general_msg_header_list.get("nb_antennas")])[0]
bufIdx += general_msg_header_list.get("nb_antennas")
# get number of bits
number_of_bits = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("number_of_bits")])[0]
bufIdx += general_msg_header_list.get("number_of_bits")
# get length of bytes
length_bytes = struct.unpack('<I', buf[bufIdx:bufIdx+general_msg_header_list.get("length_bytes")])[0]
bufIdx += general_msg_header_list.get("length_bytes")
# print all captured data
if DEBUG_WIRELESS_RECORDED_DATA:
print("\n" + "="*80)
print(f"UL CONFIG METADATA - {tracer_msgs_identities[msg_id]}")
print(f"Unix TS: {unix_capture_ts_sec}.{unix_capture_ts_nsec:09d} | MSG ID: {msg_id}")
print("-"*80)
print(f"Frame: {frame:<5} Slot: {slot:<5} Nid Cell: {Nid_cell:<5} RNTI: {rnti:<5}")
print(f"Frame Type: {frame_type} Freq Range: {freq_range} | SCS: {subcarrier_spacing} Hz Cyclic Prefix: {cyclic_prefix} Symbols/Slot: {symbols_per_slot}")
print("-"*80)
print(f"RB: Start={rb_start:<4} Size={rb_size:<4} | Symbol Allocation: Start={start_symbol_index:<3} Count={nr_of_symbols:<3}")
print(f"MCS: Index={mcs_index:<3} Table={mcs_table} | QAM Order: {qam_mod_order} Layers: {nrOfLayers} Transform Precoding: {transform_precoding}")
print("-"*80)
print(f"DMRS: Config Type={dmrs_config_type} Symb Pos={ul_dmrs_symb_pos} ({number_dmrs_symbols} symbols)")
print(f" Port={dmrs_port} SCID={dmrs_scid} | Antennas: {nb_antennas}")
print("-"*80)
print(f"Data: {number_of_bits} bits ({length_bytes} bytes)")
print("="*80)
# raise exception if time stamp is zero, it means that the data is not recorded yet
if unix_capture_ts_sec == 0 and unix_capture_ts_nsec == 0:
raise Exception("ERROR: Time stamp is zero, data is not recorded yet or something wrong, check logs!")
# get recorded data
buf = shm.read(bufIdx + length_bytes)
captured_data = {}
# If message is bit message, store data in bytes
# then the field number_of_bits should be not zero
if "_BITS" in tracer_msgs_identities[msg_id]:
recorded_data = struct.unpack("<" + int(length_bytes) * 'B', buf[bufIdx:bufIdx + length_bytes])
bufIdx += length_bytes
# convert data in bytes to bits
bits_vector = []
for byte in recorded_data:
bits_vector.extend([int(bit) for bit in format(int(byte), '08b')])
captured_data["sigmf_data_type"] = "ri8_le"
# convert to uint8
captured_data["recorded_data"] = np.asarray(bits_vector).astype(np.uint8)
else:
recorded_data = struct.unpack("<" + int(length_bytes/2) * 'h', buf[bufIdx:bufIdx + length_bytes])
bufIdx += length_bytes
# Convert real data to complext data
# converting list to array
recorded_data = np.asarray(recorded_data)
recorded_data_complex = common_utils.real_to_complex(recorded_data)
captured_data["sigmf_data_type"] = "cf32_le"
# convert to complex64
captured_data["recorded_data"] = recorded_data_complex.astype(np.complex64)
# store data in dictonary
captured_data["message_id"] = msg_id
captured_data["message_type"] = tracer_msgs_identities[msg_id]
captured_data["frame"] = frame
captured_data["slot"] = slot
captured_data["unix_capture_ts_sec"] = unix_capture_ts_sec
captured_data["unix_capture_ts_nsec"] = unix_capture_ts_nsec
captured_data["timestamp_seconds"] = timestamp_seconds
captured_data["timestamp_nseconds"] = timestamp_nseconds
captured_data["frame_type"] = frame_type
captured_data["freq_range"] = freq_range
captured_data["subcarrier_spacing"] = subcarrier_spacing
captured_data["cyclic_prefix"] = cyclic_prefix
# captured_data["symbols_per_slot"] = symbols_per_slot ... not used
captured_data["Nid_cell"] = Nid_cell
captured_data["rnti"] = rnti
captured_data["rb_size"] = rb_size
captured_data["rb_start"] = rb_start
captured_data["start_symbol_index"] = start_symbol_index
captured_data["nr_of_symbols"] = nr_of_symbols
captured_data["qam_mod_order"] = qam_mod_order
captured_data["mcs_index"] = mcs_index
captured_data["mcs_table"] = mcs_table
captured_data["nrOfLayers"] = nrOfLayers
captured_data["transform_precoding"] = transform_precoding
captured_data["dmrs_config_type"] = dmrs_config_type
captured_data["ul_dmrs_symb_pos"] = ul_dmrs_symb_pos
captured_data["number_dmrs_symbols"] = number_dmrs_symbols
captured_data["dmrs_port"] = dmrs_port
captured_data["dmrs_scid"] = dmrs_scid
captured_data["nb_antennas"] = nb_antennas
captured_data["number_of_bits"] = number_of_bits
return captured_data, bufIdx
# Read data from Shared memory based Data Conversion Service message structure
def read_data_from_shm(shm, bufIdx, tracer_msgs_identities):
# print buffer index
if DEBUG_BUFFER_READING:
print("Buffer Index: ", bufIdx)
# Get MSG ID by reading Sync Header
sync_header_msg_list, sync_header_message_length = \
data_recording_messages_def.get_sync_header_msg_list()
msg_id_length = sync_header_msg_list.get("msg_id")
msg_id = get_msg_id_from_shm(shm, bufIdx, msg_id_length)
if DEBUG_WIRELESS_RECORDED_DATA:
print(f" [T-Tracer] Reading MSG ID: {msg_id} - {tracer_msgs_identities[msg_id]}")
# Get MSG data based on Data Conversion Service message structure
# UL IQ/bits messages (default UL data format)
captured_data, bufIdx = read_msg_data_from_shm(shm, bufIdx, tracer_msgs_identities)
return captured_data, bufIdx

View File

@@ -6,12 +6,11 @@
import os
import sigmf
from sigmf import SigMFFile
from datetime import datetime, timezone
# from sigmf.utils import get_data_type_str
import numpy as np
from datetime import datetime
import yaml
from sigmf import SigMFCollection
from .wireless_parameters_mapper import map_waveform_metadata_to_wireless_dic, derive_remaining_5gnr_metadata, STANDARDS
from .config_interface import get_node_by_type
DEBUG_WIRELESS_RECORDED_DATA = True
"""
SERIALIZATION_SCHEMES = {
@@ -25,29 +24,21 @@ SERIALIZATION_SCHEMES = {
"tx-payload-bits": ["bits", "subcarriers", "ofdm_symbols"],
}
"""
STANDARDS = {"5gnr_oai": "5gnr"}
def time_stamp_formating(unix_capture_ts_sec, unix_capture_ts_nsec, datetime_offset):
# Convert Unix epoch (sec, nsec) to a datetime object (UTC).
# Python datetime only supports µs, so we format the nanosecond
# fractional part manually to preserve the full 9-digit resolution.
dt_obj = datetime.fromtimestamp(unix_capture_ts_sec, tz=timezone.utc)
def time_stamp_formating(time_stamp, datetime_offset):
# Parse the input string into a datetime object
time_stamp_ms_obj = datetime.strptime(time_stamp, "%Y%m%d_%H%M%S%f")
# Format with nanosecond fractional seconds (9 digits)
time_stamp_ns_iso = (
dt_obj.strftime("%Y_%m_%dT%H:%M:%S")
+ f".{unix_capture_ts_nsec:09d}"
+ datetime_offset
# Format the datetime object into the desired output format with milliseconds
time_stamp_ms_iso = (
time_stamp_ms_obj.strftime("%Y_%m_%dT%H:%M:%S.%f")[:-3] + datetime_offset
)
time_stamp_ns_file_name = time_stamp_ns_iso.replace(":", "_").replace(".", "_")
time_stamp_ms = time_stamp_ms_iso
time_stamp_ms_file_name = time_stamp_ms_iso.replace(":", "_").replace(".", "_")
return time_stamp_ns_iso, time_stamp_ns_file_name
def _get_node_meta_data(config_meta_data, role):
"""Return the meta_data dict for the first node whose type matches the given type string."""
node = get_node_by_type(config_meta_data["data_recording_config"]["nodes"], role)
return node["meta_data"] if node else {}
return time_stamp_ms, time_stamp_ms_file_name
def create_serialization_metadata(serialization_scheme, data_source: str,
@@ -55,10 +46,6 @@ def create_serialization_metadata(serialization_scheme, data_source: str,
"""Creates dict that specifies the serialization metadata."""
# retrieve parameter from LinkSimulator config
# if serialization_scheme empty, return empty dict, It is a meta-data only message
if not serialization_scheme:
return {}
if data_source == "5gnr_oai":
num_ofdm_symbol = link_sim_parameters["nr_of_symbols"]
num_subcarriers = link_sim_parameters["rb_size"] * 12
@@ -85,73 +72,131 @@ def create_serialization_metadata(serialization_scheme, data_source: str,
return serialization_dict
# map OAI config data to Wireless Dictionary Parameter Map - SigMF metadata
def create_system_components_metadata(captured_data, config_meta_data):
def map_metadata_to_sigmf_format(scope, waveform_generator, parameter_map_file, captured_data):
"""
Maps metadata from Waveform creator to API and SigMF format.
The used parameters and the mapping pairs are specified in a separate YAML file
that must be provided as well.
"""
# read waveform parameter map from yaml file
dir_path = os.path.dirname(__file__)
src_path = os.path.split(dir_path)[0]
with open(os.path.join(src_path, parameter_map_file), "r") as file:
parameter_map_dic = yaml.load(file, Loader=yaml.Loader)
# preallocate target dict
sigmf_metadata_dict = {}
# get standard and name of generator
standard_key = parameter_map_dic["waveform_generator"][waveform_generator]
generator = standard_key["generator"]
if scope == "tx":
parameter_map_dic = parameter_map_dic["transmitter"][
STANDARDS[waveform_generator]
]
elif scope == "channel":
parameter_map_dic = parameter_map_dic["channel"]
elif scope == "rx":
parameter_map_dic = parameter_map_dic["receiver"]
else:
raise Exception(
f"Invalid mapping scope '{scope}'! Only 'tx', 'channel' and 'rx' are valid!"
)
# check if standard key is given
if parameter_map_dic is None:
raise Exception(
"Invalid standard key: "
"Name should be corrected or added to wireless_link_parameter_map.yaml, given: "
f"{waveform_generator}"
)
for parameter_pair in parameter_map_dic:
# check if key for chosen simulator even exists
if waveform_generator + "_parameter" in parameter_pair.keys():
# only continue with mapping from file if direct equivalent exists
if parameter_pair[waveform_generator + "_parameter"]["name"]:
# It is not necessary to get all parameters from wireless_link_parameter_map.yaml
# in captured_data since some parameters related to DL or UL only
if (parameter_pair[waveform_generator + "_parameter"]["name"] in captured_data.keys()):
# extract value from waveform config source
value = captured_data[
parameter_pair[waveform_generator + "_parameter"]["name"]]
# additional mapping if parameter values should come from a discrete set of values
if ("value_map" in parameter_pair[waveform_generator + "_parameter"].keys()):
value = parameter_pair[waveform_generator + "_parameter"]["value_map"][value]
# write to target dictionary for SigMF
sigmf_metadata_dict[parameter_pair["sigmf_parameter_name"]] = value
else:
raise Exception(
f"Incomplete specification in field '{waveform_generator}_parameter'!")
# else: # fill_non_explicit_fields
# waveform_config[parameter_pair["sigmf_parameter_name"]] = "none"
if not sigmf_metadata_dict:
raise Exception(
"""ERROR: Check captured meta-data or provided config and meta data """)
# check for non-JSON-serializable data types
def isfloat(NumberString):
try:
float(NumberString)
return True
except ValueError:
return False
for key, value in sigmf_metadata_dict.items():
if isinstance(value, np.integer):
sigmf_metadata_dict[key] = int(value)
elif isinstance(value, (np.float16, np.float32, np.float64)):
sigmf_metadata_dict[key] = np.format_float_positional(value, trim="-")
elif isinstance(value, int):
sigmf_metadata_dict[key] = int(value)
elif isinstance(value, float):
# store value in decimal and not in scientific notation
sigmf_metadata_dict[key] = float(value)
elif isinstance(value, str) and key != "standard":
# convert string to lower case
sigmf_metadata_dict[key] = value.lower()
if isfloat(value):
if value.isdigit():
sigmf_metadata_dict[key] = int(float(value))
elif value.replace(".", "", 1).isdigit() and value.count(".") < 2:
sigmf_metadata_dict[key] = float(value)
return sigmf_metadata_dict, generator
def create_system_components_metadata(waveform_generator, parameter_map_file, captured_data):
"""Creates system components (TX, channel, RX) metadata that will reside in the annotations."""
# map OAI config data to Wireless Dictionary Parameter Map - SigMF metadata
waveform_generator = config_meta_data["data_recording_config"]["global_info"][
"waveform_generator"]
parameter_map_file = config_meta_data["data_recording_config"]["parameter_map_file"]
# map metadata of waveform generator to Wireless Dictionary Parameter Map - SigMF metadata
signal_metadata, generator = map_waveform_metadata_to_wireless_dic(
"tx", waveform_generator, parameter_map_file, captured_data)
# derive remaining Signal metadata
signal_metadata = derive_remaining_5gnr_metadata(captured_data, signal_metadata)
# map metadata of waveform generator to SigMF format
signal_info, generator = map_metadata_to_sigmf_format(
"tx", waveform_generator, parameter_map_file, captured_data
)
tx_metadata = {
"signal:detail": {
"standard": STANDARDS[waveform_generator],
"generator": generator,
STANDARDS[waveform_generator]: signal_metadata,
STANDARDS[waveform_generator]: signal_info,
}
}
channel_metadata = {} # will be filled later
rx_metadata = {} # will be filled later
# get meta data from config file
base_station_meta_data = _get_node_meta_data(config_meta_data, "gnb")
user_equipment_meta_data = _get_node_meta_data(config_meta_data, "ue")
# Set number of antennas based on link direction and captured side
# Find number of antennas from global info on other side of the link
if "UL" in captured_data["message_type"]:
# On UE side, It is TX antennas
# On gNB side, It is RX antennas
if "UE" in captured_data["message_type"]:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"num_tx_antennas"] = captured_data["nb_antennas"]
rx_metadata.update({"num_rx_antennas": base_station_meta_data["num_rx_antennas"]})
else:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"num_tx_antennas"] = user_equipment_meta_data["num_tx_antennas"]
rx_metadata.update({"num_rx_antennas": captured_data["nb_antennas"]})
if "DL" in captured_data["message_type"]:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"link_direction"] = "downlink"
# On UE side, It is RX antennas
# On gNB side, It is TX antennasr
if "UE" in captured_data["message_type"]:
rx_metadata["signal:detail"][STANDARDS[waveform_generator]]["num_rx_antennas"] = \
captured_data["nb_antennas"] ## TO DO check key name: no DL message from UE yet
tx_metadata.update({"num_tx_antennas": base_station_meta_data["num_tx_antennas"]})
else:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"num_tx_antennas"] = captured_data["nrOfLayers"]
rx_metadata.update({"num_rx_antennas": user_equipment_meta_data["num_rx_antennas"]})
return tx_metadata, channel_metadata, rx_metadata
def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, idx):
"""
Compiles and saves provided data and metadata into SigMF file format.
"""
# get meta data from config file
base_station_meta_data = _get_node_meta_data(config_meta_data, "gnb")
user_equipment_meta_data = _get_node_meta_data(config_meta_data, "ue")
base_station_meta_data = config_meta_data["data_recording_config"]["base_station"][
"meta_data"]
user_equipment_meta_data = config_meta_data["data_recording_config"][
"user_equipment"]["meta_data"]
# Check the receive target path is valid, else create folder
data_storage_path = config_meta_data["data_recording_config"]["data_storage_path"]
@@ -161,34 +206,111 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
# Write recorded data to file
# Get time stamp
time_stamp, time_stamp_file_name = time_stamp_formating(
captured_data["unix_capture_ts_sec"], captured_data["unix_capture_ts_nsec"],
global_info["datetime_offset"])
time_stamp_ms, time_stamp_ms_file_name = time_stamp_formating(
captured_data["time_stamp"], global_info["datetime_offset"])
# Map OAI Message Name to SigMF Message Name
file_name_prefix = config_meta_data["data_recording_config"][
"supported_oai_tracer_messages"][captured_data["message_type"]]["file_name_prefix"]
recorded_data_file_name = (
file_name_prefix + "-rec-" + str(idx) + "-" + time_stamp_file_name)
file_name_prefix + "-rec-" + str(idx) + "-" + time_stamp_ms_file_name)
dataset_filename = recorded_data_file_name + ".sigmf-data"
dataset_file_path = os.path.join(data_storage_path, dataset_filename)
if DEBUG_WIRELESS_RECORDED_DATA:
print(dataset_file_path)
print(dataset_file_path)
captured_data["recorded_data"].tofile(dataset_file_path)
# ----------------------------------------------------
# map OAI config data to Wireless Dictionary Parameter Map - SigMF metadata
# map OAI config data to SigMF metadata
waveform_generator = config_meta_data["data_recording_config"]["global_info"][
"waveform_generator"]
tx_metadata, channel_metadata, rx_metadata = create_system_components_metadata(captured_data, config_meta_data)
parameter_map_file = config_meta_data["data_recording_config"]["parameter_map_file"]
tx_metadata, channel_metadata, rx_metadata = create_system_components_metadata(
waveform_generator, parameter_map_file, captured_data)
# Add other OAI metadata to SigMF metadata
# ----------------------------------------------------
# Set 1: Parameters needs to be derived from OAI message
# ----------------------------------------------------
# PUSCH DMRS: OFDM symbol start index within slot
get_pusch_dmrs_start_ofdm_symbol = 0
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch_dmrs:ofdm_symbol_idx"] = []
for symbol in range(captured_data["start_symbol_index"],
captured_data["start_symbol_index"] + captured_data["nr_of_symbols"]):
dmrs_symbol_flag = (captured_data["ul_dmrs_symb_pos"] >> symbol) & 0x01
if dmrs_symbol_flag and not get_pusch_dmrs_start_ofdm_symbol:
get_pusch_dmrs_start_ofdm_symbol = 1
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch_dmrs:start_ofdm_symbol"] = symbol
if dmrs_symbol_flag:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch_dmrs:ofdm_symbol_idx"].append(symbol) # Append symbol to the list
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch_dmrs:duration_num_ofdm_symbols"] = 1
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch_dmrs:num_add_positions"] = (captured_data["number_dmrs_symbols"] - 1)
# If the message is a BIT message, add number of bits to the signal info
# "UE_PHY_UL_SCRAMBLED_TX_BITS", "GNB_PHY_UL_PAYLOAD_RX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
if "_BITS" in captured_data["message_type"]:
# if "BIT" in captured_data["message_type"]:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]]["num_bits"] = (
captured_data["number_of_bits"])
# If the message is captured from UE, it is UPLink message,
# so number of antennas is num of Tx antennas
# number of antennas on Rx side should be read from global_info given by user
if "UE" in captured_data["message_type"]:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"num_tx_antennas"
] = captured_data["nb_antennas"]
rx_metadata.update(
{"num_rx_antennas": base_station_meta_data["num_rx_antennas"]}
)
# If the message is captured from gNB, it is UPLink message,
# so number of antennas is num of Rx antennas
# number of antennas on Tx side should be read from global_info given by user
else:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"num_tx_antennas"] = user_equipment_meta_data["num_tx_antennas"]
rx_metadata.update({"num_rx_antennas": captured_data["nb_antennas"]})
# Check if the message type contains "UL"
if "UL" in captured_data["message_type"]:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"link_direction"] = "uplink"
else:
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"link_direction"] = "downlink"
# ----------------------------------------------------
# Set 2: Parameters that is hardcoded
# ----------------------------------------------------
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch:content"] = "compliant"
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch:mapping_type"] = "B"
tx_metadata["signal:detail"][STANDARDS[waveform_generator]]["num_slots"] = 1
if config_meta_data["test_config"]["test_mode"] == "rf_simulation":
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch:payload_bit_pattern"] = "random"
elif config_meta_data["test_config"]["test_mode"] == "rf_real_time":
tx_metadata["signal:detail"][STANDARDS[waveform_generator]][
"pusch:payload_bit_pattern"] = "zeros"
else:
raise Exception("ERROR: Test mode is not supported in SigMF formatting")
# ----------------------------------------------------
# Read mean parameters
freq = config_meta_data["environment_emulation"]["target_link_config"][
"wireless_channel"]["carrierFreqValueList_hz"][0]
bandwidth = config_meta_data["data_recording_config"]["common_meta_data"][
"bandwidth"]
sample_rate = config_meta_data["data_recording_config"]["common_meta_data"]["sample_rate"]
# used_signal_bandwidth = (
# pow(10, 6) * config_meta_data["environment_emulation"]["target_link_config"]["ran_config"][
# "uplink"]["ul_used_signal_BW_MHz"])
sample_rate = config_meta_data["data_recording_config"]["common_meta_data"][
"sample_rate"]
# get signal emitter info
signal_emitter = {
@@ -200,14 +322,23 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
"sample_rate": sample_rate,
"bandwidth": bandwidth,
"gain_tx": user_equipment_meta_data["tx_gain"],
"clock_reference": config_meta_data["data_recording_config"]["common_meta_data"]["clock_reference"],
"clock_reference": config_meta_data["data_recording_config"][
"common_meta_data"
]["clock_reference"],
}
# ----------------------------------------------------
# get channel metadata
# ----------------------------------------------------
# get downlink channel info
# ch_downlink_config = config_meta_data["environment_emulation"]["target_link_config"]["wireless_channel"]["downlink"]
# ch_downlink ={"channel_model": ch_downlink_config["type"]}
# ch_downlink_model = {"channel_model": "AWGN"}
# get uplink channel info
ch_uplink_config = config_meta_data["environment_emulation"]["target_link_config"]["wireless_channel"]["uplink"]
ch_uplink_config = config_meta_data["environment_emulation"]["target_link_config"][
"wireless_channel"
]["uplink"]
# Real Time RF Emulation
if config_meta_data["test_config"]["test_mode"] == "rf_real_time":
channel_metadata.update({"emulation_mode": "ni rf real time"})
@@ -215,7 +346,7 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
channel_metadata.update(
{
"channel_model": ch_uplink_config["statistical"]["predef_channel_profile"],
"snr_esn0_db": ch_uplink_config["statistical"]["snr_db"],
"snr_esn0_db": ch_uplink_config["statistical"]["snr_db"],
}
)
@@ -230,7 +361,6 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
)
else:
raise Exception("ERROR: channel type is not supported in SigMF formatting")
# RF Simulation
elif config_meta_data["test_config"]["test_mode"] == "rf_simulation":
channel_metadata.update({"emulation_mode": "oai rf simulation"})
@@ -267,6 +397,16 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
],
}
)
# ----------------------------------------------------
# get test config
# ----------------------------------------------------
"""
test_config = {
"test_name": config_meta_data["test_config"]["test_name"],
"test_mode": config_meta_data["test_config"]["test_mode"],
"dut_type": config_meta_data["test_config"]["dut_type"],
}
"""
# Create sigmf metadata
# ----------------------
# Add global parameters to SigMF metadata
@@ -299,9 +439,10 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
# Add capture parameters to SigMF metadata
# ----------------------
serialization_scheme = config_meta_data["data_recording_config"][
"supported_oai_tracer_messages"][captured_data["message_type"]]["serialization_scheme"]
"supported_oai_tracer_messages"
][captured_data["message_type"]]["serialization_scheme"]
capture_metadata = {
sigmf.SigMFFile.DATETIME_KEY: time_stamp,
sigmf.SigMFFile.DATETIME_KEY: time_stamp_ms,
SigMFFile.FREQUENCY_KEY: freq,
**create_serialization_metadata(
serialization_scheme, waveform_generator, captured_data
@@ -345,8 +486,7 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
dataset_meta_file_path = os.path.join(data_storage_path, dataset_meta_filename)
meta.tofile(dataset_meta_file_path) # extension is optional
if DEBUG_WIRELESS_RECORDED_DATA:
print(dataset_meta_file_path)
print(dataset_meta_file_path)
return dataset_meta_file_path
@@ -373,9 +513,8 @@ def save_sigmf_collection(streams: list, global_info: dict, description: str, st
# save collection file
collection.tofile(collection_filepath)
if DEBUG_WIRELESS_RECORDED_DATA:
print("")
print(collection_filepath + ".sigmf-collection")
print("")
print(collection_filepath + ".sigmf-collection")
def load_sigmf(filename: str, storage_path: str, scope: str):
@@ -397,7 +536,7 @@ def load_sigmf(filename: str, storage_path: str, scope: str):
annotation_length = annotations_metadata[0][sigmf.SigMFFile.LENGTH_INDEX_KEY]
# get capture metadata
#capture_metadata = signal.get_capture_info(annotation_start_idx)
capture_metadata = signal.get_capture_info(annotation_start_idx)
# from source code: sigmffile.py
# "autoscale : bool, default True
@@ -412,4 +551,4 @@ def load_sigmf(filename: str, storage_path: str, scope: str):
data = signal.read_samples(
annotation_start_idx, annotation_length, autoscale=False, raw_components=True)
return data, global_metadata, annotations_metadata
return data, global_metadata, annotations_metadata

View File

@@ -1,10 +1,12 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Sync service between captured Data from 5G NR gNB and UE
# brief Sync service between captured Data from 5GNR gNB and UE
import struct
from lib import data_recording_messages_def
from lib import shared_memory_interface
DEBUG_POW_MEAS_SYNC = True
# check if first frame ahead:
@@ -27,55 +29,34 @@ def is_frame_ahead(frame1, frame2, max_frame=1023):
# find the frame and slot start
def find_frame_slot_start(dataset1_start_info, dataset2_start_info, is_dataset2_pow_monitoring):
def find_frame_slot_start(dataset1_start, dataset2_start):
"""
Function to find the frame and slot start for data sync
Args:
dataset1_start_info: Dictionary containing the start information of dataset1
dataset2_start_info: Dictionary containing the start information of dataset2
is_dataset2_pow_monitoring: Boolean indicating if dataset2 is pow monitoring
Returns:
sync_info: Dictionary containing the sync information
sync_info["frame"] = frame_start
sync_info["slot"] = slot_start
Note: if is_dataset2_pow_monitoring is true, then the slot start is taken from dataset1
Since the pow monitoring is capturing all slots while the gNB and UE are capturing only data slots
"""
sync_info = {}
if dataset1_start_info["frame"] == dataset2_start_info["frame"] :
if dataset1_start["frame"] == dataset2_start["frame"]:
frame_diff = 0
frame_start = dataset1_start_info["frame"]
if is_dataset2_pow_monitoring:
if dataset1_start_info["slot"] >= dataset2_start_info["slot"]:
slot_start = dataset1_start_info["slot"]
elif dataset1_start_info["slot"] < dataset2_start_info["slot"]:
# Go to next frame and use data slot as reference
frame_start = (frame_start + 1) % 1024
# Use the slot from dataset1 as reference
slot_start = dataset1_start_info["slot"]
else:
slot_start = max(dataset1_start_info["slot"], dataset2_start_info["slot"])
frame_start = dataset1_start["frame"]
slot_start = max(dataset1_start["slot"], dataset2_start["slot"])
elif is_frame_ahead(dataset1_start_info["frame"], dataset2_start_info["frame"]):
frame_start = dataset1_start_info["frame"]
slot_start = dataset1_start_info["slot"]
frame_diff = (dataset1_start_info["frame"]- dataset2_start_info["frame"] + 1024) % 1024
elif is_frame_ahead(dataset2_start_info["frame"], dataset1_start_info["frame"]):
frame_start = dataset2_start_info["frame"]
if is_dataset2_pow_monitoring:
if dataset1_start_info["slot"] >= dataset2_start_info["slot"]:
slot_start = dataset1_start_info["slot"]
elif dataset1_start_info["slot"] < dataset2_start_info["slot"]:
# Go to next frame and use data slot as reference
frame_start = (frame_start + 1) % 1024
# Use the slot from dataset1 as reference
slot_start = dataset1_start_info["slot"]
else:
slot_start = dataset2_start_info["slot"]
frame_diff = (dataset2_start_info["frame"] - dataset1_start_info["frame"] + 1024) % 1024
# check first if the delta time between the two datasets is larger than expected offset time.
elif is_frame_ahead(dataset1_start["frame"], dataset2_start["frame"]):
frame_start = dataset1_start["frame"]
slot_start = dataset1_start["slot"]
frame_diff = (dataset1_start["frame"]- dataset2_start["frame"] + 1024) % 1024
elif is_frame_ahead(dataset2_start["frame"], dataset1_start["frame"]):
frame_start = dataset2_start["frame"]
slot_start = dataset2_start["slot"]
frame_diff = (dataset2_start["frame"] - dataset1_start["frame"] + 1024) % 1024
# check first if the delta time between the two datasets is larger than expected offset time.
# So, the sync will not be applied
# check during the calculation of the frame the ramp-around from 1023 to 0
if abs(frame_diff) > 6:
@@ -89,34 +70,54 @@ def find_frame_slot_start(dataset1_start_info, dataset2_start_info, is_dataset2_
sync_info["slot"] = slot_start
return sync_info
# Sync data across N NR tracer nodes
def sync_multiple_nr_nodes(node_shm_readings):
"""Sync frame/slot across N NR tracer nodes.
Args:
node_shm_readings: dict {node_id: shm_reading}
# Read data from gNB T-tracer Application
def get_frame_slot_start(shm_reading, bufIdx, general_msg_header_list,
general_message_header_length):
buf = shm_reading.read(bufIdx + general_message_header_length)
msg_id = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("msg_id")])[0]
bufIdx += general_msg_header_list.get("msg_id")
frame = struct.unpack("<H", buf[bufIdx: bufIdx + general_msg_header_list.get("frame")])[0]
bufIdx += general_msg_header_list.get("frame")
slot = struct.unpack("B", buf[bufIdx: bufIdx + general_msg_header_list.get("slot")])[0]
return frame, slot
Returns:
sync_info: dict with 'frame' and 'slot' of the common sync point
# Sync data between gNB and UE
def sync_gnb_ue_captured_data(shm_reading_gnb, shm_reading_ue):
"""
sync_header_msg, sync_header_msg_length = (
data_recording_messages_def.get_sync_header_msg_list())
Function to get the sync (frame, slot) data between gNB and UE
Args:
shm_reading_gnb: Shared memory for gNB
shm_reading_ue: Shared memory for UE
Returns:
sync_info: Dictionary containing the sync information between gNB and UE
sync_info["frame"] = frame_start
sync_info["slot"] = slot_start
"""
# get general message header list
general_msg_header_list, general_message_header_length = (
data_recording_messages_def.get_general_msg_header_list())
# Get frame/slot from each node
node_start_infos = {}
for node_id, shm_reading in node_shm_readings.items():
frame, slot = shared_memory_interface.get_frame_slot_start(
shm_reading, 0, sync_header_msg, sync_header_msg_length)
node_start_infos[node_id] = {"frame": frame, "slot": slot}
print(f" [NR Node Sync] {node_id} start: frame={frame}, slot={slot}")
# Find the latest common frame/slot by pairwise comparison
node_ids = list(node_start_infos.keys())
sync_info = node_start_infos[node_ids[0]]
for i in range(1, len(node_ids)):
sync_info = find_frame_slot_start(sync_info, node_start_infos[node_ids[i]],
is_dataset2_pow_monitoring=False)
print(f" [NR Node Sync] Multi-node sync point: frame={sync_info['frame']}, slot={sync_info['slot']}")
# Read data from gNB T-tracer Application
bufIdx = 0
frame_gnb, slot_gnb = get_frame_slot_start(
shm_reading_gnb, bufIdx, general_msg_header_list, general_message_header_length)
# Read data from UE T-tracer Application
bufIdx = 0
frame_ue, slot_ue = get_frame_slot_start(
shm_reading_ue, bufIdx, general_msg_header_list, general_message_header_length)
dataset1_start = {}
dataset1_start["frame"] = frame_gnb
dataset1_start["slot"] = slot_gnb
dataset2_start = {}
dataset2_start["frame"] = frame_ue
dataset2_start["slot"] = slot_ue
# Sync data between gNB and UE
# We noticed that the maximum difference between the frame number of gNB and UE is 3 frames
# Calculate the frame difference considering the wrap-around from 1023 to 0
sync_info = find_frame_slot_start(dataset1_start, dataset2_start)
print(" gNB Start info: ", dataset1_start)
print(" UE Start info: ", dataset2_start)
print(" gNB and UE Sync info: ", sync_info)
return sync_info

View File

@@ -1,189 +0,0 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Wireless parameters mapper to map captured parameters from waveform generator to API and SigMF format based on a mapping specification in a YAML file.
# It also includes functions to derive other parameters that are not directly captured but can be derived from the captured parameters.
import os
import numpy as np
import yaml
STANDARDS = {"5gnr_oai": "5gnr"}
def map_waveform_metadata_to_wireless_dic(scope, waveform_generator, parameter_map_file, captured_data):
"""
Maps metadata from Waveform creator to API and SigMF format.
The used parameters and the mapping pairs are specified in a separate YAML file
that must be provided as well.
"""
# read waveform parameter map from yaml file
dir_path = os.path.dirname(__file__)
src_path = os.path.split(dir_path)[0]
with open(os.path.join(src_path, parameter_map_file), "r") as file:
parameter_map_dic = yaml.load(file, Loader=yaml.Loader)
# preallocate target dict
signal_metadata = {}
# get standard and name of generator
standard_key = parameter_map_dic["waveform_generator"][waveform_generator]
generator = standard_key["generator"]
if scope == "tx":
parameter_map_dic = parameter_map_dic["transmitter"][
STANDARDS[waveform_generator]
]
elif scope == "channel":
parameter_map_dic = parameter_map_dic["channel"]
elif scope == "rx":
parameter_map_dic = parameter_map_dic["receiver"]
else:
raise Exception(
f"Invalid mapping scope '{scope}'! Only 'tx', 'channel' and 'rx' are valid!"
)
# check if standard key is given
if parameter_map_dic is None:
raise Exception(
"Invalid standard key: "
"Name should be corrected or added to wireless_link_parameter_map.yaml, given: "
f"{waveform_generator}"
)
for parameter_pair in parameter_map_dic:
# check if key for chosen simulator even exists
if waveform_generator + "_parameter" in parameter_pair.keys():
# only continue with mapping from file if direct equivalent exists
if parameter_pair[waveform_generator + "_parameter"]["name"]:
# It is not necessary to get all parameters from wireless_link_parameter_map.yaml
# in captured_data since some parameters related to DL or UL only
if (parameter_pair[waveform_generator + "_parameter"]["name"] in captured_data.keys()):
# extract value from waveform config source
value = captured_data[
parameter_pair[waveform_generator + "_parameter"]["name"]]
# additional mapping if parameter values should come from a discrete set of values
if ("value_map" in parameter_pair[waveform_generator + "_parameter"].keys()):
value = parameter_pair[waveform_generator + "_parameter"]["value_map"][value]
# write to target dictionary for SigMF
signal_metadata[parameter_pair["sigmf_parameter_name"]] = value
else:
raise Exception(
f"Incomplete specification in field '{waveform_generator}_parameter'!")
if not signal_metadata:
raise Exception(
"""ERROR: Check captured meta-data or provided config and meta data """)
# check for non-JSON-serializable data types
def isfloat(NumberString):
try:
float(NumberString)
return True
except ValueError:
return False
for key, value in signal_metadata.items():
if isinstance(value, np.integer):
signal_metadata[key] = int(value)
elif isinstance(value, (np.float16, np.float32, np.float64)):
signal_metadata[key] = np.format_float_positional(value, trim="-")
elif isinstance(value, int):
signal_metadata[key] = int(value)
elif isinstance(value, float):
# store value in decimal and not in scientific notation
signal_metadata[key] = float(value)
elif isinstance(value, str) and key != "standard":
# convert string to lower case
signal_metadata[key] = value.lower()
if isfloat(value):
if value.isdigit():
signal_metadata[key] = int(float(value))
elif value.replace(".", "", 1).isdigit() and value.count(".") < 2:
signal_metadata[key] = float(value)
return signal_metadata, generator
# Derive other Signal metadata from captured metadata
def derive_remaining_5gnr_metadata(captured_data, signal_metadata):
# Add other OAI metadata to Wireless Dictionary Parameter Map - SigMF metadata
# ----------------------------------------------------
# Set 1: Parameters needs to be derived from OAI message
# ----------------------------------------------------
# DMRS Info:
# Check if the message type contains "UL"
if "UL" in captured_data["message_type"]:
# PUSCH DMRS: OFDM symbol start index within slot
get_pusch_dmrs_start_ofdm_symbol = 0
signal_metadata["pusch_dmrs:ofdm_symbol_idx"] = []
for symbol in range(captured_data["start_symbol_index"],
captured_data["start_symbol_index"] + captured_data["nr_of_symbols"]):
dmrs_symbol_flag = (captured_data["ul_dmrs_symb_pos"] >> symbol) & 0x01
if dmrs_symbol_flag and not get_pusch_dmrs_start_ofdm_symbol:
get_pusch_dmrs_start_ofdm_symbol = 1
signal_metadata["pusch_dmrs:start_ofdm_symbol"] = symbol
if dmrs_symbol_flag:
signal_metadata["pusch_dmrs:ofdm_symbol_idx"].append(symbol) # Append symbol to the list
signal_metadata["pusch_dmrs:duration_num_ofdm_symbols"] = 1
signal_metadata["pusch_dmrs:num_add_positions"] = (captured_data["number_dmrs_symbols"] - 1)
if "DL" in captured_data["message_type"]:
# PDSCH DMRS: OFDM symbol start index within slot
get_pdsch_dmrs_start_ofdm_symbol = 0
signal_metadata["pdsch_dmrs:ofdm_symbol_idx"] = []
for symbol in range(captured_data["StartSymbolIndex"],
captured_data["StartSymbolIndex"] + captured_data["NrOfSymbols"]):
dmrs_symbol_flag = captured_data["dlDmrsSymbPos"] & (1 << symbol)
if dmrs_symbol_flag:
if dmrs_symbol_flag and not get_pdsch_dmrs_start_ofdm_symbol:
get_pdsch_dmrs_start_ofdm_symbol = 1
signal_metadata["pdsch_dmrs:start_ofdm_symbol"] = symbol
if dmrs_symbol_flag:
signal_metadata["pdsch_dmrs:ofdm_symbol_idx"].append(symbol) # Append symbol to the list
signal_metadata["pdsch_dmrs:duration_num_ofdm_symbols"] = 1
signal_metadata["pdsch_dmrs:num_add_positions"] = (captured_data["numDmrsSymbols"] - 1)
# If the message is a BIT message, add number of bits to the signal info
# "UE_PHY_UL_SCRAMBLED_TX_BITS", "GNB_PHY_UL_PAYLOAD_RX_BITS", "UE_PHY_UL_PAYLOAD_TX_BITS"
if "_BITS" in captured_data["message_type"]:
# if "BIT" in captured_data["message_type"]:
signal_metadata["num_bits"] = (captured_data["number_of_bits"])
# Set Link direction and number of antennas based on link direction and captured side
if "UL" in captured_data["message_type"]:
signal_metadata["link_direction"] = "uplink"
# On UE side, It is TX antennas
# On gNB side, It is RX antennas
if "UE" in captured_data["message_type"]:
signal_metadata["num_tx_antennas"] = captured_data["nb_antennas"]
else:
signal_metadata["num_rx_antennas"] = captured_data["nb_antennas"]
if "DL" in captured_data["message_type"]:
signal_metadata["link_direction"] = "downlink"
# On UE side, It is RX antennas
# On gNB side, It is TX antennas
if "UE" in captured_data["message_type"]:
signal_metadata["num_rx_antennas"] = captured_data["nb_antennas"] ## TO DO check key name: no DL message from UE yet
else:
signal_metadata["num_tx_antennas"] = captured_data["nrOfLayers"]
# ----------------------------------------------------
# Set 2: Parameters that is hardcoded
# ----------------------------------------------------
# Note: The mapping type is hardcoded. It is fixed in OAI to mapping type A for DL and B for UL.
# Look to: openair2/RRC/NR/nr_rrc_config.c
if "UL" in captured_data["message_type"]:
signal_metadata["pusch:mapping_type"] = "B"
signal_metadata["pusch:content"] = "compliant"
signal_metadata["pusch:payload_bit_pattern"] = "random"
if "DL" in captured_data["message_type"]:
signal_metadata["pdsch:mapping_type"] = "A"
signal_metadata["pdsch:content"] = "compliant"
signal_metadata["pdsch:payload_bit_pattern"] = "random"
signal_metadata["num_slots"] = 1
return signal_metadata

View File

@@ -118,7 +118,7 @@ def calculate_stats(main_config_file: str):
if __name__ == "__main__":
main_config = "/home/user/workarea/oai_recorded_data/config_data_recording_app_extended.json"
main_config = "/home/user/workarea/oai_recorded_data/config_data_recording_app.json"
# calculate BER stats
calculate_stats(main_config)

View File

@@ -7,13 +7,11 @@
#include <stdint.h>
#include <stdbool.h>
#include <stdatomic.h>
#if defined(__cplusplus)
#include <atomic>
// use atomic_size_t to help interworking with C++
using std::atomic_size_t;
#else
#include <stdatomic.h>
#endif
typedef struct spsc_q {

View File

@@ -262,7 +262,28 @@ static inline int get_num_dmrs(uint16_t dmrs_mask )
return(num_dmrs);
}
static inline int count_bits(uint8_t *arr, int sz)
{
AssertFatal(sz % sizeof(int) == 0, "to implement if needed\n");
int ret = 0;
for (uint *ptr = (uint *)arr; (uint8_t *)ptr < arr + sz; ptr++)
ret += __builtin_popcount(*ptr);
return ret;
}
static __attribute__((always_inline)) inline int count_bits64(uint64_t v)
{
return __builtin_popcountll(v);
}
static __attribute__((always_inline)) inline int count_bits64_with_mask(uint64_t v, int start, int num)
{
uint64_t mask = ((1LL << num) - 1) << start;
return count_bits64(v & mask);
}
void warn_higher_threequarter_fs(const int n_rb, const int mu);
uint64_t from_nrarfcn(int nr_bandP, uint8_t scs_index, uint32_t dl_nrarfcn);
uint32_t to_nrarfcn(uint64_t dl_CarrierFreq);
uint8_t set_ssb_case(int scs, int nr_band);

View File

@@ -2,7 +2,7 @@
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "bits.h"
#include "reverse_bits.h"
// we avoid assertions.h as it necessitates othe dependencies (e.g., exit_function)
#include <assert.h>
#include <simde/x86/gfni.h>

View File

@@ -0,0 +1,14 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef REVERSE_BITS_H_
#define REVERSE_BITS_H_
#include <stddef.h>
#include <stdint.h>
uint64_t reverse_bits(uint64_t in, int n_bits);
void reverse_bits_u8(uint8_t const* in, size_t sz, uint8_t* out);
#endif /* REVERSE_BITS_H_ */

View File

@@ -16,7 +16,7 @@
#include <linux/types.h>
// global var to enable openair performance profiler
extern int cpu_meas_enabled;
extern double cpu_freq_GHz __attribute__ ((aligned(32)));
extern double cpu_freq_GHz __attribute__ ((aligned(32)));;
// structure to store data to compute cpu measurment
#if defined(__x86_64__) || defined(__i386__) || defined(__arm__) || defined(__aarch64__)
typedef long long oai_cputime_t;

View File

@@ -72,9 +72,9 @@ Tested libxran releases:
| Vendor |
|-----------------------------------------|
| `oran_f_release_v1.0` |
| `oran_k_release_v1.0` |
**Note**: The libxran driver of OAI identifies the above F release version as "6.1.0" (F is the sixth letter, then 1.0), and the above K release as "11.1.0".
**Note**: The libxran driver of OAI identifies the above F release version as "6.1.0" (F is the sixth letter, then 1.0).
### Configure your server
@@ -274,7 +274,7 @@ timedatectl set-ntp false
### DPDK (Data Plane Development Kit)
Download DPDK version 20.11.9 (F release) or 24.11.4 (K release).
Download DPDK version 20.11.9.
```bash
# on debian
@@ -282,8 +282,7 @@ sudo apt install wget xz-utils libnuma-dev
# on Fedora/RHEL
sudo dnf install wget xz numactl-devel
cd
wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz # F release
wget http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz # K release
wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz
```
#### DPDK Compilation and Installation
@@ -291,11 +290,10 @@ wget http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz # K release
```bash
# Installing meson : it should pull ninja-build and compiler packages
# on debian
sudo apt install python3-pyelftools meson
sudo apt install meson
# on Fedora/RHEL
sudo dnf install python3-pyelftools meson
tar xvf dpdk-20.11.9.tar.xz && cd dpdk-stable-20.11.9 # F release
tar xvf dpdk-24.11.4.tar.xz && cd dpdk-stable-24.11.4 # K release
sudo dnf install meson
tar xvf dpdk-20.11.9.tar.xz && cd dpdk-stable-20.11.9
meson build
ninja -C build
@@ -361,8 +359,7 @@ pkg-config --libs libdpdk --static
Go back to the version folder you used to build and install
```
cd ~/dpdk-stable-20.11.9 # F release
cd ~/dpdk-stable-24.11.4 # K release
cd ~/dpdk-stable-20.11.9
sudo ninja deinstall -C build
```
@@ -387,13 +384,6 @@ git checkout oran_f_release_v1.0
git apply ~/openairinterface5g/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch
```
#### K release
```bash
git clone https://github.com/openairinterface/o-du-phy.git ~/phy
cd ~/phy
git checkout <desired-tag> # shall match a variable `K_VERSION`
```
Compile the fronthaul interface library by calling `make` and the option
`XRAN_LIB_SO=1` to have it build a shared object. Note that we provide two
environment variables `RTE_SDK` for the path to the source tree of DPDK, and
@@ -408,7 +398,6 @@ This feature is intended to enable experiments and future improvements on Arm sy
cd ~/phy/fhi_lib/lib
make clean
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=~/dpdk-stable-20.11.9/ XRAN_DIR=~/phy/fhi_lib make XRAN_LIB_SO=1 # F release
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=~/dpdk-stable-24.11.4/ XRAN_DIR=~/phy/fhi_lib make XRAN_LIB_SO=1 # K release
...
[AR] build/libxran.so
./build/libxran.so
@@ -494,13 +483,23 @@ Note that you might also call cmake directly instead of using `build_oai`:
```
cd ~/openairinterface5g
mkdir build && cd build
# build RAN after manually building xran F or K release
cmake .. -GNinja -DOAI_FHI72=ON -Dxran_LOCATION=$HOME/phy/fhi_lib/lib
# build RAN and xran K release automatically
cmake .. -GNinja -DOAI_FHI72=ON -Dxran_DOWNLOAD=ON
ninja nr-softmodem oran_fhlib_5g params_libconfig
```
Note that in tags 2025.w06 and prior, the FHI72 driver used polling to wait for
the next slot. This is inefficient as it burns CPU time, and has been replaced
with a more efficient mechanism. Nevertheless, if you experience problems that
did not occur previously, it is possible to re-enable polling, either with
`build_oai` like this
./build_oai --gNB --ninja -t oran_fhlib_5g --cmake-opt -Dxran_LOCATION=$HOME/phy/fhi_lib/lib --cmake-opt -DOAI_FHI72_USE_POLLING=ON
or with `cmake` like so
cmake .. -GNinja -DOAI_FHI72=ON -Dxran_LOCATION=$HOME/phy/fhi_lib/lib -DOAI_FHI72_USE_POLLING=ON
ninja oran_fhlib_5g
## Configuration
RU and DU configurations have a circular dependency: you have to configure DU MAC address in the RU configuration and the RU MAC address, VLAN and Timing advance parameters in the DU configuration.
@@ -1260,10 +1259,11 @@ For two RUs using a 8x8 configuration, i.e. a single antenna system, the referen
For two RUs each using a 4x4 configuration, i.e. a distributed antenna system (DAS),
we use the analog beamforming implementation. More details can be found in
[this document](./analog_beamforming.md). It is important to note the following parameters in the configuration file:
- In RU section `nb_tx` and `nb_rx` indicates the total number of physical antenna ports across all distributed RUs.
- `pusch_AntennaPorts`, `pdsch_AntennaPorts_XP * pdsch_AntennaPorts_N1 * pdsch_AntennaPorts_N2` indicate the number of logical antenna ports for one analog beam.
The reference DU configuration file is [`gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf).
[this document](./analog_beamforming.md). It is important to note that
the configuration file should be set as a 4x4 scenario and each RU would be given a
different beam. The reference DU configuration file is [`gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb-du.sa.band77.273prb.fhi72.4x4-das-benetel650_650.conf).
DAS is enabled by setting to 1 the parameter `enable_das` in `L1` section.
The following parameters shall be configured on the gNB in the `MACRLC` section:
- `set_analog_beamforming`
@@ -1636,8 +1636,6 @@ PKG_CONFIG_PATH=/opt/mplane-v2/lib/pkgconfig cmake .. -GNinja -DOAI_FHI72=ON -DO
ninja nr-softmodem oran_fhlib_5g_mplane params_libconfig
```
Note: instead of setting `xran_LOCATION`, feel free to use `xran_DOWNLOAD` option for automatic xran K release build.
### Start the gNB
Run the `nr-softmodem` from the build directory:
```bash

View File

@@ -108,7 +108,6 @@ Some directories under `radio` contain READMEs:
- [fhi_72](../radio/fhi_72/README.md)
- [vrtsim](../radio/vrtsim/README.md)
- [rf_emulator](../radio/emulator/README.md)
- [zmq](../radio/zmq/README.md)
The other SDRs (AW2S, LimeSDR, ...) have no READMEs.

View File

@@ -10,11 +10,11 @@ This document explains the implementation of analog beamforming in OAI codebase.
Beamforming is a technique applied to antenna arrays to create a directional radiation pattern. This often consists in providing a different phase shift to each element of the array such that signals with a different angle of arrival/departure experience a change in radiation pattern because of constructive or destructive interference.
There are three main beamforming techniques: analog, digital and hybrid. The names refer to the phase shift application before or after the digital to analog conversion (or analog to digital in reception). When we speak about analog beamforming we generally refer to a technique where the phase shifts that produce the beam steering are applied by the radio unit (RU) choosing from a finite set of steering directions. The advantage of analog beamforming is a simplified analog circuitry and therefore reduced costs.
There are three main beamforming techinques: analog, digital and hybrid. The names refer to the phase shift application before or after the digital to analog conversion (or analog to digital in reception). When we speak about analog beamforming we generally refer to a techinique where the phase shifts that produce the beam stearing are applied by the radio unit (RU) choosing from a finite set of steering directions. The advantage of analog beamforming is a simplified analog circuitry and therefore reduced costs.
The presence of a limited number of predefined beams at RU poses constraints to the scheduler at gNB. As a matter of fact, the scheduler can serve only a limited number of beams, depending on the RU characteristics (possibly only 1), in a given time scale, that also depends on the RU characteristics (e.g. 1 slot or 1 symbol). This limitation doesn't exist for digital beamforming.
Analog beamforming implementation also allows to enable distributed antenna systems (DAS), where each beam corresponds to one antenna (or a set of antennas) of the system. In this scenario, the scheduler constraint is alleviated because normally the number of concurrent beams allowed equals the total number of beams.
Analog beamforming implementation also allows to enable distributed antenna systems (DAS), where each beam corrisponds to one antenna (or a set of antennas) of the system. In this scenario, the scheduler constaint is alleviated because normally the number of concurrent beams allowed equals the total number of beams.
## Configuration file fields for analog beamforming
@@ -28,7 +28,7 @@ In the `MACRLC` section of configuration files, there are three new parameters:
Setting analog beamforming to "preconfigured" or "lophy" changes the way FAPI beam index is treated. By setting "preconfigured", we instruct L1 to look up in Hi-PHY preconfigured DBM beam index. By setting "lophy", we instruct L2 to directly signal to Lo-PHY the beam index (e.g. over 7.2x fronthaul).
DAS is enabled by setting the field `beams_per_period` in `MACRLC` section to a value equal to the number of distributed units and by ensuring `nb_tx` and `nb_rx` in RU section is adequate enough to serve all beams. `beam_mode` must be set to "preconfigured" and `beam_weights` can be omitted.
DAS is enabled by setting to 1 the parameter `enable_das` in the L1 section of the configuration file. In case of DAS enabled, the field `beam_weights` in `MACRLC` section can be omitted and the number of beams per period equals the total number of beams.
## Implementation in OAI scheduler
@@ -55,16 +55,19 @@ This definition of beam-ID is present only in the most recent versions of SCF PH
In addition to that, a `config_request` structure defined as vendor extension (`nfapi_nr_analog_beamforming_ve_t`) configures the lower layers at initialization with the following information:
- `analog_bf_vendor_ext` which can assume values 1 or 0 for enabling or disabling analog beamforming
- `num_beams_period_vendor_ext` which corresponds to the configuration parameter `beams_per_period`
Therefore, in case of analog beamforming, L2 provides in each channel FAPI message information about the beam index via the beam-ID parameter with MSB set to 1.
## L1 implementation
The total number of logical antenna ports available at L1 is same as `pusch_AntennaPorts * beams_per_period` in UL and `pdsch_AntennaPorts_N1 * pdsch_AntennaPorts_N2 * pdsch_AntennaPorts_XP * beams_per_period` in DL.
To handle multiple concurrent beams, L2 uses spatial stream indices specified by FAPI to signal L1 on which logical ports to use for a DL or UL signal. The config file parameter `spatial_stream_index` can be used to specify an array of logical port indices to be used. If this parameter is not provided then the indices defaults to `[0 ... pusch_AntennaPorts - 1]`. This parameter is particularly useful when a specific subset of eAxCID has to be used.
To handle multiple concurrent beams, the buffers containing Tx and Rx data in frequency domain (`txdataF` and `rxdataF`) have been extended by one dimension to contain multiple concurrent beams to be transmitted/received.
The function `beam_index_allocation`, called by every L1 channel, is responsible to match the FAPI analog beam index to the RU beam index and to store the latter `beam_id` structure, which allocates the beams per symbol, despite L2 only supporting beam change at slot level. At the same time, the function returns the concurrent beam index, to be used to store data in frequency domain buffers. While doing so, the function also checks if there is room for current beam in the list of concurrent beams, which should always be the case, if L2 properly allocated the channels.
In case of DAS, since each beam corresponds to a specific antenna port, the `beam_index_allocation` function is simplified in the sense that the beam index corresponds to the antenna port index of the frequency domain buffers.
## RU implementation
The implementation is still work in progress.
The first dimension of the Tx and Rx buffers contains the number of Tx/Rx antennas which is at least the number of logical antenna ports.
The first dimension of the Tx and Rx buffers that used to contain the number of Tx/Rx antennas, it is now extended to contain the number of parallel streams which is the number of antennas multiplied by the number of concurrent beams.

View File

@@ -50,11 +50,10 @@ python3 -m pip install -r common/utils/data_recording/requirements.txt
The Data Recording application provides configuration file in [JSON](http://www.json.org/) format. It is stored in [common/utils/data_recording/config/config_data_recording.json](../common/utils/data_recording/config/config_data_recording.json) folder. The main parameters are:
- **data_storage_path**: Path to directory for data storage
- **num_records**: Number of requested data records in slots. The maximum number of num_records depends on the memory size. If the user would like to record large number of records, user needs to increase the memory size defined here (common/utils/T/tracer/shared_memory_config.h)
- **num_records**: Number of requested data records in slots
- **t_tracer_message_definition_file**: T-Tracer message definition file
- **parameter_map_file**: Parameter mapping dictionary (OAI parameters to standardized metadata). It is located here: [common/utils/data_recording/config/wireless_link_parameter_map.yaml](../common/utils/data_recording/config/wireless_link_parameter_map.yaml)
- **start_frame_number**: It can be used to start the recording from a specific frame, but it is not yet supported.
- **common_meta_data**: Common metadata parameters that are not read from the system yet, but shall be included into the SigMF metadata such as the sampling rate, the bandwidth, and the clock reference of USRP.
- **base_station**:
- requested_tracer_messages: Requested base station data traces. The supported messages are:
@@ -63,18 +62,16 @@ The Data Recording application provides configuration file in [JSON](http://www.
- GNB_PHY_UL_FD_CHAN_EST_DMRS_POS
- GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL
- GNB_PHY_UL_PAYLOAD_RX_BITS
- tracer_service_baseStation_address: The IP address and port number of base station server. If the Data Recording App and the Data Collection (TTracer) Service are running on the same server of base station, the IP address and port number are : `127.0.0.1:2021`
- meta_data: Additional base station metadata (additional fixed base station parameters that cannot be read from the system yet, but shall be included into the SigMF metadata)
- shared_mem_config: Shared Memory configuration such as in T-Tracer App of gNB
- **user_equipment**
- requested_tracer_messages: Requested user equipment data traces. The supported messages are:
- UE_PHY_UL_SCRAMBLED_TX_BITS
- UE_PHY_UL_PAYLOAD_TX_BITS
- tracer_service_userEquipment_address**: The IP address and port number of UE server.
- meta_data: Additional user equipment metadata (additional fixed UE parameters that are not read from the system yet, but shall be included into the SigMF metadata)
- shared_mem_config: Shared Memory configuration such as in T-Tracer App of UE
- **common_meta_data**: Common metadata parameters that are not read from the system yet, but shall be included into the SigMF metadata such as the sampling rate, the bandwidth, and the clock reference of USRP.
- **tracer_service_baseStation_address**: The IP address and port number of base station server. If the Data Recording App and the Data Collection (TTracer) Service are running on the same server of base station, the IP address and port number are : `127.0.0.1:2021`
- **tracer_service_userEquipment_address**: The IP address and port number of UE server.
**Notes**:
- The data can be recorded from base station and UE or from base station only or UE only. To disable the recording from any station, make the list of `requested_tracer_messages` empty such as: `requested_tracer_messages:[]`
@@ -82,68 +79,54 @@ The Data Recording application provides configuration file in [JSON](http://www.
The figure below illustrates an example of a JSON Data Recording App configuration file.
```json
{
"data_recording_config": {
"data_storage_path": "/home/user/workarea/oai_recorded_data/",
"data_file_format": "SigMF",
"enable_saving_tracer_messages_sigmf": true,
"num_records": 5,
"t_tracer_message_definition_file": "../T/T_messages.txt",
"parameter_map_file": "config/wireless_link_parameter_map.yaml",
"start_frame_number": 5,
"common_meta_data": {
"sample_rate": 61440000.0,
"bandwidth": 40000000.0,
"clock_reference": "external"
},
"nodes": [
{
"id": "gnb_0",
"type": "gnb",
"data_storage_path": "/home/user/workarea/oai_recorded_data/",
"data_file_format": "SigMF",
"enable_saving_tracer_messages_sigmf": true,
"num_records": 5,
"t_tracer_message_definition_file": "../T/T_messages.txt",
"parameter_map_file": "config/wireless_link_parameter_map.yaml",
"start_frame_number": 5,
"base_station": {
"requested_tracer_messages": [
"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_PUSCH_IQ",
"GNB_PHY_UL_FD_DMRS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_POS",
"GNB_PHY_UL_FD_CHAN_EST_DMRS_INTERPL",
"GNB_PHY_UL_PAYLOAD_RX_BITS"],
"tracer_service_address": "127.0.0.1:2021",
"meta_data": {
"num_tx_antennas": 1,
"GNB_PHY_UL_PAYLOAD_RX_BITS"
],
"meta_data":{
"num_rx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 30.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "328AB35"
},
"shared_mem_config": {
"read_path": "/tmp/gnb_app1",
"write_path": "/tmp/gnb_app2",
"project_id": 2335
}
},
{
"id": "ue_0",
"type": "ue",
"user_equipment": {
"requested_tracer_messages": [
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"],
"tracer_service_address": "127.0.0.1:2023",
"meta_data": {
"UE_PHY_UL_SCRAMBLED_TX_BITS",
"UE_PHY_UL_PAYLOAD_TX_BITS"
],
"meta_data":{
"num_tx_antennas": 1,
"num_rx_antennas": 1,
"tx_gain": 48.0,
"rx_gain": 30.0,
"rx_gain": 40.0,
"hw_type": "USRP X410",
"hw_subtype": "ZBX",
"seid": "323F75F"
},
"shared_mem_config": {
"read_path": "/tmp/ue_app1",
"write_path": "/tmp/ue_app2",
"project_id": 2336
}
}
]
},
"common_meta_data": {
"sample_rate": 61440000.0,
"bandwidth": 40000000.0,
"clock_reference": "external"
},
"tracer_service_baseStation_address": "127.0.0.1:2021",
"tracer_service_userEquipment_address": "127.0.0.1:2023"
}
}
```
@@ -170,7 +153,7 @@ The following figure shows an example of Wireless Link Parameter Map Dictionary.
### Global Metadata
There are some metadata parameters that the user may need to change only once. Those parameters have been hard coded in the Data Recording App header.
[common/utils/data_recording/data_recording_app_v1.1.py](../common/utils/data_recording/data_recording_app_v1.1.py). Those are:
[common/utils/data_recording/data_recording_app_v1.0.py](../common/utils/data_recording/data_recording_app_v1.0.py). Those are:
- The global metadata such as: author, description, sigmf collection file prefix, datetime_offset, enable saving config Data Recording App in json file with recorded data, and name of signal generator (i.e. 5gnr_oai). The name of signal generator is used for parameter mapping dictionary (OAI parameters to standardized metadata).
- The mapping between supported OAI messages and file_name_prefix, scope, and description.
@@ -254,11 +237,11 @@ For the test simplicity, user can run the system in PHY-test mode. We will show
Run NR gNB Softmodem with USRP:
```
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.prs.band78.fr1.106PRB.usrpx310 --gNBs.[0].min_rxtxtime 6 --usrp-tx-thread-config 1 --phy-test -d --T_stdout 2 --T_nowait
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.1x1.usrpx410_3300MHz.conf --gNBs.[0].min_rxtxtime 6 --usrp-tx-thread-config 1 --phy-test -d --T_stdout 2 --T_nowait
```
Run NR gNB Softmodem in RF Simulation:
```
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb0.prs.band78.fr1.106PRB.usrpx310.conf --gNBs.[0].min_rxtxtime 6 --rfsim --phy-test --rfsimulator.[0].serveraddr server --T_stdout 2 --T_nowait
sudo ./cmake_targets/ran_build/build/nr-softmodem -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band78.sa.fr1.106PRB.1x1.usrpx410_3300MHz.conf --gNBs.[0].min_rxtxtime 6 --rfsim --rfsimulator.[0].serveraddr server --phy-test -d --T_stdout 2 --T_nowait
```
> **Note:** User needs to change the name of gNB config file to your gNB config file.
@@ -282,9 +265,9 @@ sudo ./cmake_targets/ran_build/build/nr-uesoftmodem --usrp-args "type=x4xx,addr=
```
Run OAI Soft UE in RF Simulation, assume both gNB and UE are running on the same server in Simulation mode`:
Run OAI Soft UE in RF Simulation, assume the IP address of gNB server is `192.168.100.2`:
```
sudo ./cmake_targets/ran_build/build/nr-uesoftmodem --rfsim --phy-test -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --reconfig-file /home/user/workarea/openairinterface5g/reconfig.raw --rbconfig-file /home/user/workarea/openairinterface5g/rbconfig.raw --T_stdout 2 --T_nowait --T_port 2023
sudo ./cmake_targets/ran_build/build/nr-uesoftmodem --rfsim --rfsimulator.[0].serveraddr 192.168.100.2 --phy-test -O ./targets/PROJECTS/GENERIC-NR-5GC/CONF/ue.conf --reconfig-file /home/user/workarea/rrc_files/reconfig.raw --rbconfig-file /home/user/workarea/rrc_files/rbconfig.raw --T_stdout 2 --T_nowait --T_port 2023
```
### Step3: Run Data Recording Application Services
@@ -303,7 +286,7 @@ Run gNB Data Collection service on one terminal:
```
Run UE Data Collection service on another terminal:
```
./t_tracer_app_ue -d ../T_messages.txt -p 2023
./t_tracer_app_ue -d ../T_messages.txt
```
Second, run main Data Recording Application:
@@ -311,7 +294,7 @@ Second, run main Data Recording Application:
Run the following python script:
```
python3 data_recording_app_v1.1.py
python3 data_recording_app_v1.0.py
```
The recorded data set will be stored in the configured path, assume `/home/user/workarea/oai_recorded_data/`.
@@ -389,8 +372,8 @@ The following figure shows an example of SigMF metadata file, for example for th
{
"global": {
"core:author": "Abdo Gaber",
"core:collection": "data-collection-rec-1-2026_04_21T14_14_07_008277523",
"core:dataset": "raw-ce-fd-data-rec-1-2026_04_21T14_14_07_008279596.sigmf-data",
"core:collection": "data-collection-rec-0-2025_06_19T11_19_00_037",
"core:dataset": "raw-ce-fd-data-rec-0-2025_06_19T11_19_00_037.sigmf-data",
"core:datatype": "cf32_le",
"core:description": "Frequency-domain raw channel estimates (at DMRS positions)",
"core:hw": "USRP X410",
@@ -398,12 +381,12 @@ The following figure shows an example of SigMF metadata file, for example for th
"core:num_channels": 1,
"core:recorder": "NI Data Recording Application for OAI",
"core:sample_rate": 61440000.0,
"core:sha512": "fce1c6e85e986939e8cf5a3937d0d2f08d9f27ab52ea4065d6e347beb5acf7017a4f9d200c70345f1724ff52d886403e2fddedce62c5c1de4ef895a941547e3d",
"core:sha512": "55ae5bc81b4812e68f6e1c195600f240fc3ca71113c732271ee42a5524da98636c3c7ac587c46fd87c6d1b9253ca6504a4b94eafba8f4065099096fc7721228c",
"core:version": "1.2.1"
},
"captures": [
{
"core:datetime": "2026_04_21T14:14:07.008279596",
"core:datetime": "2025_06_19T11:19:00.037",
"core:frequency": 3319680000.0,
"core:sample_start": 0,
"serialization:counts": [
@@ -448,11 +431,10 @@ The following figure shows an example of SigMF metadata file, for example for th
"5gnr": {
"cell_id": 0,
"cp_mode": "normal",
"frame": 916,
"frame": 785,
"frame_structure": "tdd",
"frequency_range": "fr1",
"link_direction": "uplink",
"num_rx_antennas": 1,
"num_slots": 1,
"num_tx_antennas": 1,
"pusch:content": "compliant",
@@ -463,7 +445,7 @@ The following figure shows an example of SigMF metadata file, for example for th
"pusch:num_layer": 1,
"pusch:num_ofdm_symbols": 13,
"pusch:num_prb": 50,
"pusch:payload_bit_pattern": "random",
"pusch:payload_bit_pattern": "zeros",
"pusch:start_ofdm_symbol": 0,
"pusch:start_prb": 0,
"pusch:transform_precoding": "disabled",

View File

@@ -45,9 +45,7 @@ RUN rm -f /etc/rhsm-host && \
gcc-toolset-13-gcc-c++ \
# python3-pip and pyyaml are used for conf template generation
python3-pip && \
pip3 install --ignore-installed \
pyyaml \
pyelftools && \
pip3 install --ignore-installed pyyaml && \
echo "/usr/local/lib" > /etc/ld.so.conf.d/local-lib.conf && \
echo "/usr/local/lib64" >> /etc/ld.so.conf.d/local-lib.conf

View File

@@ -40,8 +40,7 @@ RUN apt-get update && \
wget \
flatbuffers-compiler \
libflatbuffers-dev \
libnanomsg-dev \
libzmq3-dev && \
libnanomsg-dev && \
# --break-system-packages allows externally managed environment in pip
pip3 install --ignore-installed pyyaml --break-system-packages

View File

@@ -9,7 +9,6 @@
FROM ran-base:latest AS ran-base
ARG xran_VERSION=11.1.1
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00
ENV DEBIAN_FRONTEND=noninteractive
@@ -21,8 +20,7 @@ RUN apt-get update && \
wget \
xz-utils \
pkg-config \
libnuma-dev \
python3-pyelftools && \
libnuma-dev && \
rm -rf /var/lib/apt/lists/*
RUN rm -Rf /oai-ran
@@ -30,19 +28,20 @@ WORKDIR /oai-ran
COPY . .
## Download and build DPDK
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
tar -xf dpdk-24.11.4.tar.xz && \
cd dpdk-stable-24.11.4 && \
meson build && \
RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \
tar -xvf dpdk-20.11.9.tar.xz && \
cd dpdk-stable-20.11.9 && \
meson setup -Dmachine=default build && \
ninja -C build && \
ninja install -C build
## Build Fronthaul library
RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy && \
git checkout $xran_VERSION &&\
git checkout oran_f_release_v1.0 &&\
git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch && \
cd /opt/phy/fhi_lib/lib && \
TARGET=armv8 WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
TARGET=armv8 WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
## Build Arm RAN Acceleration Library
RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \

View File

@@ -14,12 +14,10 @@ RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
ARG xran_VERSION=11.1.1
## Download and build DPDK
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
tar -xf dpdk-24.11.4.tar.xz && \
cd dpdk-stable-24.11.4 && \
RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \
tar -xvf dpdk-20.11.9.tar.xz && \
cd dpdk-stable-20.11.9 && \
meson build && \
ninja -C build && \
ninja install -C build
@@ -27,9 +25,10 @@ RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
## Build Fronthaul library
RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy && \
git checkout $xran_VERSION &&\
git checkout oran_f_release_v1.0 &&\
git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
FROM ran-base AS ran-build-fhi72
ARG E2AP_VERSION=E2AP_V3

View File

@@ -9,7 +9,6 @@
FROM ran-base:latest AS ran-base
ARG xran_VERSION=11.1.1
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00
ENV DEBIAN_FRONTEND=noninteractive
@@ -21,8 +20,7 @@ RUN apt-get update && \
wget \
xz-utils \
pkg-config \
libnuma-dev \
python3-pyelftools && \
libnuma-dev && \
rm -rf /var/lib/apt/lists/*
RUN rm -Rf /oai-ran
@@ -30,9 +28,9 @@ WORKDIR /oai-ran
COPY . .
## Download and build DPDK
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
tar -xf dpdk-24.11.4.tar.xz && \
cd dpdk-stable-24.11.4 && \
RUN wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz && \
tar -xvf dpdk-20.11.9.tar.xz && \
cd dpdk-stable-20.11.9 && \
meson build && \
ninja -C build && \
ninja install -C build
@@ -40,9 +38,10 @@ RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
## Build Fronthaul library
RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy && \
git checkout $xran_VERSION &&\
git checkout oran_f_release_v1.0 &&\
git apply /oai-ran/cmake_targets/tools/oran_fhi_integration_patches/F/oaioran_F.patch && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-20.11.9/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
FROM ran-base AS ran-build-fhi72
## Build and install OAI

View File

@@ -32,7 +32,6 @@ RUN \
-w USRP -t Ethernet -w BLADERF \
--build-e2 --cmake-opt -DKPM_VERSION=$KPM_VERSION --cmake-opt -DE2AP_VERSION=$E2AP_VERSION \
--cmake-opt -DOAI_VRTSIM_TAPS_CLIENT=ON \
-w ZMQ \
--noavx512 \
--build-tool-opt -k10 \
--cmake-opt -DT_RECORD_DB=ON \

View File

@@ -41,7 +41,6 @@ RUN rm -f /etc/rhsm-host && \
python3-pip \
net-tools \
iputils \
libatomic \
yaml-cpp-devel && \
pip3 install six && \
pip3 install requests && \

View File

@@ -18,7 +18,6 @@ ENV TZ=Europe/Paris
RUN dnf update -y && \
dnf install -y \
procps-ng \
libatomic \
libXpm \
libX11 \
lksctp-tools \

View File

@@ -41,7 +41,6 @@ RUN apt-get update && \
python3 \
python3-six \
python3-requests \
libatomic1 \
libyaml-cpp-dev && \
# if the --sanitize option was used to build, additional packages are required
/bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \

View File

@@ -47,8 +47,7 @@ RUN apt-get update && \
python3-requests \
libusb-1.0-0 \
libyaml-cpp-dev \
libnanomsg5 \
libzmq5 && \
libnanomsg5 && \
# if the --sanitize option was used to build, additional packages are required
/bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \
libasan8 \
@@ -79,7 +78,6 @@ COPY --from=gnb-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_rrc.so \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv_o1.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/oai-ran/cmake_targets/ran_build/build/liboai_zmqdevif.so \
/usr/local/lib/
# Now we are copying from builder-image the UHD files.
@@ -107,8 +105,7 @@ RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai
/usr/local/lib/libldpc*.so \
/usr/local/lib/libtelnetsrv.so \
/usr/local/lib/libtelnetsrv_ci.so \
/usr/local/lib/libuhd.so.4.8.0 \
/usr/local/lib/liboai_zmqdevif.so
/usr/local/lib/libuhd.so.4.8.0
WORKDIR /opt/oai-gnb
#EXPOSE 2152/udp # S1U, GTP/UDP

View File

@@ -50,8 +50,7 @@ RUN apt-get update && \
iperf \
libyaml-cpp-dev \
libzstd1 \
libnanomsg5 \
libzmq5 && \
libnanomsg5 && \
# if the --sanitize option was used to build, additional packages are required
/bin/bash -c 'if [[ "$BUILD_OPTION" = "--sanitize" ]]; then DEBIAN_FRONTEND=noninteractive apt-get install --yes \
libasan8 \
@@ -84,7 +83,6 @@ COPY --from=nr-ue-build \
/oai-ran/cmake_targets/ran_build/build/libtelnetsrv.so \
/oai-ran/cmake_targets/ran_build/build/libparams_yaml.so \
/oai-ran/cmake_targets/ran_build/build/libvrtsim.so \
/oai-ran/cmake_targets/ran_build/build/liboai_zmqdevif.so \
# Wildcards so it doesn't fail if not present
/oai-ran/cmake_targets/ran_build/build/_deps*/fmtlog-build*/libfmtlog-shared*.so \
/oai-ran/cmake_targets/ran_build/build/_deps*/clickhouse-cpp-build*/clickhouse/libclickhouse*.so \
@@ -116,8 +114,7 @@ RUN /bin/bash -c "ln -s /usr/local/lib/liboai_usrpdevif.so /usr/local/lib/liboai
/usr/local/lib/libtelnetsrv.so \
/usr/local/lib/libtelnetsrv_ciUE.so \
/usr/local/lib/libtelnetsrv_5Gue.so \
/usr/local/lib/libuhd.so.4.8.0 \
/usr/local/lib/liboai_zmqdevif.so
/usr/local/lib/libuhd.so.4.8.0
WORKDIR /opt/oai-nr-ue

View File

@@ -65,6 +65,8 @@ static void tx_func(processingData_L1tx_t *info)
reset_active_ulsch(gNB, frame_rx);
}
clear_slot_beamid(gNB, slot_tx);
nfapi_nr_slot_indication_scf_t ind = {.sfn = frame_tx, .slot = slot_tx};
start_meas(&gNB->slot_indication_stats);
// this variable is very big (multiple MB), so we put it into static storage
@@ -170,15 +172,17 @@ static void rx_func(processingData_L1_t *info)
if (gNB->phase_comp) {
//apply the rx signal rotation here
int soffset = (slot_rx % RU_RX_SLOT_DEPTH) * gNB->frame_parms.symbols_per_slot * gNB->frame_parms.ofdm_symbol_size;
for (int aa = 0; aa < gNB->frame_parms.nb_antennas_tx; aa++) {
const uint max_symb = (gNB->frame_parms.Ncp == NR_EXTENDED) ? 12 : 14;
for (int sym = 0; sym < max_symb; sym++)
apply_nr_rotation_symbol_RX(&gNB->frame_parms,
gNB->common_vars.rxdataF[aa] + soffset + sym * gNB->frame_parms.ofdm_symbol_size,
gNB->frame_parms.symbol_rotation[1],
gNB->frame_parms.N_RB_UL,
slot_rx,
sym);
for (int bb = 0; bb < gNB->common_vars.num_beams_period; bb++) {
for (int aa = 0; aa < gNB->frame_parms.nb_antennas_rx; aa++) {
const uint max_symb = (gNB->frame_parms.Ncp == NR_EXTENDED) ? 12 : 14;
for (int sym = 0; sym < max_symb; sym++)
apply_nr_rotation_symbol_RX(&gNB->frame_parms,
gNB->common_vars.rxdataF[bb][aa] + soffset + sym * gNB->frame_parms.ofdm_symbol_size,
gNB->frame_parms.symbol_rotation[1],
gNB->frame_parms.N_RB_UL,
slot_rx,
sym);
}
}
}
phy_procedures_gNB_uespec_RX(gNB, frame_rx, slot_rx, &UL_INFO);
@@ -211,7 +215,6 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size
output += print_meas_log(&gNB->dlsch_modulation_stats, "DLSCH modulation", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->dlsch_pdsch_generation_stats, "PDSCH generation", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->phy_proc_rx, "L1 Rx processing", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->pusch_rx_beamforming, "PUSCH Rx Beamforming", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_deinterleave, "UL segment deinterleaving", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_rate_unmatch, "UL segment rate recovery", NULL, NULL, output, end - output);
output += print_meas_log(&gNB->ts_ldpc_decode, "UL segments decoding", NULL, NULL, output, end - output);
@@ -225,7 +228,7 @@ static size_t dump_L1_meas_stats(PHY_VARS_gNB *gNB, RU_t *ru, char *output, size
bool full_slot = ru->half_slot_parallelization == 0;
if (ru->feptx_prec) {
output += print_meas_log(&ru->precoding_stats,
"feptx_prec",
full_slot ? "feptx_prec (per port)" : "feptx_prec (per port, half_slot)",
NULL,
NULL,
output,
@@ -278,7 +281,6 @@ void *nrL1_stats_thread(void *param) {
reset_meas(&gNB->ts_ldpc_decode);
reset_meas(&gNB->ul_indication_stats);
reset_meas(&gNB->slot_indication_stats);
reset_meas(&gNB->pusch_rx_beamforming);
reset_meas(&gNB->rx_pusch_stats);
reset_meas(&gNB->dlsch_scrambling_stats);
reset_meas(&gNB->dlsch_modulation_stats);
@@ -358,7 +360,10 @@ void init_eNB_afterRU(void)
AssertFatal(gNB->RU_list[ru_id]->common.rxdataF != NULL, "RU %d : common.rxdataF is NULL\n", gNB->RU_list[ru_id]->idx);
for (int i = 0; i < gNB->RU_list[ru_id]->nb_rx; aa++, i++) {
LOG_I(PHY, "Attaching RU %d antenna %d to gNB antenna %d\n", gNB->RU_list[ru_id]->idx, i, aa);
gNB->common_vars.rxdataF[aa] = (c16_t *)gNB->RU_list[ru_id]->common.rxdataF[i];
for (int b = 0; b < gNB->RU_list[ru_id]->num_beams_period; b++) {
int idx = i + b * gNB->RU_list[ru_id]->nb_rx;
gNB->common_vars.rxdataF[b][aa] = (c16_t *)gNB->RU_list[ru_id]->common.rxdataF[idx];
}
}
}
/* TODO: review this code, there is something wrong.

View File

@@ -49,7 +49,6 @@ static int DEFRUTPCORES[] = {-1,-1,-1,-1};
#include "nfapi_interface.h"
#include <nfapi/oai_integration/vendor_ext.h>
#include "executables/nr-softmodem-common.h"
#include "PHY/phy_digital_beamforming.h"
static void NRRCconfig_RU(configmodule_interface_t *cfg);
@@ -348,7 +347,7 @@ 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;
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)];
@@ -455,27 +454,49 @@ static void rx_rf(RU_t *ru, int *frame, int *slot)
stop_meas(&ru->rx_fhaul);
}
void ctrl_rf(RU_t *ru, int frame, int slot, uint64_t timestamp, struct nr_grid *nrg)
static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
{
const int num_ant_ports = ru->nb_tx;
uint16_t beam_id[num_ant_ports];
radio_tx_gpio_flag_t flags_gpio = 0;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
openair0_config_t *cfg0 = &ru->openair0_cfg;
for (int_fast16_t i = 0; i < num_ant_ports; i++) {
if (!IS_BIT_SET(nrg[i].grid_info->beam_id, 15))
return; // MSB must be set to send beam to RU
beam_id[i] = nrg[i].grid_info->beam_id & 0x7fff; // FAPI beam_id is only 15 bits. MSB already handled
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
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;
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;
}
default:
AssertFatal(false, "illegal GPIO controller %d\n", cfg0->gpio_controller);
}
const NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
uint64_t ts = timestamp + ru->ts_offset;
nfapi_nr_config_request_scf_t *cfg = &ru->config;
int slot_type = nr_slot_select(cfg, frame, slot % fp->slots_per_frame);
int prevslot_type = nr_slot_select(cfg, frame, (slot + (fp->slots_per_frame - 1)) % fp->slots_per_frame);
if (cfg->cell_config.frame_duplex_type.value == TDD && slot_type == NR_DOWNLINK_SLOT && prevslot_type == NR_UPLINK_SLOT
&& !get_softmodem_params()->continuous_tx && !IS_SOFTMODEM_RFSIM)
ts -= ru->sf_extension;
ru->rfdevice.trx_set_beams(&ru->rfdevice, beam_id, num_ant_ports, ts);
return flags_gpio;
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
@@ -493,6 +514,7 @@ 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);
@@ -536,22 +558,24 @@ 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;
int nt = ru->nb_tx * ru->num_beams_period;
void *txp[nt];
// prepare tx buffer pointers
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);
ctrl_rf(ru, frame, slot, timestamp, ru->common.ru_tx_grid);
// 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_burst);
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",
@@ -561,7 +585,7 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
slot,
proc->frame_tx_unwrap,
slot,
flags_burst,
flags,
siglen + sf_extension,
txs,
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
@@ -593,13 +617,13 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
AssertFatal(ru->nb_rx > 0 && ru->nb_rx <= 8, "openair0 does not support more than 8 antennas\n");
cfg->num_rb_dl = N_RB;
cfg->tx_num_channels = ru->nb_tx;
cfg->rx_num_channels = ru->nb_rx;
cfg->num_distributed_ru = 1;
cfg->tx_num_channels = ru->nb_tx * ru->num_beams_period;
cfg->rx_num_channels = ru->nb_rx * ru->num_beams_period;
cfg->num_distributed_ru = ru->num_beams_period;
LOG_I(PHY,"Setting RF config for N_RB %d, NB_RX %d, NB_TX %d\n",cfg->num_rb_dl,cfg->rx_num_channels,cfg->tx_num_channels);
LOG_I(PHY,"tune_offset %.0f Hz, sample_rate %.0f Hz\n",cfg->tune_offset,cfg->sample_rate);
for (int i = 0; i < ru->nb_tx; i++) {
for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) {
if (ru->if_frequency == 0) {
cfg->tx_freq[i] = fp->dl_CarrierFreq;
} else if (ru->if_freq_offset) {
@@ -614,7 +638,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
i, cfg->tx_gain[i],cfg->tx_freq[i]);
}
for (int i = 0; i < ru->nb_rx; i++) {
for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) {
if (ru->if_frequency == 0) {
cfg->rx_freq[i] = fp->ul_CarrierFreq;
} else if (ru->if_freq_offset) {
@@ -687,7 +711,7 @@ int setup_RU_buffers(RU_t *ru)
if (ru->openair0_cfg.mmapped_dma == 1) {
// replace RX signal buffers with mmaped HW versions
for (int i = 0; i < ru->nb_rx; i++) {
for (int i = 0; i < ru->nb_rx * ru->num_beams_period; i++) {
int card = i / 4;
int ant = i % 4;
LOG_D(PHY, "Mapping RU id %u, rx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant);
@@ -699,7 +723,7 @@ int setup_RU_buffers(RU_t *ru)
}
}
for (int i = 0; i < ru->nb_tx; i++) {
for (int i = 0; i < ru->nb_tx * ru->num_beams_period; i++) {
int card = i / 4;
int ant = i % 4;
LOG_D(PHY, "Mapping RU id %u, tx_ant %d, on card %d, chain %d\n", ru->idx, i, ru->rf_map.card + card, ru->rf_map.chain + ant);
@@ -823,9 +847,6 @@ void *ru_thread(void *param)
t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out");
if (t != NULL)
ru->fh_south_out = t;
t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out_ctrl");
if (t != NULL)
ru->fh_south_out_ctrl = t;
} else {
malloc_IF4p5_buffer(ru);
}
@@ -1106,14 +1127,13 @@ void set_function_spec_param(RU_t *ru)
ru->do_prach = 0; // no prach processing in RU
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
ru->feptx_prec = nr_feptx_prec;
ru->feptx_prec = NULL;
ru->fh_north_in = NULL; // no incoming fronthaul from north
ru->fh_north_out = NULL; // no outgoing fronthaul to north
ru->nr_start_if = NULL; // no if interface
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_out_ctrl = ctrl_rf; // local synchronous 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
@@ -1124,8 +1144,8 @@ void set_function_spec_param(RU_t *ru)
ru->do_prach = 0;
ru->txfh_in_fep = 0;
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_prec = nr_feptx_prec; // transmit precoding
ru->feptx_ofdm = nr_feptx_tp; // IDFTs
ru->feptx_prec = NULL; // need to do transmit Precoding + IDFTs
ru->feptx_ofdm = nr_feptx_tp; // need to do transmit Precoding + IDFTs
ru->fh_south_in = fh_if5_south_in; // synchronous IF5 reception
ru->fh_south_out = (ru->txfh_in_fep>0) ? NULL : fh_if5_south_out; // synchronous IF5 transmission
ru->fh_south_asynch_in = NULL; // no asynchronous UL

View File

@@ -131,18 +131,13 @@ void exit_function(const char *file, const char *function, const int line, const
printf("%s:%d %s() Exiting OAI softmodem: %s\n",file,line, function, s);
}
oai_exit = 1;
if (RC.ru == NULL)
exit(-1); // likely init not completed, prevent crash or hang, exit now...
for (ru_id=0; ru_id<RC.nb_RU; ru_id++) {
if (RC.ru[ru_id] == NULL) {
continue;
}
if (RC.ru[ru_id]->ifdevice.trx_stop_func) {
RC.ru[ru_id]->ifdevice.trx_stop_func(&RC.ru[ru_id]->ifdevice);
RC.ru[ru_id]->ifdevice.trx_stop_func = NULL;
}
if (RC.ru[ru_id]->rfdevice.trx_end_func) {
if (RC.ru[ru_id] && RC.ru[ru_id]->rfdevice.trx_end_func) {
if (RC.ru[ru_id]->rfdevice.trx_get_stats_func) {
RC.ru[ru_id]->rfdevice.trx_get_stats_func(&RC.ru[ru_id]->rfdevice);
RC.ru[ru_id]->rfdevice.trx_get_stats_func = NULL;
@@ -151,10 +146,6 @@ void exit_function(const char *file, const char *function, const int line, const
RC.ru[ru_id]->rfdevice.trx_end_func = NULL;
}
if (RC.ru[ru_id]->ifdevice.trx_stop_func) {
RC.ru[ru_id]->ifdevice.trx_stop_func(&RC.ru[ru_id]->ifdevice);
RC.ru[ru_id]->ifdevice.trx_stop_func = NULL;
}
if (RC.ru[ru_id] && RC.ru[ru_id]->ifdevice.trx_end_func) {
if (RC.ru[ru_id]->ifdevice.trx_get_stats_func) {
RC.ru[ru_id]->ifdevice.trx_get_stats_func(&RC.ru[ru_id]->ifdevice);
@@ -165,8 +156,6 @@ void exit_function(const char *file, const char *function, const int line, const
}
}
oai_exit = 1;
if (assert) {
abort();
} else {
@@ -369,6 +358,9 @@ int stop_L1(module_id_t gnb_id)
if (RC.nb_nr_L1_inst > 0)
stop_gNB(RC.nb_nr_L1_inst);
if (RC.nb_RU > 0)
stop_RU(RC.nb_RU);
/* stop trx devices, multiple carrier currently not supported by RU */
if (ru->rfdevice.trx_get_stats_func) {
ru->rfdevice.trx_get_stats_func(&ru->rfdevice);
@@ -386,9 +378,6 @@ int stop_L1(module_id_t gnb_id)
LOG_I(GNB_APP, "turned off RU ifdevice\n");
}
if (RC.nb_RU > 0)
stop_RU(RC.nb_RU);
/* release memory used by the RU/gNB threads (incomplete), after all
* threads have been stopped (they partially use the same memory) */
for (int inst = 0; inst < RC.nb_RU; inst++) {

View File

@@ -352,14 +352,6 @@ void nrue_ru_start(void)
}
}
void nrue_ru_stop(void)
{
for (int ru_id = 0; ru_id < nrue_ru_count; ru_id++) {
if (openair0_dev[ru_id].trx_stop_func)
openair0_dev[ru_id].trx_stop_func(&openair0_dev[ru_id]);
}
}
void nrue_ru_end(void)
{
for (int ru_id = 0; ru_id < nrue_ru_count; ru_id++) {

View File

@@ -22,7 +22,6 @@ void nrue_set_ru_params(configmodule_interface_t *cfg);
void nrue_init_openair0(void);
void nrue_ru_start(void);
void nrue_ru_stop(void);
void nrue_ru_end(void);
void nrue_ru_set_freq(PHY_VARS_NR_UE *UE, uint64_t ul_carrier, uint64_t dl_carrier, int freq_offset);
int nrue_ru_adjust_rx_gain(PHY_VARS_NR_UE *UE, int gain_change);

View File

@@ -112,7 +112,6 @@ void exit_function(const char *file, const char *function, const int line, const
oai_exit = 1;
nrue_ru_stop();
nrue_ru_end();
if (assert) {
@@ -458,8 +457,6 @@ int main(int argc, char **argv)
LOG_W(NR_PHY, "Returned from ITTI signal handler\n");
oai_exit = 1;
nrue_ru_stop();
if (PHY_vars_UE_g && PHY_vars_UE_g[0]) {
for (int CC_id = 0; CC_id < MAX_NUM_CCs; CC_id++) {
PHY_VARS_NR_UE *phy_vars = PHY_vars_UE_g[0][CC_id];

View File

@@ -52,7 +52,7 @@ uint32_t pullarray32(uint8_t **values_to_pull,
uint32_t pullarrays32(uint8_t **in, int32_t out[], uint32_t max_len, uint32_t len, uint8_t *end);
uint32_t pusharray8(uint8_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end);
uint32_t pusharray16(const uint16_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end);
uint32_t pusharray16(uint16_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end);
uint32_t pusharrays16(int16_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end);
uint32_t pusharray32(const uint32_t *values_to_push,
uint32_t max_num_values_to_push,

View File

@@ -381,7 +381,7 @@ uint32_t pullarrays16(uint8_t **in, int16_t out[], uint32_t max_len, uint32_t le
return 0;
}
}
uint32_t pusharray16(const uint16_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end)
uint32_t pusharray16(uint16_t in[], uint32_t max_len, uint32_t len, uint8_t **out, uint8_t *end)
{
if (len == 0)
return 1;

View File

@@ -719,7 +719,7 @@ uint8_t pack_dbt_table_tlv_value(void *tlv, uint8_t **ppWritePackedMsg, uint8_t
}
for (int k = 0; k< dbt_config->num_txrus; k++) {
const nfapi_nr_txru_t *tx_ru = &dig_beam->txru_list[k];
if (!(push16(tx_ru->r, ppWritePackedMsg, end) && push16(tx_ru->i, ppWritePackedMsg, end))) {
if (!(push16(tx_ru->dig_beam_weight_Re, ppWritePackedMsg, end) && push16(tx_ru->dig_beam_weight_Im, ppWritePackedMsg, end))) {
return 0;
}
}
@@ -1215,6 +1215,17 @@ uint8_t pack_nr_config_request(void *msg, uint8_t **ppWritePackedMsg, uint8_t *e
numTLVs++;
}
// The call to pack the TLV would be the same as any other TLV, it is only packed if the tag is set,
// so, it's safe to add the call to pack_nr_tlv even if it is not always set
retval &= pack_nr_tlv(NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG,
&(pNfapiMsg->analog_beamforming_ve.num_beams_period_vendor_ext),
ppWritePackedMsg,
end,
&pack_uint8_tlv_value);
// only increase if it was set
numTLVs += pNfapiMsg->analog_beamforming_ve.num_beams_period_vendor_ext.tl.tag == NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG;
// The call to pack the TLV would be the same as any other TLV, it is only packed if the tag is set,
// so, it's safe to add the call to pack_nr_tlv even if it is not always set
retval &= pack_nr_tlv(NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG,
@@ -1257,7 +1268,7 @@ static uint8_t unpack_dbt_table_tlv_value(void *tlv, uint8_t **ppReadPackedMsg,
}
for (int k = 0; k < dbt_config->num_txrus; k++) {
nfapi_nr_txru_t *tx_ru = &dig_beam->txru_list[k];
if (!(pulls16(ppReadPackedMsg, &tx_ru->r, end) && pulls16(ppReadPackedMsg, &tx_ru->i, end))) {
if (!(pull16(ppReadPackedMsg, &tx_ru->dig_beam_weight_Re, end) && pull16(ppReadPackedMsg, &tx_ru->dig_beam_weight_Im, end))) {
return 0;
}
}
@@ -1393,10 +1404,15 @@ uint8_t unpack_nr_config_request(uint8_t **ppReadPackedMsg, uint8_t *end, void *
{NFAPI_NR_CONFIG_TDD_TABLE, NULL, &unpack_nr_tdd_table_10_04},
#endif
#endif
{NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG,
&(pNfapiMsg->analog_beamforming_ve.analog_bf_vendor_ext),
&unpack_uint8_tlv_value},
{NFAPI_NR_FAPI_SSB_CASE_VENDOR_EXTENSION_TAG, &(pNfapiMsg->ssb_table.case_v3), &unpack_uint8_tlv_value},
{NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG,
&(pNfapiMsg->analog_beamforming_ve.num_beams_period_vendor_ext),
&unpack_uint8_tlv_value},
{NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG,
&(pNfapiMsg->analog_beamforming_ve.analog_bf_vendor_ext),
&unpack_uint8_tlv_value},
{NFAPI_NR_FAPI_SSB_CASE_VENDOR_EXTENSION_TAG,
&(pNfapiMsg->ssb_table.case_v3),
&unpack_uint8_tlv_value},
{NFAPI_NR_CONFIG_RSSI_MEASUREMENT_TAG, &(pNfapiMsg->measurement_config.rssi_measurement), &unpack_uint8_tlv_value},
{NFAPI_NR_CONFIG_BEAMFORMING_TABLE_TAG, NULL, &unpack_dbt_table_tlv_value},
{NFAPI_NR_CONFIG_PRECODING_TABLE_V6_TAG, NULL, &unpack_pm_table_tlv_value},

View File

@@ -257,8 +257,8 @@ bool eq_config_request(const nfapi_nr_config_request_scf_t *unpacked_req, const
for (int k = 0; k < req->dbt_config.num_txrus; ++k) {
const nfapi_nr_txru_t *unpacked_tx_ru = &unpacked_dig_beam->txru_list[k];
const nfapi_nr_txru_t *req_tx_ru = &req_dig_beam->txru_list[k];
EQ(unpacked_tx_ru->r, req_tx_ru->r);
EQ(unpacked_tx_ru->i, req_tx_ru->i);
EQ(unpacked_tx_ru->dig_beam_weight_Re, req_tx_ru->dig_beam_weight_Re);
EQ(unpacked_tx_ru->dig_beam_weight_Im, req_tx_ru->dig_beam_weight_Im);
}
}
@@ -298,6 +298,8 @@ bool eq_config_request(const nfapi_nr_config_request_scf_t *unpacked_req, const
EQ_TLV(unpacked_req->nfapi_config.timing_info_period, req->nfapi_config.timing_info_period);
EQ_TLV(unpacked_req->analog_beamforming_ve.num_beams_period_vendor_ext, req->analog_beamforming_ve.num_beams_period_vendor_ext);
EQ_TLV(unpacked_req->analog_beamforming_ve.analog_bf_vendor_ext, req->analog_beamforming_ve.analog_bf_vendor_ext);
return true;
@@ -811,8 +813,8 @@ void copy_config_request(const nfapi_nr_config_request_scf_t *src, nfapi_nr_conf
for (int k = 0; k < dst->dbt_config.num_txrus; ++k) {
nfapi_nr_txru_t *dst_tx_ru = &dst_dig_beam->txru_list[k];
const nfapi_nr_txru_t *src_tx_ru = &src_dig_beam->txru_list[k];
dst_tx_ru->r = src_tx_ru->r;
dst_tx_ru->i = src_tx_ru->i;
dst_tx_ru->dig_beam_weight_Re = src_tx_ru->dig_beam_weight_Re;
dst_tx_ru->dig_beam_weight_Im = src_tx_ru->dig_beam_weight_Im;
}
}
@@ -871,6 +873,8 @@ void copy_config_request(const nfapi_nr_config_request_scf_t *src, nfapi_nr_conf
COPY_TLV(dst->nfapi_config.tx_data_timing_offset, src->nfapi_config.tx_data_timing_offset);
COPY_TLV(dst->analog_beamforming_ve.num_beams_period_vendor_ext, src->analog_beamforming_ve.num_beams_period_vendor_ext);
COPY_TLV(dst->analog_beamforming_ve.analog_bf_vendor_ext, src->analog_beamforming_ve.analog_bf_vendor_ext);
}
@@ -1256,8 +1260,8 @@ void dump_config_request(const nfapi_nr_config_request_scf_t *msg)
depth++;
for (int k = 0; k < dbt_config->num_txrus; k++) {
const nfapi_nr_txru_t *tx_ru = &dig_beam->txru_list[k];
INDENTED_GENERIC_PRINT("Dig Beam Weight Real", "0x%02x", tx_ru->r);
INDENTED_GENERIC_PRINT("Dig Beam Weight Imaginary", "0x%02x", tx_ru->i);
INDENTED_GENERIC_PRINT("Dig Beam Weight Real", "0x%02x", tx_ru->dig_beam_weight_Re);
INDENTED_GENERIC_PRINT("Dig Beam Weight Imaginary", "0x%02x", tx_ru->dig_beam_weight_Im);
}
depth--;
}
@@ -1330,6 +1334,7 @@ void dump_config_request(const nfapi_nr_config_request_scf_t *msg)
INDENTED_TLV_FORMAT_PRINT("TX_DATA Timing Offset", "%d", nfapi_config->tx_data_timing_offset);
/* Beamforming VE */
const nfapi_nr_analog_beamforming_ve_t *analog_beamforming_ve = &msg->analog_beamforming_ve;
INDENTED_TLV_FORMAT_PRINT("Num Beams per Period", "%d", analog_beamforming_ve->num_beams_period_vendor_ext);
INDENTED_TLV_PRINT("Analog Beamforming VE", analog_beamforming_ve->analog_bf_vendor_ext);
/* Vendor Extension */
if (msg->vendor_extension) {

View File

@@ -267,17 +267,6 @@ static uint8_t pack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppWritePa
if(!pack_nr_tx_beamforming_pdu(&value->precodingAndBeamforming,ppWritePackedMsg, end)) {
return 0;
}
#ifndef ENABLE_AERIAL
if (!push8(value->param_v4.numSpatialStreamIndices, ppWritePackedMsg, end))
return 0;
if (!pusharray8(value->param_v4.spatialStreamIndices,
MAX_NUM_SPATIAL_STREAMS,
value->param_v4.numSpatialStreamIndices,
ppWritePackedMsg,
end))
return 0;
#endif
return 1;
}
@@ -317,31 +306,6 @@ static uint8_t pack_dl_tti_pdcch_pdu_rel15_value(void *tlv, uint8_t **ppWritePac
return 0;
}
}
#ifndef ENABLE_AERIAL
// Spatial steam indices for MU-MIMO
const nfapi_v4_pdcch_pdu_parameters_t *p = &value->param_v4;
if (!push16(p->numSpatialStreams, ppWritePackedMsg, end))
return 0;
for (uint_fast16_t i = 0; i < p->numSpatialStreams; i++)
if (!(push16(p->dci_spatialStreamIndices[i].dci_index, ppWritePackedMsg, end)
&& push16(p->dci_spatialStreamIndices[i].spatial_stream_index, ppWritePackedMsg, end)))
return 0;
#endif
return 1;
}
static inline uint8_t pack_spatial_stream_indices(const nfapi_nr_spatial_stream_index_t *ss,
uint8_t **ppWritePackedMsg,
uint8_t *end)
{
if (!push8(ss->numSpatialStreamIndices, ppWritePackedMsg, end))
return 0;
if (!pusharray16(ss->spatialStreamIndices, MAX_NUM_SPATIAL_STREAMS, ss->numSpatialStreamIndices, ppWritePackedMsg, end))
return 0;
return 1;
}
@@ -404,14 +368,6 @@ static uint8_t pack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppWritePac
if (!push8(value->maintenance_parms_v3.ldpcBaseGraph, ppWritePackedMsg, end)
|| !push32(value->maintenance_parms_v3.tbSizeLbrmBytes, ppWritePackedMsg, end))
return 0;
// PDSCH parameter in FAPI v4 for MU-MIMO spatial stream indexing
if (!push8(value->param_v4.numberCodewords, ppWritePackedMsg, end))
return 0;
for (uint_fast8_t c = 0; c < value->param_v4.numberCodewords; c++)
if (!pack_spatial_stream_indices(value->param_v4.spatialStreamsCw + c, ppWritePackedMsg, end))
return 0;
#endif
return 1;
}
@@ -433,11 +389,6 @@ static uint8_t pack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppWritePacke
if(!pack_nr_tx_beamforming_pdu(&value->precoding_and_beamforming,ppWritePackedMsg, end)) {
return 0;
}
#ifndef ENABLE_AERIAL
if (!(push8(value->param_v4.spatialStreamIndexPresent, ppWritePackedMsg, end)
&& push16(value->param_v4.spatialStreamIndex, ppWritePackedMsg, end)))
return 0;
#endif
return 1;
}
@@ -565,30 +516,6 @@ static uint8_t unpack_dl_tti_pdcch_pdu_rel15_value(void *tlv, uint8_t **ppReadPa
return 0;
}
}
#ifndef ENABLE_AERIAL
// Spatial steam indices for MU-MIMO
nfapi_v4_pdcch_pdu_parameters_t *p = &value->param_v4;
if (!pull16(ppReadPackedMsg, &p->numSpatialStreams, end))
return 0;
for (uint_fast16_t i = 0; i < p->numSpatialStreams; i++) {
if (!(pull16(ppReadPackedMsg, &p->dci_spatialStreamIndices[i].dci_index, end)
&& pull16(ppReadPackedMsg, &p->dci_spatialStreamIndices[i].spatial_stream_index, end)))
return 0;
}
#endif
return 1;
}
static inline uint8_t unpack_spatial_stream_indices(nfapi_nr_spatial_stream_index_t *ss, uint8_t **ppReadPackedMsg, uint8_t *end)
{
if (!pull8(ppReadPackedMsg, &ss->numSpatialStreamIndices, end))
return 0;
if (!pullarray16(ppReadPackedMsg, ss->spatialStreamIndices, MAX_NUM_SPATIAL_STREAMS, ss->numSpatialStreamIndices, end))
return 0;
return 1;
}
@@ -651,14 +578,6 @@ static uint8_t unpack_dl_tti_pdsch_pdu_rel15_value(void *tlv, uint8_t **ppReadPa
if (!pull8(ppReadPackedMsg, &value->maintenance_parms_v3.ldpcBaseGraph, end)
|| !pull32(ppReadPackedMsg, &value->maintenance_parms_v3.tbSizeLbrmBytes, end))
return 0;
// PDSCH parameter in FAPI v4 for MU-MIMO spatial stream indexing
if (!pull8(ppReadPackedMsg, &value->param_v4.numberCodewords, end))
return 0;
for (uint_fast8_t c = 0; c < value->param_v4.numberCodewords; c++)
if (!unpack_spatial_stream_indices(value->param_v4.spatialStreamsCw + c, ppReadPackedMsg, end))
return 0;
#endif
return 1;
}
@@ -682,17 +601,6 @@ static uint8_t unpack_dl_tti_csi_rs_pdu_rel15_value(void *tlv, uint8_t **ppReadP
if(!unpack_nr_tx_beamforming_pdu(&value->precodingAndBeamforming, ppReadPackedMsg, end)) {
return 0;
}
#ifndef ENABLE_AERIAL
if (!pull8(ppReadPackedMsg, &value->param_v4.numSpatialStreamIndices, end))
return 0;
if (!pullarray8(ppReadPackedMsg,
value->param_v4.spatialStreamIndices,
MAX_NUM_SPATIAL_STREAMS,
value->param_v4.numSpatialStreamIndices,
end))
return 0;
#endif
return 1;
}
@@ -717,11 +625,6 @@ static uint8_t unpack_dl_tti_ssb_pdu_rel15_value(void *tlv, uint8_t **ppReadPack
if(!unpack_nr_tx_beamforming_pdu(&value->precoding_and_beamforming, ppReadPackedMsg, end)) {
return 0;
}
#ifndef ENABLE_AERIAL
if (!(pull8(ppReadPackedMsg, &value->param_v4.spatialStreamIndexPresent, end)
&& pull16(ppReadPackedMsg, &value->param_v4.spatialStreamIndex, end)))
return 0;
#endif
return 1;
}
@@ -821,14 +724,7 @@ static uint8_t pack_ul_tti_request_prach_pdu(const nfapi_nr_prach_pdu_t *prach_p
return 0;
}
if (!pack_nr_rx_beamforming_pdu(&prach_pdu->beamforming, ppWritePackedMsg, end))
return 0;
const nfapi_nr_spatial_stream_index_t *p = &prach_pdu->param_v4;
if (!pack_spatial_stream_indices(p, ppWritePackedMsg, end))
return 0;
return 1;
return pack_nr_rx_beamforming_pdu(&prach_pdu->beamforming, ppWritePackedMsg, end);
}
static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, uint8_t **ppWritePackedMsg, uint8_t *end)
@@ -920,8 +816,7 @@ static uint8_t pack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, ui
}
#ifndef ENABLE_AERIAL
if (!(push8(pusch_pdu->maintenance_parms_v3.ldpcBaseGraph, ppWritePackedMsg, end)
&& push32(pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes, ppWritePackedMsg, end)
&& pack_spatial_stream_indices(&pusch_pdu->param_v4, ppWritePackedMsg, end)))
&& push32(pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes, ppWritePackedMsg, end)))
return 0;
#endif
return 1;
@@ -949,15 +844,7 @@ static uint8_t pack_ul_tti_request_pucch_pdu(const nfapi_nr_pucch_pdu_t *pucch_p
return 0;
}
if (!pack_nr_rx_beamforming_pdu(&pucch_pdu->beamforming, ppWritePackedMsg, end))
return 0;
#ifndef ENABLE_AERIAL
if (!pack_spatial_stream_indices(&pucch_pdu->param_v4, ppWritePackedMsg, end))
return 0;
#endif
return 1;
return pack_nr_rx_beamforming_pdu(&pucch_pdu->beamforming, ppWritePackedMsg, end);
}
static uint8_t pack_ul_tti_request_srs_parameters_v4(nfapi_v4_srs_parameters_t *srsParameters,
@@ -1162,14 +1049,7 @@ static uint8_t unpack_ul_tti_request_prach_pdu(nfapi_nr_prach_pdu_t *prach_pdu,
return 0;
}
if (!unpack_nr_rx_beamforming_pdu(&prach_pdu->beamforming, ppReadPackedMsg, end))
return 0;
#ifndef ENABLE_AERIAL
if (!unpack_spatial_stream_indices(&prach_pdu->param_v4, ppReadPackedMsg, end))
return 0;
#endif
return 1;
return unpack_nr_rx_beamforming_pdu(&prach_pdu->beamforming, ppReadPackedMsg, end);
}
static uint8_t unpack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu, uint8_t **ppReadPackedMsg, uint8_t *end)
@@ -1258,8 +1138,7 @@ static uint8_t unpack_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pusch_pdu,
}
#ifndef ENABLE_AERIAL
if (!(pull8(ppReadPackedMsg, &pusch_pdu->maintenance_parms_v3.ldpcBaseGraph, end)
&& pull32(ppReadPackedMsg, &pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes, end)
&& unpack_spatial_stream_indices(&pusch_pdu->param_v4, ppReadPackedMsg, end)))
&& pull32(ppReadPackedMsg, &pusch_pdu->maintenance_parms_v3.tbSizeLbrmBytes, end)))
return 0;
#endif
return 1;
@@ -1286,14 +1165,7 @@ static uint8_t unpack_ul_tti_request_pucch_pdu(nfapi_nr_pucch_pdu_t *pucch_pdu,
&& pull16(ppReadPackedMsg, &pucch_pdu->bit_len_csi_part2, end))) {
return 0;
}
if (!unpack_nr_rx_beamforming_pdu(&pucch_pdu->beamforming, ppReadPackedMsg, end))
return 0;
#ifndef ENABLE_AERIAL
if (!unpack_spatial_stream_indices(&pucch_pdu->param_v4, ppReadPackedMsg, end))
return 0;
#endif
return 1;
return unpack_nr_rx_beamforming_pdu(&pucch_pdu->beamforming, ppReadPackedMsg, end);
}
static uint8_t unpack_ul_tti_request_srs_parameters_v4(nfapi_v4_srs_parameters_t *srsParameters,

View File

@@ -983,76 +983,126 @@ static void copy_dl_tti_beamforming(const nfapi_nr_tx_precoding_and_beamforming_
}
}
// Macro to get size from start to a member (exclusive)
#define SIZE_UPTO(type, member) offsetof(type, member)
// Macro to get size from start to a member (inclusive)
#define SIZE_INCLUDING(type, member) (offsetof(type, member) + sizeof(((type *)0)->member))
// Macro to copy partial struct
#define PARTIAL_COPY(dst, src, type, last_member) memcpy((dst), (src), SIZE_INCLUDING(type, last_member))
// Macro to copy partial struct
#define PARTIAL_COPY_RANGE(dst, src, type, first_member, last_member) \
memcpy(((uint8_t *)(dst)) + SIZE_UPTO(type, first_member), \
((uint8_t *)(src)) + SIZE_UPTO(type, first_member), \
SIZE_INCLUDING(type, last_member) - SIZE_UPTO(type, first_member))
// Macro to copy array
#define COPY_STRUCT_ARRAY(dst, src, data_member, count_member) \
do { \
size_t _len = (src)->count_member * sizeof((src)->data_member[0]); \
if (_len > sizeof((dst)->data_member)) \
_len = sizeof((dst)->data_member); \
(dst)->count_member = (src)->count_member; \
memcpy((dst)->data_member, (src)->data_member, _len); \
} while (0)
static void copy_dl_tti_request_pdcch_pdu(const nfapi_nr_dl_tti_pdcch_pdu_rel15_t *src, nfapi_nr_dl_tti_pdcch_pdu_rel15_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_dl_tti_pdcch_pdu_rel15_t, numDlDci);
size_t partial_size = offsetof(nfapi_nr_dl_tti_pdcch_pdu_rel15_t, dci_pdu);
memcpy(dst, src, partial_size);
dst->BWPSize = src->BWPSize;
dst->BWPStart = src->BWPStart;
dst->SubcarrierSpacing = src->SubcarrierSpacing;
dst->CyclicPrefix = src->CyclicPrefix;
dst->StartSymbolIndex = src->StartSymbolIndex;
dst->DurationSymbols = src->DurationSymbols;
for (int fdr_idx = 0; fdr_idx < 6; ++fdr_idx) {
dst->FreqDomainResource[fdr_idx] = src->FreqDomainResource[fdr_idx];
}
dst->CceRegMappingType = src->CceRegMappingType;
dst->RegBundleSize = src->RegBundleSize;
dst->InterleaverSize = src->InterleaverSize;
dst->CoreSetType = src->CoreSetType;
dst->ShiftIndex = src->ShiftIndex;
dst->precoderGranularity = src->precoderGranularity;
dst->numDlDci = src->numDlDci;
for (int dl_dci = 0; dl_dci < dst->numDlDci; ++dl_dci) {
nfapi_nr_dl_dci_pdu_t *dst_dci_pdu = &dst->dci_pdu[dl_dci];
const nfapi_nr_dl_dci_pdu_t *src_dci_pdu = &src->dci_pdu[dl_dci];
PARTIAL_COPY(dst_dci_pdu, src_dci_pdu, nfapi_nr_dl_dci_pdu_t, AggregationLevel);
dst_dci_pdu->RNTI = src_dci_pdu->RNTI;
dst_dci_pdu->ScramblingId = src_dci_pdu->ScramblingId;
dst_dci_pdu->ScramblingRNTI = src_dci_pdu->ScramblingRNTI;
dst_dci_pdu->CceIndex = src_dci_pdu->CceIndex;
dst_dci_pdu->AggregationLevel = src_dci_pdu->AggregationLevel;
copy_dl_tti_beamforming(&src_dci_pdu->precodingAndBeamforming, &dst_dci_pdu->precodingAndBeamforming);
PARTIAL_COPY_RANGE(dst_dci_pdu, src_dci_pdu, nfapi_nr_dl_dci_pdu_t, beta_PDCCH_1_0, Payload);
}
dst->param_v4.numSpatialStreams = src->param_v4.numSpatialStreams;
for (uint_fast16_t i = 0; i < dst->param_v4.numSpatialStreams; i++) {
dst->param_v4.dci_spatialStreamIndices[i] = src->param_v4.dci_spatialStreamIndices[i];
dst_dci_pdu->beta_PDCCH_1_0 = src_dci_pdu->beta_PDCCH_1_0;
dst_dci_pdu->powerControlOffsetSS = src_dci_pdu->powerControlOffsetSS;
dst_dci_pdu->PayloadSizeBits = src_dci_pdu->PayloadSizeBits;
for (int i = 0; i < 8; ++i) {
dst_dci_pdu->Payload[i] = src_dci_pdu->Payload[i];
}
}
}
static void copy_dl_tti_request_pdsch_pdu(const nfapi_nr_dl_tti_pdsch_pdu_rel15_t *src, nfapi_nr_dl_tti_pdsch_pdu_rel15_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_dl_tti_pdsch_pdu_rel15_t, nEpreRatioOfPDSCHToPTRS);
copy_dl_tti_beamforming(&src->precodingAndBeamforming, &dst->precodingAndBeamforming);
PARTIAL_COPY_RANGE(dst, src, nfapi_nr_dl_tti_pdsch_pdu_rel15_t, powerControlOffset, maintenance_parms_v3);
dst->param_v4.numberCodewords = src->param_v4.numberCodewords;
for (uint_fast8_t i = 0; i < dst->param_v4.numberCodewords; i++) {
const nfapi_nr_spatial_stream_index_t *s = src->param_v4.spatialStreamsCw + i;
nfapi_nr_spatial_stream_index_t *d = dst->param_v4.spatialStreamsCw + i;
COPY_STRUCT_ARRAY(d, s, spatialStreamIndices, numSpatialStreamIndices);
dst->pduBitmap = src->pduBitmap;
dst->rnti = src->rnti;
dst->pduIndex = src->pduIndex;
dst->BWPSize = src->BWPSize;
dst->BWPStart = src->BWPStart;
dst->SubcarrierSpacing = src->SubcarrierSpacing;
dst->CyclicPrefix = src->CyclicPrefix;
dst->NrOfCodewords = src->NrOfCodewords;
for (int cw = 0; cw < dst->NrOfCodewords; ++cw) {
dst->targetCodeRate[cw] = src->targetCodeRate[cw];
dst->qamModOrder[cw] = src->qamModOrder[cw];
dst->mcsIndex[cw] = src->mcsIndex[cw];
dst->mcsTable[cw] = src->mcsTable[cw];
dst->rvIndex[cw] = src->rvIndex[cw];
dst->TBSize[cw] = src->TBSize[cw];
}
dst->dataScramblingId = src->dataScramblingId;
dst->nrOfLayers = src->nrOfLayers;
dst->transmissionScheme = src->transmissionScheme;
dst->refPoint = src->refPoint;
dst->dlDmrsSymbPos = src->dlDmrsSymbPos;
dst->dmrsConfigType = src->dmrsConfigType;
dst->dlDmrsScramblingId = src->dlDmrsScramblingId;
dst->SCID = src->SCID;
dst->numDmrsCdmGrpsNoData = src->numDmrsCdmGrpsNoData;
dst->dmrsPorts = src->dmrsPorts;
dst->resourceAlloc = src->resourceAlloc;
for (int i = 0; i < 36; ++i) {
dst->rbBitmap[i] = src->rbBitmap[i];
}
dst->rbStart = src->rbStart;
dst->rbSize = src->rbSize;
dst->VRBtoPRBMapping = src->VRBtoPRBMapping;
dst->StartSymbolIndex = src->StartSymbolIndex;
dst->NrOfSymbols = src->NrOfSymbols;
dst->PTRSPortIndex = src->PTRSPortIndex;
dst->PTRSTimeDensity = src->PTRSTimeDensity;
dst->PTRSFreqDensity = src->PTRSFreqDensity;
dst->PTRSReOffset = src->PTRSReOffset;
dst->nEpreRatioOfPDSCHToPTRS = src->nEpreRatioOfPDSCHToPTRS;
copy_dl_tti_beamforming(&src->precodingAndBeamforming, &dst->precodingAndBeamforming);
dst->powerControlOffset = src->powerControlOffset;
dst->powerControlOffsetSS = src->powerControlOffsetSS;
dst->isLastCbPresent = src->isLastCbPresent;
dst->isInlineTbCrc = src->isInlineTbCrc;
dst->dlTbCrc = src->dlTbCrc;
dst->maintenance_parms_v3.ldpcBaseGraph = src->maintenance_parms_v3.ldpcBaseGraph;
dst->maintenance_parms_v3.tbSizeLbrmBytes = src->maintenance_parms_v3.tbSizeLbrmBytes;
}
static void copy_dl_tti_request_csi_rs_pdu(const nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *src, nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_dl_tti_csi_rs_pdu_rel15_t, power_control_offset_ss);
dst->bwp_size = src->bwp_size;
dst->bwp_start = src->bwp_start;
dst->subcarrier_spacing = src->subcarrier_spacing;
dst->cyclic_prefix = src->cyclic_prefix;
dst->start_rb = src->start_rb;
dst->nr_of_rbs = src->nr_of_rbs;
dst->csi_type = src->csi_type;
dst->row = src->row;
dst->freq_domain = src->freq_domain;
dst->symb_l0 = src->symb_l0;
dst->symb_l1 = src->symb_l1;
dst->cdm_type = src->cdm_type;
dst->freq_density = src->freq_density;
dst->scramb_id = src->scramb_id;
dst->power_control_offset = src->power_control_offset;
dst->power_control_offset_ss = src->power_control_offset_ss;
copy_dl_tti_beamforming(&src->precodingAndBeamforming, &dst->precodingAndBeamforming);
const struct nfapi_nr_csi_spatial_stream_index *s = &src->param_v4;
struct nfapi_nr_csi_spatial_stream_index *d = &dst->param_v4;
COPY_STRUCT_ARRAY(d, s, spatialStreamIndices, numSpatialStreamIndices);
}
static void copy_dl_tti_request_ssb_pdu(const nfapi_nr_dl_tti_ssb_pdu_rel15_t *src, nfapi_nr_dl_tti_ssb_pdu_rel15_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_dl_tti_ssb_pdu_rel15_t, ssbRsrp);
dst->PhysCellId = src->PhysCellId;
dst->BetaPss = src->BetaPss;
dst->SsbBlockIndex = src->SsbBlockIndex;
dst->SsbSubcarrierOffset = src->SsbSubcarrierOffset;
dst->ssbOffsetPointA = src->ssbOffsetPointA;
dst->bchPayloadFlag = src->bchPayloadFlag;
dst->bchPayload = src->bchPayload;
dst->ssbRsrp = src->ssbRsrp;
copy_dl_tti_beamforming(&src->precoding_and_beamforming, &dst->precoding_and_beamforming);
dst->param_v4 = src->param_v4;
}
static void copy_dl_tti_request_pdu(const nfapi_nr_dl_tti_request_pdu_t *src, nfapi_nr_dl_tti_request_pdu_t *dst)
@@ -1125,25 +1175,73 @@ static void copy_ul_tti_beamforming(const nfapi_nr_ul_beamforming_t *src, nfapi_
static void copy_ul_tti_request_prach_pdu(const nfapi_nr_prach_pdu_t *src, nfapi_nr_prach_pdu_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_prach_pdu_t, num_cs);
dst->phys_cell_id = src->phys_cell_id;
dst->num_prach_ocas = src->num_prach_ocas;
dst->prach_format = src->prach_format;
dst->num_ra = src->num_ra;
dst->prach_start_symbol = src->prach_start_symbol;
dst->num_cs = src->num_cs;
copy_ul_tti_beamforming(&src->beamforming, &dst->beamforming);
const nfapi_nr_spatial_stream_index_t *s = &src->param_v4;
nfapi_nr_spatial_stream_index_t *d = &dst->param_v4;
COPY_STRUCT_ARRAY(d, s, spatialStreamIndices, numSpatialStreamIndices);
}
static void copy_ul_tti_request_pusch_pdu(const nfapi_nr_pusch_pdu_t *src, nfapi_nr_pusch_pdu_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_pusch_pdu_t, nr_of_symbols);
dst->pdu_bit_map = src->pdu_bit_map;
dst->rnti = src->rnti;
dst->handle = src->handle;
dst->bwp_size = src->bwp_size;
dst->bwp_start = src->bwp_start;
dst->subcarrier_spacing = src->subcarrier_spacing;
dst->cyclic_prefix = src->cyclic_prefix;
dst->target_code_rate = src->target_code_rate;
dst->qam_mod_order = src->qam_mod_order;
dst->mcs_index = src->mcs_index;
dst->mcs_table = src->mcs_table;
dst->transform_precoding = src->transform_precoding;
dst->data_scrambling_id = src->data_scrambling_id;
dst->nrOfLayers = src->nrOfLayers;
dst->ul_dmrs_symb_pos = src->ul_dmrs_symb_pos;
dst->dmrs_config_type = src->dmrs_config_type;
dst->ul_dmrs_scrambling_id = src->ul_dmrs_scrambling_id;
dst->pusch_identity = src->pusch_identity;
dst->scid = src->scid;
dst->num_dmrs_cdm_grps_no_data = src->num_dmrs_cdm_grps_no_data;
dst->dmrs_ports = src->dmrs_ports;
dst->resource_alloc = src->resource_alloc;
for (int i = 0; i < 36; ++i) {
dst->rb_bitmap[i] = src->rb_bitmap[i];
}
dst->rb_start = src->rb_start;
dst->rb_size = src->rb_size;
dst->vrb_to_prb_mapping = src->vrb_to_prb_mapping;
dst->frequency_hopping = src->frequency_hopping;
dst->tx_direct_current_location = src->tx_direct_current_location;
dst->uplink_frequency_shift_7p5khz = src->uplink_frequency_shift_7p5khz;
dst->start_symbol_index = src->start_symbol_index;
dst->nr_of_symbols = src->nr_of_symbols;
if (dst->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_DATA) {
const nfapi_nr_pusch_data_t *src_pusch_data = &src->pusch_data;
nfapi_nr_pusch_data_t *dst_pusch_data = &dst->pusch_data;
*dst_pusch_data = *src_pusch_data;
dst_pusch_data->rv_index = src_pusch_data->rv_index;
dst_pusch_data->harq_process_id = src_pusch_data->harq_process_id;
dst_pusch_data->new_data_indicator = src_pusch_data->new_data_indicator;
dst_pusch_data->tb_size = src_pusch_data->tb_size;
dst_pusch_data->num_cb = src_pusch_data->num_cb;
for (int i = 0; i < (src_pusch_data->num_cb + 7) / 8; ++i) {
dst_pusch_data->cb_present_and_position[i] = src_pusch_data->cb_present_and_position[i];
}
}
if (src->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_UCI) {
const nfapi_nr_pusch_uci_t *src_pusch_uci = &src->pusch_uci;
nfapi_nr_pusch_uci_t *dst_pusch_uci = &dst->pusch_uci;
*dst_pusch_uci = *src_pusch_uci;
dst_pusch_uci->harq_ack_bit_length = src_pusch_uci->harq_ack_bit_length;
dst_pusch_uci->csi_part1_bit_length = src_pusch_uci->csi_part1_bit_length;
dst_pusch_uci->csi_part2_bit_length = src_pusch_uci->csi_part2_bit_length;
dst_pusch_uci->alpha_scaling = src_pusch_uci->alpha_scaling;
dst_pusch_uci->beta_offset_harq_ack = src_pusch_uci->beta_offset_harq_ack;
dst_pusch_uci->beta_offset_csi1 = src_pusch_uci->beta_offset_csi1;
dst_pusch_uci->beta_offset_csi2 = src_pusch_uci->beta_offset_csi2;
}
if (src->pdu_bit_map & PUSCH_PDU_BITMAP_PUSCH_PTRS) {
const nfapi_nr_pusch_ptrs_t *src_pusch_ptrs = &src->pusch_ptrs;
@@ -1154,7 +1252,10 @@ static void copy_ul_tti_request_pusch_pdu(const nfapi_nr_pusch_pdu_t *src, nfapi
for (int i = 0; i < src_pusch_ptrs->num_ptrs_ports; ++i) {
const nfapi_nr_ptrs_ports_t *src_ptrs_port = &src_pusch_ptrs->ptrs_ports_list[i];
nfapi_nr_ptrs_ports_t *dst_ptrs_port = &dst_pusch_ptrs->ptrs_ports_list[i];
*dst_ptrs_port = *src_ptrs_port;
dst_ptrs_port->ptrs_port_index = src_ptrs_port->ptrs_port_index;
dst_ptrs_port->ptrs_dmrs_port = src_ptrs_port->ptrs_dmrs_port;
dst_ptrs_port->ptrs_re_offset = src_ptrs_port->ptrs_re_offset;
}
dst_pusch_ptrs->ptrs_time_density = src_pusch_ptrs->ptrs_time_density;
@@ -1164,23 +1265,50 @@ static void copy_ul_tti_request_pusch_pdu(const nfapi_nr_pusch_pdu_t *src, nfapi
if (src->pdu_bit_map & PUSCH_PDU_BITMAP_DFTS_OFDM) {
const nfapi_nr_dfts_ofdm_t *src_dfts_ofdm = &src->dfts_ofdm;
nfapi_nr_dfts_ofdm_t *dst_dfts_ofdm = &dst->dfts_ofdm;
*dst_dfts_ofdm = *src_dfts_ofdm;
dst_dfts_ofdm->low_papr_group_number = src_dfts_ofdm->low_papr_group_number;
dst_dfts_ofdm->low_papr_sequence_number = src_dfts_ofdm->low_papr_sequence_number;
dst_dfts_ofdm->ul_ptrs_sample_density = src_dfts_ofdm->ul_ptrs_sample_density;
dst_dfts_ofdm->ul_ptrs_time_density_transform_precoding = src_dfts_ofdm->ul_ptrs_time_density_transform_precoding;
}
copy_ul_tti_beamforming(&src->beamforming, &dst->beamforming);
dst->maintenance_parms_v3.ldpcBaseGraph = src->maintenance_parms_v3.ldpcBaseGraph;
dst->maintenance_parms_v3.tbSizeLbrmBytes = src->maintenance_parms_v3.tbSizeLbrmBytes;
const nfapi_nr_spatial_stream_index_t *s = &src->param_v4;
nfapi_nr_spatial_stream_index_t *d = &dst->param_v4;
COPY_STRUCT_ARRAY(d, s, spatialStreamIndices, numSpatialStreamIndices);
}
static void copy_ul_tti_request_pucch_pdu(const nfapi_nr_pucch_pdu_t *src, nfapi_nr_pucch_pdu_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_pucch_pdu_t, bit_len_csi_part2);
dst->rnti = src->rnti;
dst->handle = src->handle;
dst->bwp_size = src->bwp_size;
dst->bwp_start = src->bwp_start;
dst->subcarrier_spacing = src->subcarrier_spacing;
dst->cyclic_prefix = src->cyclic_prefix;
dst->format_type = src->format_type;
dst->multi_slot_tx_indicator = src->multi_slot_tx_indicator;
dst->pi_2bpsk = src->pi_2bpsk;
dst->prb_start = src->prb_start;
dst->prb_size = src->prb_size;
dst->start_symbol_index = src->start_symbol_index;
dst->nr_of_symbols = src->nr_of_symbols;
dst->freq_hop_flag = src->freq_hop_flag;
dst->second_hop_prb = src->second_hop_prb;
dst->group_hop_flag = src->group_hop_flag;
dst->sequence_hop_flag = src->sequence_hop_flag;
dst->hopping_id = src->hopping_id;
dst->initial_cyclic_shift = src->initial_cyclic_shift;
dst->data_scrambling_id = src->data_scrambling_id;
dst->time_domain_occ_idx = src->time_domain_occ_idx;
dst->pre_dft_occ_idx = src->pre_dft_occ_idx;
dst->pre_dft_occ_len = src->pre_dft_occ_len;
dst->add_dmrs_flag = src->add_dmrs_flag;
dst->dmrs_scrambling_id = src->dmrs_scrambling_id;
dst->dmrs_cyclic_shift = src->dmrs_cyclic_shift;
dst->sr_flag = src->sr_flag;
dst->bit_len_harq = src->bit_len_harq;
dst->bit_len_csi_part1 = src->bit_len_csi_part1;
dst->bit_len_csi_part2 = src->bit_len_csi_part2;
copy_ul_tti_beamforming(&src->beamforming, &dst->beamforming);
const nfapi_nr_spatial_stream_index_t *s = &src->param_v4;
nfapi_nr_spatial_stream_index_t *d = &dst->param_v4;
COPY_STRUCT_ARRAY(d, s, spatialStreamIndices, numSpatialStreamIndices);
}
static void copy_ul_tti_request_srs_parameters(const nfapi_v4_srs_parameters_t *src,
@@ -1188,20 +1316,24 @@ static void copy_ul_tti_request_srs_parameters(const nfapi_v4_srs_parameters_t *
nfapi_v4_srs_parameters_t *dst)
{
dst->srs_bandwidth_size = src->srs_bandwidth_size;
#ifdef ENABLE_AERIAL
// For Aerial, we always process the 4 reported symbols, not only the ones indicated by num_symbols
const uint8_t symbols_to_fill = 4;
(void)num_symbols; // To supress unused warning
#else
const uint8_t symbols_to_fill = num_symbols;
#endif // ENABLE_AERIAL
for (uint_fast8_t symbol_idx = 0; symbol_idx < symbols_to_fill; ++symbol_idx) {
for (int symbol_idx = 0; symbol_idx < num_symbols; ++symbol_idx) {
nfapi_v4_srs_parameters_symbols_t *dst_symbol = &dst->symbol_list[symbol_idx];
const nfapi_v4_srs_parameters_symbols_t *src_symbol = &src->symbol_list[symbol_idx];
dst_symbol->srs_bandwidth_start = src_symbol->srs_bandwidth_start;
dst_symbol->sequence_group = src_symbol->sequence_group;
dst_symbol->sequence_number = src_symbol->sequence_number;
}
#ifdef ENABLE_AERIAL
// For Aerial, we always process the 4 reported symbols, not only the ones indicated by num_symbols
for (int symbol_idx = num_symbols; symbol_idx < 4; ++symbol_idx) {
nfapi_v4_srs_parameters_symbols_t *dst_symbol = &dst->symbol_list[symbol_idx];
const nfapi_v4_srs_parameters_symbols_t *src_symbol = &src->symbol_list[symbol_idx];
dst_symbol->srs_bandwidth_start = src_symbol->srs_bandwidth_start;
dst_symbol->sequence_group = src_symbol->sequence_group;
dst_symbol->sequence_number = src_symbol->sequence_number;
}
#endif // ENABLE_AERIAL
dst->usage = src->usage;
const uint8_t nUsage = __builtin_popcount(dst->usage);
for (int idx = 0; idx < nUsage; ++idx) {
@@ -1214,12 +1346,37 @@ static void copy_ul_tti_request_srs_parameters(const nfapi_v4_srs_parameters_t *
dst->ue_antennas_in_this_srs_resource_set = src->ue_antennas_in_this_srs_resource_set;
dst->sampled_ue_antennas = src->sampled_ue_antennas;
dst->report_scope = src->report_scope;
COPY_STRUCT_ARRAY(dst, src, Ul_spatial_stream_ports, num_ul_spatial_streams_ports);
dst->num_ul_spatial_streams_ports = src->num_ul_spatial_streams_ports;
for (int idx = 0; idx < dst->num_ul_spatial_streams_ports; ++idx) {
dst->Ul_spatial_stream_ports[idx] = src->Ul_spatial_stream_ports[idx];
}
}
static void copy_ul_tti_request_srs_pdu(const nfapi_nr_srs_pdu_t *src, nfapi_nr_srs_pdu_t *dst)
{
PARTIAL_COPY(dst, src, nfapi_nr_srs_pdu_t, t_offset);
dst->rnti = src->rnti;
dst->handle = src->handle;
dst->bwp_size = src->bwp_size;
dst->bwp_start = src->bwp_start;
dst->subcarrier_spacing = src->subcarrier_spacing;
dst->cyclic_prefix = src->cyclic_prefix;
dst->num_ant_ports = src->num_ant_ports;
dst->num_symbols = src->num_symbols;
dst->num_repetitions = src->num_repetitions;
dst->time_start_position = src->time_start_position;
dst->config_index = src->config_index;
dst->sequence_id = src->sequence_id;
dst->bandwidth_index = src->bandwidth_index;
dst->comb_size = src->comb_size;
dst->comb_offset = src->comb_offset;
dst->cyclic_shift = src->cyclic_shift;
dst->frequency_position = src->frequency_position;
dst->frequency_shift = src->frequency_shift;
dst->frequency_hopping = src->frequency_hopping;
dst->group_or_sequence_hopping = src->group_or_sequence_hopping;
dst->resource_type = src->resource_type;
dst->t_srs = src->t_srs;
dst->t_offset = src->t_offset;
copy_ul_tti_beamforming(&src->beamforming, &dst->beamforming);
copy_ul_tti_request_srs_parameters(&src->srs_parameters_v4, 1 << src->num_symbols, &dst->srs_parameters_v4);
}

View File

@@ -25,6 +25,7 @@
#define NFAPI_NR_NFAPI_TIMING_WINDOW_TAG 0x011E
#define NFAPI_NR_NFAPI_TIMING_INFO_MODE_TAG 0x011F
#define NFAPI_NR_NFAPI_TIMING_INFO_PERIOD_TAG 0x0120
#define NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG 0xA000
#define NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG 0xA001
#define NFAPI_NR_FAPI_SSB_CASE_VENDOR_EXTENSION_TAG 0xA002

View File

@@ -11,13 +11,19 @@
#include "nfapi_interface.h"
#include "nfapi_nr_interface.h"
#define NFAPI_NR_MAX_NB_CCE_AGGREGATION_LEVELS 5
#define NFAPI_NR_MAX_NB_TCI_STATES_PDCCH 64
#define NFAPI_NR_MAX_NB_CORESETS 12
#define NFAPI_NR_MAX_NB_SEARCH_SPACES 40
#define NFAPI_MAX_NUM_UL_UE_PER_GROUP 6
#define NFAPI_MAX_NUM_UL_PDU 255
#define NFAPI_MAX_NUM_UCI_INDICATION 8
#define NFAPI_MAX_NUM_GROUPS 8
#define NFAPI_MAX_NUM_CB 8
#define NFAPI_MAX_NUM_PRGS 1
#define NFAPI_MAX_NUM_BG_IF 6
#define NFAPI_MAX_NUM_PERIODS 8
// Extension to the generic structures for single tlv values
@@ -466,15 +472,18 @@ typedef struct
//table 3-32
typedef c16_t nfapi_nr_txru_t;
typedef struct {
uint16_t dig_beam_weight_Re;
uint16_t dig_beam_weight_Im;
} nfapi_nr_txru_t;
typedef struct {
uint16_t beam_idx; //0~32767
uint16_t beam_idx; //0~65535
nfapi_nr_txru_t *txru_list;
} nfapi_nr_dig_beam_t;
typedef struct {
uint16_t num_dig_beams; //0~32767
uint16_t num_dig_beams; //0~65535
uint16_t num_txrus; //0~65535
nfapi_nr_dig_beam_t *dig_beam_list;
} nfapi_nr_dbt_pdu_t;
@@ -506,6 +515,7 @@ typedef struct {
} nfapi_nr_pm_tlv_ve_t;
typedef struct {
nfapi_uint8_tlv_t num_beams_period_vendor_ext;
nfapi_uint8_tlv_t analog_bf_vendor_ext;
} nfapi_nr_analog_beamforming_ve_t;
@@ -849,21 +859,6 @@ typedef struct {
} nfapi_nr_dl_dci_pdu_t;
// The maximum number of spatial streams to be mapped depends on TLV 0x16E and
// the number of streams could be same as number of layers or number of antenna
// ports or number of baseband ports. Hence we set this to be the maximum number
// of baseband ports
#define MAX_NUM_SPATIAL_STREAMS 16
typedef struct {
uint16_t dci_index;
uint16_t spatial_stream_index;
} nfapi_v4_dci_spatial_stream_index_t;
typedef struct {
uint16_t numSpatialStreams;
nfapi_v4_dci_spatial_stream_index_t dci_spatialStreamIndices[MAX_NUM_SPATIAL_STREAMS];
} nfapi_v4_pdcch_pdu_parameters_t;
typedef struct {
///Bandwidth part size [TS38.213 sec12]. Number of contiguous PRBs allocated to the BWP,Value: 1->275
@@ -896,32 +891,13 @@ typedef struct {
uint16_t numDlDci;
///DL DCI PDU
nfapi_nr_dl_dci_pdu_t dci_pdu[MAX_DCI_CORESET];
/// Spatial stream indexing for MU-MIMO
nfapi_v4_pdcch_pdu_parameters_t param_v4;
} nfapi_nr_dl_tti_pdcch_pdu_rel15_t;
} nfapi_nr_dl_tti_pdcch_pdu_rel15_t;
typedef struct {
uint8_t ldpcBaseGraph;
uint32_t tbSizeLbrmBytes;
}nfapi_v3_pdsch_maintenance_parameters_t;
typedef struct {
/// Number of spatial streams used in the index array
uint8_t numSpatialStreamIndices;
/// Spatial stream index array
uint16_t spatialStreamIndices[MAX_NUM_SPATIAL_STREAMS];
} nfapi_nr_spatial_stream_index_t;
#define MAX_NUM_CODEWORDS 2
typedef struct {
// MU-MIMO support in FAPIv4
/// Number of codewords with spatial stream indices
uint8_t numberCodewords;
/// Spatial stream indexing for codeworeds
nfapi_nr_spatial_stream_index_t spatialStreamsCw[MAX_NUM_CODEWORDS];
} nfapi_v4_pdsch_parameters_t;
typedef struct {
uint16_t pduBitmap;
uint16_t rnti;
@@ -1014,9 +990,8 @@ typedef struct {
uint32_t dlTbCrc;
nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3;
/// PDSCH parameters FAPI v4. used only for spatial stream indexing in MU-MIMO
nfapi_v4_pdsch_parameters_t param_v4;
} nfapi_nr_dl_tti_pdsch_pdu_rel15_t;
}nfapi_nr_dl_tti_pdsch_pdu_rel15_t;
//for pdsch_pdu:
/*
@@ -1100,15 +1075,9 @@ typedef struct
uint8_t power_control_offset; // Ratio of PDSCH EPRE to NZP CSI-RSEPRE [3GPP TS 38.214, sec 5.2.2.3.1], Value: 0->23 representing -8 to 15 dB in 1dB steps; 255: L1 is configured with ProfileSSS
uint8_t power_control_offset_ss; // Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE [3GPP TS 38.214, sec 5.2.2.3.1], Values: 0: -3dB; 1: 0dB; 2: 3dB; 3: 6dB; 255: L1 is configured with ProfileSSS
nfapi_nr_tx_precoding_and_beamforming_t precodingAndBeamforming;
/// Spatial stream indexing for MU-MIMO
struct nfapi_nr_csi_spatial_stream_index {
/// Number of spatial streams used in the index array
uint8_t numSpatialStreamIndices;
/// Spatial stream index array
uint8_t spatialStreamIndices[MAX_NUM_SPATIAL_STREAMS];
} param_v4;
} nfapi_nr_dl_tti_csi_rs_pdu_rel15_t;
typedef struct
{
uint32_t bch_payload;//BCH payload. The valid bits are indicated in the PARAM/CONFIG TLVs. If PARAM/CONFIG TLVs indicate MAC generates full bchPayload then the payload length is 31 bits with the 8 LSB bits being. Otherwise timing PBCH bits are generated by the PHY. And for bchPayload the 24 LSB are used.
@@ -1149,11 +1118,6 @@ typedef struct {
/// A value indicating the channel quality between the gNB and nrUE. Value: 0->255 dBM
uint8_t ssbRsrp;
nfapi_nr_tx_precoding_and_beamforming_t precoding_and_beamforming;
/// Spatial stream indexing
struct nfapi_v4_ssb_param {
uint8_t spatialStreamIndexPresent;
uint16_t spatialStreamIndex;
} param_v4;
} nfapi_nr_dl_tti_ssb_pdu_rel15_t;
typedef struct {
@@ -1321,7 +1285,7 @@ typedef struct
uint8_t prach_start_symbol;
uint16_t num_cs;
nfapi_nr_ul_beamforming_t beamforming;
nfapi_nr_spatial_stream_index_t param_v4;
} nfapi_nr_prach_pdu_t;
//for pusch_pdu:
@@ -1434,8 +1398,6 @@ typedef struct
//beamforming
nfapi_nr_ul_beamforming_t beamforming;
nfapi_v3_pdsch_maintenance_parameters_t maintenance_parms_v3;
// Spatial stream indexing for MU-MIMO
nfapi_nr_spatial_stream_index_t param_v4;
} nfapi_nr_pusch_pdu_t;
//for pucch_pdu:
@@ -1481,7 +1443,7 @@ typedef struct
uint16_t bit_len_csi_part2;
nfapi_nr_ul_beamforming_t beamforming;
nfapi_nr_spatial_stream_index_t param_v4;
} nfapi_nr_pucch_pdu_t;
typedef struct {

View File

@@ -207,8 +207,8 @@ static void fill_config_request_tlv_tdd_rand(nfapi_nr_config_request_scf_t *nfap
beam->txru_list = calloc(nb_tx , sizeof(*beam->txru_list));
for (int j = 0; j < nb_tx; j++) {
nfapi_nr_txru_t *txru = &beam->txru_list[j];
txru->r = rand16_range(1, 0xffff);
txru->i = rand16_range(1, 0xffff);
txru->dig_beam_weight_Re = rand16_range(1, 0xffff);
txru->dig_beam_weight_Im = rand16_range(1, 0xffff);
}
}
nfapi_resp->num_tlv++;
@@ -276,7 +276,13 @@ static void fill_config_request_tlv_tdd_rand(nfapi_nr_config_request_scf_t *nfap
nfapi_resp->num_tlv++;
/*
// TODO: Uncomment this block when ready to enable the pack of the following VE TLVs in nr_fapi_p5.c
// NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG
// NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG
FILL_TLV(nfapi_resp->analog_beamforming_ve.num_beams_period_vendor_ext,
NFAPI_NR_FAPI_NUM_BEAMS_PERIOD_VENDOR_EXTENSION_TAG,
rand8());
nfapi_resp->num_tlv++;
FILL_TLV(nfapi_resp->analog_beamforming_ve.analog_bf_vendor_ext, NFAPI_NR_FAPI_ANALOG_BF_VENDOR_EXTENSION_TAG, rand8());
nfapi_resp->num_tlv++;
*/

View File

@@ -1,20 +0,0 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef OPENAIRINTERFACE_NR_FAPI_COMMON_UTIL_TEST_H
#define OPENAIRINTERFACE_NR_FAPI_COMMON_UTIL_TEST_H
#include "nfapi/tests/nr_fapi_test.h"
static uint8_t get_num_spatial_streams(void)
{
const uint16_t max_logical_ant_ports = MAX_NUM_SPATIAL_STREAMS;
return rand8_range(0, max_logical_ant_ports);
}
static void fill_spatial_streams(const uint8_t num_s, uint16_t s[MAX_NUM_SPATIAL_STREAMS])
{
for (int n = 0; n < num_s; n++)
s[n] = rand16_range(0, num_s - 1);
}
#endif

View File

@@ -4,7 +4,6 @@
#include "dci_payload_utils.h"
#include "nr_fapi_p7.h"
#include "nr_fapi_p7_utils.h"
#include "nr_fapi_common_util_test.h"
static void fill_dl_tti_request_beamforming(nfapi_nr_tx_precoding_and_beamforming_t *precodingAndBeamforming)
{
@@ -49,11 +48,6 @@ static void fill_dl_tti_request_pdcch_pdu(nfapi_nr_dl_tti_pdcch_pdu_rel15_t *pdu
pdu->dci_pdu[dl_dci].PayloadSizeBits = rand16_range(0, DCI_PAYLOAD_BYTE_LEN * 8);
generate_payload(pdu->dci_pdu[dl_dci].PayloadSizeBits, pdu->dci_pdu[dl_dci].Payload);
}
pdu->param_v4.numSpatialStreams = get_num_spatial_streams();
for (int n = 0; n < pdu->param_v4.numSpatialStreams; n++) {
pdu->param_v4.dci_spatialStreamIndices[n].dci_index = rand16();
pdu->param_v4.dci_spatialStreamIndices[n].spatial_stream_index = rand16_range(0, pdu->param_v4.numSpatialStreams - 1);
}
}
static void fill_dl_tti_request_pdsch_pdu(nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdu)
@@ -115,12 +109,6 @@ static void fill_dl_tti_request_pdsch_pdu(nfapi_nr_dl_tti_pdsch_pdu_rel15_t *pdu
}
pdu->maintenance_parms_v3.ldpcBaseGraph = rand8();
pdu->maintenance_parms_v3.tbSizeLbrmBytes = rand32();
pdu->param_v4.numberCodewords = rand8_range(0, MAX_NUM_CODEWORDS);
for (int c = 0; c < pdu->param_v4.numberCodewords; c++) {
pdu->param_v4.spatialStreamsCw[c].numSpatialStreamIndices = get_num_spatial_streams();
fill_spatial_streams(pdu->param_v4.spatialStreamsCw[c].numSpatialStreamIndices,
pdu->param_v4.spatialStreamsCw[c].spatialStreamIndices);
}
}
static void fill_dl_tti_request_csi_rs_pdu(nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *pdu)
@@ -159,9 +147,6 @@ static void fill_dl_tti_request_csi_rs_pdu(nfapi_nr_dl_tti_csi_rs_pdu_rel15_t *p
pdu->power_control_offset = rand8_range(0, 23);
pdu->power_control_offset_ss = rand8_range(0, 3);
fill_dl_tti_request_beamforming(&pdu->precodingAndBeamforming);
pdu->param_v4.numSpatialStreamIndices = get_num_spatial_streams();
for (int n = 0; n < pdu->param_v4.numSpatialStreamIndices; n++)
pdu->param_v4.spatialStreamIndices[n] = rand8_range(0, pdu->param_v4.numSpatialStreamIndices - 1);
}
static void fill_dl_tti_request_ssb_pdu(nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu)
@@ -174,8 +159,6 @@ static void fill_dl_tti_request_ssb_pdu(nfapi_nr_dl_tti_ssb_pdu_rel15_t *pdu)
pdu->bchPayloadFlag = rand8_range(0, 2);
pdu->bchPayload = rand24();
fill_dl_tti_request_beamforming(&pdu->precoding_and_beamforming);
pdu->param_v4.spatialStreamIndexPresent = rand8(0, 1);
pdu->param_v4.spatialStreamIndex = rand16_range(0, MAX_NUM_SPATIAL_STREAMS);
}
static void fill_dl_tti_request(nfapi_nr_dl_tti_request_t *msg)

View File

@@ -4,7 +4,6 @@
#include "dci_payload_utils.h"
#include "nr_fapi_p7.h"
#include "nr_fapi_p7_utils.h"
#include "nr_fapi_common_util_test.h"
static void fill_ul_tti_request_beamforming(nfapi_nr_ul_beamforming_t *beamforming_pdu)
{
@@ -29,8 +28,6 @@ static void fill_ul_tti_request_prach_pdu(nfapi_nr_prach_pdu_t *pdu)
pdu->prach_start_symbol = rand8_range(0, 13);
pdu->num_cs = rand16_range(0, 419);
fill_ul_tti_request_beamforming(&pdu->beamforming);
pdu->param_v4.numSpatialStreamIndices = get_num_spatial_streams();
fill_spatial_streams(pdu->param_v4.numSpatialStreamIndices, pdu->param_v4.spatialStreamIndices);
}
static void fill_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pdu)
@@ -136,8 +133,6 @@ static void fill_ul_tti_request_pusch_pdu(nfapi_nr_pusch_pdu_t *pdu)
fill_ul_tti_request_beamforming(&pdu->beamforming);
pdu->maintenance_parms_v3.ldpcBaseGraph = rand8();
pdu->maintenance_parms_v3.tbSizeLbrmBytes = rand32();
pdu->param_v4.numSpatialStreamIndices = get_num_spatial_streams();
fill_spatial_streams(pdu->param_v4.numSpatialStreamIndices, pdu->param_v4.spatialStreamIndices);
}
static void fill_ul_tti_request_pucch_pdu(nfapi_nr_pucch_pdu_t *pdu)
@@ -252,8 +247,6 @@ static void fill_ul_tti_request_pucch_pdu(nfapi_nr_pucch_pdu_t *pdu)
}
fill_ul_tti_request_beamforming(&pdu->beamforming);
pdu->param_v4.numSpatialStreamIndices = get_num_spatial_streams();
fill_spatial_streams(pdu->param_v4.numSpatialStreamIndices, pdu->param_v4.spatialStreamIndices);
}
static void fill_ul_tti_request_srs_parameters(nfapi_v4_srs_parameters_t *params, const uint8_t num_symbols){

View File

@@ -5,7 +5,7 @@
#include "../nrLDPC_coding_interface.h"
#include "nrLDPC_coding_aal.h"
#include "PHY/sse_intrin.h"
#include "bits.h"
#include "reverse_bits.h"
#include <common/utils/LOG/log.h>
#include "common/config/config_paramdesc.h"
#include "common/config/config_userapi.h"

View File

@@ -3,7 +3,7 @@
*/
#include "PHY/CODING/nrPolar_tools/nr_polar_defs.h"
#include "bits.h"
#include "reverse_bits.h"
#include <assert.h>
#include <stdlib.h>
#include <stdint.h>

View File

@@ -90,7 +90,15 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB)
// shortcuts
NR_DL_FRAME_PARMS *const fp = &gNB->frame_parms;
nfapi_nr_config_request_scf_t *cfg = &gNB->gNB_config;
NR_gNB_COMMON *const common_vars = &gNB->common_vars;
NR_gNB_COMMON *const common_vars = &gNB->common_vars;
common_vars->analog_bf = cfg->analog_beamforming_ve.analog_bf_vendor_ext.value;
LOG_I(PHY, "L1 configured with%s analog beamforming\n", common_vars->analog_bf ? "" : "out");
if (common_vars->analog_bf) {
// True only if nrmac->beam_info.beam_mode == FAPI_ANALOG_BEAM, thus analog_beamforming=2
common_vars->num_beams_period = cfg->analog_beamforming_ve.num_beams_period_vendor_ext.value;
LOG_I(PHY, "Max number of concurrent beams: %d\n", common_vars->num_beams_period);
} else
common_vars->num_beams_period = 1;
int Ptx = cfg->carrier_config.num_tx_ant.value;
int Prx = cfg->carrier_config.num_rx_ant.value;
@@ -137,12 +145,25 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB)
/* Do NOT allocate per-antenna rxdataF: the gNB gets a pointer to the
* RU to copy/recover freq-domain memory from there */
common_vars->rxdataF = malloc16_clear(Prx * sizeof(*common_vars->rxdataF));
common_vars->rxdataF = (c16_t ***)malloc16(common_vars->num_beams_period * sizeof(c16_t**));
for (int i = 0; i < common_vars->num_beams_period; i++)
common_vars->rxdataF[i] = (c16_t **)malloc16(Prx * sizeof(c16_t*));
common_vars->tx_grid_info = calloc(1, Ptx * sizeof(*common_vars->tx_grid_info));
common_vars->txdataF = calloc(Ptx, sizeof(*common_vars->txdataF));
for (int j = 0; j < Ptx; j++)
common_vars->txdataF[j] = (c16_t*)malloc16_clear(fp->samples_per_slot_wCP * sizeof(c16_t));
if (cfg->analog_beamforming_ve.analog_bf_vendor_ext.value) {
common_vars->beam_id = (int **)malloc16(common_vars->num_beams_period * sizeof(int*));
for (int i = 0; i < common_vars->num_beams_period; i++) {
common_vars->beam_id[i] = (int*)malloc16(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int));
memset(common_vars->beam_id[i], -1, fp->symbols_per_slot * fp->slots_per_frame * sizeof(int));
}
}
common_vars->txdataF = (c16_t ***)malloc16(common_vars->num_beams_period * sizeof(c16_t**));
for (int i = 0; i < common_vars->num_beams_period; i++) {
common_vars->txdataF[i] = (c16_t**)malloc16_clear(Ptx * sizeof(c16_t*));
for (int j = 0; j < Ptx; j++)
common_vars->txdataF[i][j] = (c16_t*)malloc16_clear(fp->samples_per_slot_wCP * sizeof(c16_t));
}
common_vars->debugBuff = (int32_t*)malloc16_clear(fp->samples_per_frame*sizeof(int32_t)*100);
common_vars->debugBuff_sample_offset = 0;
// PRACH
init_nr_prach(gNB);
@@ -175,8 +196,8 @@ void phy_init_nr_gNB(PHY_VARS_gNB *gNB)
void phy_free_nr_gNB(PHY_VARS_gNB *gNB)
{
const int Prx = gNB->gNB_config.carrier_config.num_rx_ant.value;
const int Ptx = gNB->gNB_config.carrier_config.num_tx_ant.value;
const int Prx = gNB->gNB_config.carrier_config.num_rx_ant.value;
const int max_ul_mimo_layers = 4; // taken from phy_init_nr_gNB()
const int n_buf = Prx * max_ul_mimo_layers;
@@ -192,15 +213,24 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB)
destroy_DLSCH_struct(gNB);
NR_gNB_COMMON * common_vars = &gNB->common_vars;
for (int i = 0; i < Ptx; i++) {
free_and_zero(common_vars->txdataF[i]);
for (int j = 0; j < common_vars->num_beams_period; j++) {
if (common_vars->beam_id)
free_and_zero(common_vars->beam_id[j]);
for (int i = 0; i < Ptx; i++) {
free_and_zero(common_vars->txdataF[j][i]);
}
free_and_zero(common_vars->txdataF[j]);
}
free_and_zero(common_vars->txdataF);
free_and_zero(common_vars->tx_grid_info);
/* Do NOT free per-antenna txdataF/rxdataF: the gNB gets a pointer to the
* RU's txdataF/rxdataF, and the RU will free that */
for (int j = 0; j < common_vars->num_beams_period; j++)
free_and_zero(common_vars->rxdataF[j]);
free_and_zero(common_vars->txdataF);
free_and_zero(common_vars->rxdataF);
free_and_zero(common_vars->beam_id);
free_and_zero(common_vars->debugBuff);
for (int ULSCH_id = 0; ULSCH_id < gNB->max_nb_pusch; ULSCH_id++) {
NR_gNB_PUSCH *pusch_vars = &gNB->pusch_vars[ULSCH_id];
@@ -219,6 +249,7 @@ void phy_free_nr_gNB(PHY_VARS_gNB *gNB)
free(gNB->pusch_vars);
free_nrLDPC_coding_interface(&gNB->nrLDPC_coding_interface);
}
void nr_phy_config_request_sim(PHY_VARS_gNB *gNB,

View File

@@ -15,14 +15,31 @@ void nr_phy_init_RU(RU_t *ru)
LOG_D(PHY, "Initializing RU signal buffers (if_south %s) nb_tx %d, nb_rx %d\n", ru_if_types[ru->if_south], ru->nb_tx, ru->nb_rx);
ru->num_beams_period = 1;
if (ru->num_gNB > 0) {
nfapi_nr_config_request_scf_t *cfg = &ru->config;
ru->nb_log_antennas = 0;
for (int n = 0; n < ru->num_gNB; n++) {
if (cfg->carrier_config.num_tx_ant.value > ru->nb_log_antennas)
ru->nb_log_antennas = cfg->carrier_config.num_tx_ant.value;
}
nfapi_nr_analog_beamforming_ve_t *analog_config = &cfg->analog_beamforming_ve;
ru->num_beams_period = analog_config->analog_bf_vendor_ext.value ? analog_config->num_beams_period_vendor_ext.value : 1;
}
else ru->nb_log_antennas = ru->nb_tx;
// copy configuration from gNB[0] in to RU, assume that all gNB instances sharing RU use the same configuration
// (at least the parts that are needed by the RU, numerology and PRACH)
int nb_tx_streams = ru->nb_tx;
int nb_rx_streams = ru->nb_rx;
if ((nb_tx_streams > fp->nb_antennas_tx) || (nb_rx_streams > fp->nb_antennas_rx))
LOG_W(NR_PHY, "There could be unused baseband ports because of fewer logical ports.\n");
AssertFatal(ru->nb_log_antennas > 0 && ru->nb_log_antennas < 13, "ru->nb_log_antennas %d ! \n",ru->nb_log_antennas);
int nb_tx_streams = ru->nb_tx * ru->num_beams_period;
int nb_rx_streams = ru->nb_rx * ru->num_beams_period;
LOG_I(NR_PHY, "nb_tx_streams %d, nb_rx_streams %d, num_Beams_period %d\n", nb_tx_streams, nb_rx_streams, ru->num_beams_period);
ru->common.beam_id = malloc16_clear(ru->num_beams_period * sizeof(int*));
for(int i = 0; i < ru->num_beams_period; i++) {
ru->common.beam_id[i] = malloc16(fp->symbols_per_slot * fp->slots_per_frame * sizeof(int));
memset(ru->common.beam_id[i], -1, fp->symbols_per_slot * fp->slots_per_frame * sizeof(int));
}
if (ru->if_south <= REMOTE_IF5) { // this means REMOTE_IF5 or LOCAL_RF, so allocate memory for time-domain signals
// Time-domain signals
@@ -90,8 +107,8 @@ void nr_phy_init_RU(RU_t *ru)
void nr_phy_free_RU(RU_t *ru)
{
LOG_D(PHY, "Freeing RU signal buffers (if_south %s) nb_tx %d\n", ru_if_types[ru->if_south], ru->nb_tx);
int nb_tx_streams = ru->nb_tx;
int nb_rx_streams = ru->nb_rx;
int nb_tx_streams = ru->nb_tx * ru->num_beams_period;
int nb_rx_streams = ru->nb_rx * ru->num_beams_period;
if (ru->if_south <= REMOTE_IF5) { // this means REMOTE_IF5 or LOCAL_RF, so free memory for time-domain signals
// Hack: undo what is done at allocation
@@ -126,6 +143,9 @@ void nr_phy_free_RU(RU_t *ru)
free_and_zero(ru->common.rxdataF[i]);
free_and_zero(ru->common.rxdataF);
for(int i = 0; i < ru->num_beams_period; ++i)
free_and_zero(ru->common.beam_id[i]);
free_and_zero(ru->common.beam_id);
}
PHY_VARS_gNB *gNB0 = ru->gNB_list[0];

View File

@@ -685,6 +685,21 @@ c16_t nr_layer_precoder(int sz, c16_t datatx_F_precoding[][sz], const char *prec
((int16_t *)precodatatx_F)[1] = (int16_t)((((int16_t *)precodatatx_F)[1]*ONE_OVER_SQRT2_Q15)>>15);*/
}
c16_t nr_layer_precoder_cm(int n_layers,
int symSz,
c16_t datatx_F_precoding[n_layers][symSz],
int ap,
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS],
int offset)
{
c16_t precodatatx_F = {0};
for (int al = 0; al < n_layers; al++) {
c16_t prec_weight = weights[al][ap];
precodatatx_F = c16maddShift(datatx_F_precoding[al][offset], prec_weight, precodatatx_F, 15);
}
return precodatatx_F;
}
#if defined(__AVX512F__) && defined(__AVX512BW__)
static inline __attribute__((always_inline)) __m512i cmac0_prec512(__m512i x, __m512i w_c, __m512i w_s) {
@@ -788,165 +803,278 @@ static inline __attribute__((always_inline)) __m128i cmac_prec128(__m128i y, __m
}
#endif
#define load_consts(Type, Instruct) \
const Type w_c = Instruct(c16toI32(c16conj(weight))); \
const Type w_s = Instruct(c16toI32(c16swap(weight))); \
const Type *in_simd = (Type *)(in + (out - beginning));
#define load_consts(Type, Instruct, Rank) \
const Type w_c##Rank = Instruct(c16toI32(c16conj(weights[Rank][ant]))); \
const Type w_s##Rank = Instruct(c16toI32(c16swap(weights[Rank][ant]))); \
const Type *in##Rank = (Type *)(txdataF_res_mapped[Rank] + sc_offset + (out-beginning));
void nr_beamformer_simd(const c16_t *in, const c16_t weight, const int re_cnt, c16_t *out)
void nr_layer_precoder_simd(const int n_layers,
const int symSz,
const c16_t txdataF_res_mapped[n_layers][symSz],
const int ant,
c16_t weights[NR_MAX_NB_LAYERS][NR_MAX_CSI_PORTS],
const int sc_offset,
const int re_cnt,
c16_t *txdataF_precoded)
{
// For x86, use 256 SIMD for every 8 RE and 128 SIMD for last 4 RE
// For aarch64, use 128 SIMD for every 4 RE
AssertFatal(n_layers > 0 && n_layers <= 4, "Shouldn't get here, n_layers %d\n", n_layers);
// 512/256 SIMD: Do 16/8 RE in one iteration, 3 iterations for 2 RB
c16_t *beginning = out;
c16_t *beginning = txdataF_precoded + sc_offset;
c16_t *out=beginning;
#if defined(__AVX512F__) && defined(__AVX512BW__)
{
c16_t *end = out + (re_cnt & ~15);
load_consts(__m512i, _mm512_set1_epi32);
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
const __m512i x = _mm512_loadu_si512(in_simd++);
__m512i y = _mm512_loadu_si512(out);
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
y = cmac_prec512(y, x, w_c, w_s);
_mm512_storeu_si512(out, y);
load_consts(__m512i, _mm512_set1_epi32, 0);
if (n_layers == 1) {
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
const __m512i x = _mm512_loadu_si512(in0++);
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
__m512i y = cmac0_prec512(x, w_c0, w_s0);
_mm512_storeu_si512(out, y);
}
} else if (n_layers == 2) {
load_consts(__m512i, _mm512_set1_epi32, 1);
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
const __m512i x = _mm512_loadu_si512(in0++);
const __m512i x1 = _mm512_loadu_si512(in1++);
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
__m512i y = cmac0_prec512(x, w_c0, w_s0);
y = cmac_prec512(y, x1, w_c1, w_s1);
_mm512_storeu_si512(out, y);
}
} else if (n_layers == 3) {
load_consts(__m512i, _mm512_set1_epi32, 1);
load_consts(__m512i, _mm512_set1_epi32, 2);
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
const __m512i x = _mm512_loadu_si512(in0++);
const __m512i x1 = _mm512_loadu_si512(in1++);
const __m512i x2 = _mm512_loadu_si512(in2++);
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
__m512i y = cmac0_prec512(x, w_c0, w_s0);
y = cmac_prec512(y, x1, w_c1, w_s1);
y = cmac_prec512(y, x2, w_c2, w_s2);
_mm512_storeu_si512(out, y);
}
} else if (n_layers == 4) {
load_consts(__m512i, _mm512_set1_epi32, 1);
load_consts(__m512i, _mm512_set1_epi32, 2);
load_consts(__m512i, _mm512_set1_epi32, 3);
for (; out < end; out += sizeof(__m512i) / sizeof(*out)) {
const __m512i x = _mm512_loadu_si512(in0++);
const __m512i x1 = _mm512_loadu_si512(in1++);
const __m512i x2 = _mm512_loadu_si512(in2++);
const __m512i x3 = _mm512_loadu_si512(in3++);
// Matrix multiplication for 4 elements of the result (sizeof(simde__m256i) / sizeof(*prec_matrix) = 8)
__m512i y = cmac0_prec512(x, w_c0, w_s0);
y = cmac_prec512(y, x1, w_c1, w_s1);
y = cmac_prec512(y, x2, w_c2, w_s2);
y = cmac_prec512(y, x3, w_c3, w_s3);
_mm512_storeu_si512(out, y);
}
}
}
#endif
#ifdef __AVX2__
{
c16_t *end = beginning + (re_cnt & ~7);
load_consts(simde__m256i, simde_mm256_set1_epi32);
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
const simde__m256i x = simde_mm256_loadu_si256(in_simd++);
simde__m256i y = simde_mm256_loadu_si256(out);
// Accumulate the product
y = cmac_prec256(y, x, w_c, w_s);
// Store the result to txdataF
simde_mm256_storeu_si256(out, y);
load_consts(simde__m256i, simde_mm256_set1_epi32, 0);
if (n_layers == 1) {
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
// Accumulate the product
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
// Store the result to txdataF
simde_mm256_storeu_si256(out, y);
}
} else if (n_layers == 2) {
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
// Accumulate the product
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
y = cmac_prec256(y, x1, w_c1, w_s1);
// Store the result to txdataF
simde_mm256_storeu_si256(out, y);
}
} else if (n_layers == 3) {
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
load_consts(simde__m256i, simde_mm256_set1_epi32, 2);
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
const simde__m256i x2 = simde_mm256_loadu_si256(in2++);
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
y = cmac_prec256(y, x1, w_c1, w_s1);
y = cmac_prec256(y, x2, w_c2, w_s2);
// Store the result to txdataF
simde_mm256_storeu_si256(out, y);
}
} else if (n_layers == 4) {
load_consts(simde__m256i, simde_mm256_set1_epi32, 1);
load_consts(simde__m256i, simde_mm256_set1_epi32, 2);
load_consts(simde__m256i, simde_mm256_set1_epi32, 3);
for (; out < end; out += sizeof(simde__m256i) / sizeof(*out)) {
const simde__m256i x0 = simde_mm256_loadu_si256(in0++);
const simde__m256i x1 = simde_mm256_loadu_si256(in1++);
const simde__m256i x2 = simde_mm256_loadu_si256(in2++);
const simde__m256i x3 = simde_mm256_loadu_si256(in3++);
simde__m256i y = cmac0_prec256(x0, w_c0, w_s0);
y = cmac_prec256(y, x1, w_c1, w_s1);
y = cmac_prec256(y, x2, w_c2, w_s2);
y = cmac_prec256(y, x3, w_c3, w_s3);
// Store the result to txdataF
simde_mm256_storeu_si256(out, y);
}
}
}
#endif
c16_t *end = beginning + (re_cnt & ~3);
#ifdef __aarch64__
load_consts(int16x8_t, vdupq_n_s16);
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
const int16x8_t x = vld1q_s16((const int16_t *)in_simd++);
int16x8_t y = vld1q_s16((const int16_t *)out);
// Accumulate the product
y = cmac_prec128(y, x, w_c, w_s);
// Store the result to txdataF
*(int16x8_t *)out = y;
}
#else
#ifdef DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // Get result with trivial solution, TODO: To be removed
// 128 SIMD: Do 4 RE in one iteration, 3 iterations for 1 RB
load_consts(simde__m128i, simde_mm_set1_epi32);
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
const simde__m128i x = simde_mm_loadu_si128(in_simd++);
simde__m128i y = simde_mm_loadu_si128(out);
// Accumulate the product
y = cmac_prec128(y, x, w_c, w_s);
// Store the result to txdataF
simde_mm_storeu_si128(out, y);
}
end += (re_cnt % 4);
c16_t *inc16 = (c16_t *)in_simd;
for (; out < end; out++) {
*out = c16maddShift(*inc16++, weight, *out, 15);
c16_t y_triv[4];
for (int i = 0; i < 4; i++)
y_triv[i] = nr_layer_precoder_cm(n_layers, symSz, txdataF_res_mapped, ant, pmi_pdu, sc + i);
memcpy(out, y_triv, sizeof(y_triv));
}
#endif
}
void nr_tx_precoder_and_beamformer(const c16_t *in,
const uint16_t layer_idx,
c16_t **out,
const int out_offset,
const uint8_t nb_antennas_tx,
const uint16_t spatial_stream_index[MAX_NUM_SPATIAL_STREAMS],
const nfapi_nr_tx_precoding_and_beamforming_t *pb,
const nfapi_nr_pm_pdu_t *pm,
const nfapi_nr_dbt_pdu_t *dbt,
const uint16_t num_rb)
{
uint16_t rb = 0;
uint32_t subCarrier = 0;
while (rb < num_rb) {
// get pmi info
const int pmi = (pb->prg_size > 0) ? (pb->prgs_list[(int)rb / pb->prg_size].pm_idx) : 0;
const int pmi2 = (rb < (num_rb - 1) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 1) / pb->prg_size].pm_idx) : -1;
const int pmi3 = (rb < (num_rb - 2) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 2) / pb->prg_size].pm_idx) : -1;
const int pmi4 = (rb < (num_rb - 3) && pb->prg_size > 0) ? (pb->prgs_list[(int)(rb + 3) / pb->prg_size].pm_idx) : -1;
// If pmi of next RB and pmi of current RB are the same, we do 2 RB in a row
// if pmi differs, or current rb is the end (num_rb - 1), than we do 1 RB in a row
int rb_step0 = pmi == pmi2 ? 2 : 1;
const int rb_step = rb_step0 == 2 && pmi3 == pmi && pmi4 == pmi ? 4 : rb_step0;
const int re_cnt = NR_NB_SC_PER_RB * rb_step;
// get dbf info
const bool do_dbf =
(pb->dig_bf_interfaces > 0) && !IS_BIT_SET(pb->prgs_list[(int)rb / pb->prg_size].dig_bf_interface_list[0].beam_idx, 15);
if (pmi == 0 && !do_dbf) { // no precoding and dbf. layer are spatial streams
memcpy(out[spatial_stream_index[layer_idx]] + out_offset + subCarrier, in + subCarrier, re_cnt * sizeof(**out));
} else if (pmi > 0 && !do_dbf) { // only precoding. logical ports are spatial streams
AssertFatal(nb_antennas_tx > 1, "No precoding can be done with a single antenna port\n");
// get the precoding matrix weights:
const nfapi_nr_pm_pdu_t *pmi_pdu = &pm[pmi - 1]; // pmi 0 is identity matrix
const uint16_t num_log_ports = pmi_pdu->num_ant_ports;
for (int ss = 0; ss < num_log_ports; ss++) {
const uint16_t log_port = spatial_stream_index[ss];
AssertFatal(pmi == pmi_pdu->pm_idx, "PMI %d doesn't match to the one in precoding matrix %d\n", pmi, pmi_pdu->pm_idx);
const c16_t w = pmi_pdu->weights[layer_idx][ss];
nr_beamformer_simd(in + subCarrier, w, re_cnt, out[log_port] + out_offset + subCarrier);
}
} else if (pmi == 0 && do_dbf) { // only dbf. bb ports are spatial streams
AssertFatal(nb_antennas_tx > 1, "No dbf can be done with a single antenna port\n");
const uint16_t num_bb_ports = dbt->num_txrus;
for (int bb_port = 0; bb_port < num_bb_ports; bb_port++) {
// SSI is not used when DBF is done
const uint16_t beam_id = pb->prgs_list[(int)rb / pb->prg_size].dig_bf_interface_list[0].beam_idx & 0x7fff;
AssertFatal(beam_id == dbt->dig_beam_list[beam_id].beam_idx,
"DBF Beam ID %d doesn't match one in DBT %d\n",
beam_id,
dbt->dig_beam_list[beam_id].beam_idx);
AssertFatal(bb_port < nb_antennas_tx,
"Spatial stream index %d exceeds number of antenna ports %d\n",
bb_port,
nb_antennas_tx);
AssertFatal(pb->dig_bf_interfaces <= dbt->num_txrus, "Number of logical ports exceeded DBT matrix size\n");
const c16_t w = dbt->dig_beam_list[beam_id].txru_list[bb_port];
nr_beamformer_simd(in + subCarrier, w, re_cnt, out[bb_port] + out_offset + subCarrier);
}
} else { // precoding and dbf
AssertFatal(nb_antennas_tx > 1, "No precoding/dbf can be done with a single antenna port\n");
// get the precoding matrix weights:
const nfapi_nr_pm_pdu_t *pmi_pdu = &pm[pmi - 1]; // pmi 0 is identity matrix
const uint16_t num_bb_ports = dbt->num_txrus;
for (int bb_port = 0; bb_port < num_bb_ports; bb_port++) {
// SSI is not used when DBF is done
const uint16_t num_log_ports = pb->dig_bf_interfaces;
const uint16_t beam_id = pb->prgs_list[(int)rb / pb->prg_size].dig_bf_interface_list[0].beam_idx & 0x7fff;
AssertFatal(pmi == pmi_pdu->pm_idx, "PMI %d doesn't match to the one in precoding matrix %d\n", pmi, pmi_pdu->pm_idx);
AssertFatal(beam_id == dbt->dig_beam_list[beam_id].beam_idx,
"DBF Beam ID %d doesn't match one in DBT %d\n",
beam_id,
dbt->dig_beam_list[beam_id].beam_idx);
AssertFatal(bb_port < nb_antennas_tx,
"Spatial stream index %d exceeds number of antenna ports %d\n",
bb_port,
nb_antennas_tx);
AssertFatal(pb->dig_bf_interfaces <= dbt->num_txrus, "Number of logical ports exceeded DBT matrix size\n");
// Composite weight of precoding and dbf
c16_t w = {0};
for (int ll = 0; ll < num_log_ports; ll++)
w = c16maddShift(pmi_pdu->weights[layer_idx][ll], dbt->dig_beam_list[beam_id].txru_list[bb_port], w, 15);
nr_beamformer_simd(in + subCarrier, w, re_cnt, out[bb_port] + out_offset + subCarrier);
}
#ifdef __aarch64__
load_consts(int16x8_t, vdupq_n_s16, 0);
if (n_layers == 1) {
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
// Store the result to txdataF
*(int16x8_t *)out = y;
}
subCarrier += re_cnt;
rb += rb_step;
} // RB loop: while(rb < rel15->rbSize)
}
if (n_layers == 2) {
load_consts(int16x8_t, vdupq_n_s16, 1);
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
y = cmac_prec128(y, x1, w_c1, w_s1);
// Store the result to txdataF
*(int16x8_t *)out = y;
}
}
if (n_layers == 3) {
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
;
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
// Store the result to txdataF
*(int16x8_t *)out = y;
}
}
if (n_layers == 4) {
load_consts(int16x8_t, vdupq_n_s16, 1);
load_consts(int16x8_t, vdupq_n_s16, 2);
load_consts(int16x8_t, vdupq_n_s16, 3);
for (; out < end; out += sizeof(int16x8_t) / sizeof(*out)) {
const int16x8_t x0 = vld1q_s16((const int16_t *)in0++);
const int16x8_t x1 = vld1q_s16((const int16_t *)in1++);
const int16x8_t x2 = vld1q_s16((const int16_t *)in2++);
const int16x8_t x3 = vld1q_s16((const int16_t *)in3++);
// Accumulate the product
int16x8_t y = cmac0_prec128(x0, w_c0, w_s0);
;
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
y = cmac_prec128(y, x3, w_c3, w_s3);
// Store the result to txdataF
*(int16x8_t *)out = y;
}
}
#else
load_consts(simde__m128i, simde_mm_set1_epi32, 0);
if (n_layers == 1) {
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
// Accumulate the product
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
// Store the result to txdataF
simde_mm_storeu_si128(out, y);
}
} else if (n_layers == 2) {
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
// Accumulate the product
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
y = cmac_prec128(y, x1, w_c1, w_s1);
// Store the result to txdataF
simde_mm_storeu_si128(out, y);
}
} else if (n_layers == 3) {
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
load_consts(simde__m128i, simde_mm_set1_epi32, 2);
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
const simde__m128i x2 = simde_mm_loadu_si128(in2++);
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
// Store the result to txdataF
simde_mm_storeu_si128(out, y);
}
} else if (n_layers == 4) {
load_consts(simde__m128i, simde_mm_set1_epi32, 1);
load_consts(simde__m128i, simde_mm_set1_epi32, 2);
load_consts(simde__m128i, simde_mm_set1_epi32, 3);
for (; out < end; out += sizeof(simde__m128i) / sizeof(*out)) {
const simde__m128i x0 = simde_mm_loadu_si128(in0++);
const simde__m128i x1 = simde_mm_loadu_si128(in1++);
const simde__m128i x2 = simde_mm_loadu_si128(in2++);
const simde__m128i x3 = simde_mm_loadu_si128(in3++);
simde__m128i y = cmac0_prec128(x0, w_c0, w_s0);
y = cmac_prec128(y, x1, w_c1, w_s1);
y = cmac_prec128(y, x2, w_c2, w_s2);
y = cmac_prec128(y, x3, w_c3, w_s3);
// Store the result to txdataF
simde_mm_storeu_si128(out, y);
}
}
#endif
#ifdef DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // Print simd and trivial result, TODO: To be removed
c16_t *y_simd = (c16_t *)&y;
printf("debug_to_be_removed re_cnt=%d, sc=%u, y_simd=(%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d)\n",
re_cnt,
sc,
y_simd[0].r,
y_simd[0].i,
y_simd[1].r,
y_simd[1].i,
y_simd[2].r,
y_simd[2].i,
y_simd[3].r,
y_simd[3].i);
printf("debug_to_be_removed re_cnt=%d, sc=%u, y_triv=(%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d), (%+4d,%+4d)\n",
re_cnt,
sc,
y_triv[0].r,
y_triv[0].i,
y_triv[1].r,
y_triv[1].i,
y_triv[2].r,
y_triv[2].i,
y_triv[3].r,
y_triv[3].i);
#endif
}

Some files were not shown because too many files have changed in this diff Show More