Compare commits

...

200 Commits

Author SHA1 Message Date
Jaroslava Fiedlerova
d191447a01 Merge branch 'integration_2026_w19' into 'develop'
Integration `2026.w19`

* ci: post MR validation comments only on warnings or errors in pre-ci-check
* ci: skip merge-commit validation for integration branches
* !4088 fixing wrong logic for SIMO and MISO AWGN channels
* !4052 Add OAI - WNC gNB config file
* !4091 Fix build with unit tests, build all in CI
* !4089 Fixing broken nr-cu-nrppa-test simulator and nr-ue-nas-simualtor
* !4099 Allow control signal level at input of OFDM modulator
* !4092 Remove more unused parameters
* !4102 nr-cuup-load-test: avoid unaligned memory access
* !4077 Update microamp FR2 doc for firmware 0.1.174
* !3960 fix: issue 1054 - Config: Creating New Array members via command line arguments
* !3810 UE symbol based PBCH, PSBCH receiver
* !4103 Update CN5G images to stable release `v2.2.1`
* !3814 Multi-QoS Handling and PDU Session Modify
* !4064 FAPI: fix UCI payload byte length for bit_len multiples of 8
* !4078 [M-plane] Improvements, fixes + Add CI M-plane pipeline

Closes #1054, #1075, and #541

See merge request oai/openairinterface5g!4095
2026-05-07 19:07:13 +00:00
Jaroslava Fiedlerova
328c4bc5fa Merge remote-tracking branch 'origin/ci-test-mplane' into integration_2026_w19 (!4078)
[M-plane] Improvements, fixes + Add CI M-plane pipeline

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-07 15:28:57 +02:00
Jaroslava Fiedlerova
58942c31a5 Merge remote-tracking branch 'origin/cubb_rusim_uci_unpack' into integration_2026_w19 (!4064)
FAPI: fix UCI payload byte length for bit_len multiples of 8

Problem
When testing with cuBB as the FAPI PNF (L1) and ruSIM, the VNF (OAI L2)
was consistently failing to unpack UCI.indication messages containing
PUCCH format 2/3/4 PDUs with CSI-1 reports:

  vnf_handle_nr_uci_indication: Failed to unpack message
  pullarray8: pullarray8 no space in buffer

Root cause
All variable-length payload fields in the UCI pack/unpack functions
(HARQ, SR, CSI-1, CSI-2) computed their byte length as:
  const uint16_t csi_len = bit_len / 8 + 1;
This formula seems wrong for any bit_len that is an exact multiple of 8.
For example, bit_len=8 gives csi_len=2, but 8 bits fit in exactly 1
byte. The correct ceiling division is (bit_len + 7) / 8.
cuBB packs CSI-1 payload bytes using the correct formula, so for an
8-bit CSI codebook it sends 1 byte. OAI's unpacker computed csi_len=2
and called pullarray8 requesting 2 bytes from a buffer with only 1
remaining, triggering the error.
The failure was also specific to PUCCH 2/3/4 (and would affect PUSCH
UCI) because PUCCH 0/1 carries no variable-length byte arrays — its HARQ
payload is packed as individual uint8 values per ACK bit, so pullarray8
is never called.

Diagnosis
Added NFAPI_TRACE_NOTE logging throughout the unpack chain (enabled via
NFAPI_TRACE_LEVEL=note) to trace PDU type, bitmap, computed lengths, and
buffer remaining bytes at each branch. The following sequence confirmed
the exact failure site:

  [N] unpack_nr_uci_indication: sfn=592 slot=9 num_ucis=1 buf_remaining=21
  [N] unpack_nr_uci_indication: unpacking UCI[0/1] buf_remaining=21
  [N] unpack_nr_uci_indication_body: pdu_type=2 pdu_size=21 buf_remaining=17
  [N] unpack_nr_uci_pucch_2_3_4: PUCCH-2/3/4 rnti=0x5c2d pucch_format=0 pduBitmap=0x04 buf_remaining=4
  [N] unpack_nr_uci_pucch_2_3_4: PUCCH-2/3/4 CSI-1 csi_part1_bit_len=8 csi_len=2 buf_remaining=1
  [N] unpack_nr_uci_pucch_2_3_4: PUCCH-2/3/4 CSI-1 about to pullarray8 csi_len=2 buf_remaining=1
  [E] pullarray8: pullarray8 no space in buffer

This was also cross-checked against a pcap capture of the nvipc
interface, which confirmed cuBB sends PDUSize=21 with 1 byte for the
8-bit CSI-1 payload — consistent with (8+7)/8 = 1.

Tentative Fix
Replace bit_len / 8 + 1 with (bit_len + 7) / 8 at all 14 affected sites
across both pack and unpack functions for PUSCH, PUCCH 0/1 (SR/HARQ),
and PUCCH 2/3/4 (SR, HARQ, CSI-1, CSI-2) in nr_fapi_p7.c.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Rúben Soares Silva <rsilva@allbesmart.pt>
2026-05-07 15:03:21 +02:00
Jaroslava Fiedlerova
3b8f5c7191 Merge remote-tracking branch 'origin/rrc-qos-handling' into integration_2026_w19 (!3810)
Multi-QoS Handling and PDU Session Modify

This MR implements comprehensive QoS flows handling and PDU Session
Modify procedures in the RRC layer, enabling full support for multiple
QoS flows per DRB and dynamic QoS management per 3GPP specs. Key
changes:

1. Multi-QoS Flows Support

- Multiple QoS flows per DRB: Support for multiple QoS flows mapped to a
  single DRB
- Intelligent QoS-to-DRB mapping: Implements resource-type-aware
  multiplexing based on 3GPP TS 23.501 Table 5.7.4-1
  - DC-GBR flows (5QI 82-90): Dedicated DRB, max 1 flow per DRB
  - GBR flows (5QI 1-4, 65-67, 71-76): Max 2 flows per DRB
  - Non-GBR flows (5QI 5-11, 69-70, 79-80): Max 5 flows per DRB
  - Aggregate cap: Maximum 5 flows per DRB
- 5QI validation: Early validation of standardized 5QI values (1-9,
  65-90) during QoS flow setup/modify

2. PDU Session Modify Procedures

- Complete E1AP/RRC integration: Full Bearer Context Modification
  support with DRB lifecycle management
  - DRB To Setup/Modify/Remove lists: Complete support for all DRB
    operations in Bearer Context Modification
- QoS flow operations: Support for QoS flow add, modify, and release
  operations
  - QoS flow mapping: Proper handling of QoS flow modifications in
    DRB To Modify List
- Automatic DRB management: DRB setup, modification, and removal
  based on QoS flow changes. Reuse existing DRBs if the incoming QoS
  flow is compatible.
- Delayed transactions: PDU Session Modify added to delayed
  transactions list for proper sequencing

3. GTP-U Tunnel Refactoring

- Architecture alignment:
  - N3 tunnels: 1 per PDU session with QFI marking, container for
    multiple bearers, supporting multiple QoS flows
  - F1-U tunnels: 1 per DRB without QFI marking
- SDAP ownership of QoS: move QoS management to be fully owned by
  SDAP

4. QoS Enhancements

- Dynamic5QI support: Full support for Dynamic5QI with packet delay
  budget and packet error rate
- GBR QoS flows: Support for Guaranteed Bit Rate QoS flows with
  GFBR/MFBR parameters
- DRB QoS aggregation: DRB-level QoS computed from all mapped QoS
  flows using ARP priority (not 5QI priority)
- QoS priority level refactoring: Proper type definitions per 3GPP
  TS 23.501 (QoS Priority Level: 1-127, ARP Priority Level: 1-15)

Technical Changes

RRC Layer

- Refactored `nr_rrc_add_bearers()` to support intelligent QoS-to-DRB
  mapping
- Added `nr_rrc_update_qos()` for QoS flow add/modify with automatic DRB
  assignment
- Implemented `nr_rrc_update_pdusession()` for QoS flow release and DRB
  cleanup
- Consolidated F1 UE Context Modification Request handling
- Simplified PDU status tracking by removing intermediate states
- Added QoS flow and DRB removal utilities
- Updated RRC bearers tests with comprehensive multi-QoS testing
- Adapted `nr-cuup-load-test.c` to new GTP design

NGAP Layer

- Extended PDU Session Resource Modify Request Transfer with QoS
  add/modify/release lists
- Added proper type definitions (`pdusession_mod_req_transfer_t`,
  `qos_flow_to_release_t`)
- Fixed NGAP PDU Session Modify transfer encoding

GTP-U Layer

- Refactored tunnel creation API: scalar fields instead of arrays
- Split tunnel creation into `n3_gtpu_create()` and
  `f1_drb_gtpu_create()`
- Added bearer-to-QFI mapping structure (`gtpv1u_rb_t`)
- Implemented QFI de-duplication and one-to-one QFI-to-bearer mapping
- Reduced inter-dependencies between LTE and NR

SDAP Layer

- Fixed default DRB tracking: Only one default DRB per SDAP entity per
  PDU session (TS 37.324)
- Set default DRB to first DRB added when creating bearers

F1AP Layer

- Added GBR QoS Flow Information IE handling in CU
- Implemented DRB QoS aggregation from multiple QoS flows
- Extended F1AP test cases to support multiple QoS flows

Code Quality Improvements

- Improved error handling throughout the stack
- Enhanced logging and debugging capabilities
- Added comprehensive unit tests for multi-QoS scenarios
- Improved type safety and validation

Testing

The code was validated with Open5gs v2.7.6, OAI CN5G and COTS UE.

How to reproduce with Open5gs:

1. Start Open5gs
2. From the Open5gs web UI: add multiple QFIs to the same DNN.
3. Restart Open5gs
4. Run gNB and UE

The user will see the following:

1. QoS flows arrive via NGAP PDU Session Setup/Modify requests, each
   with a QFI and 5QI.
2. The gNB assigns each flow to a DRB, creating a new DRB or reusing an
   existing one based on 5QI compatibility (compatible flows, i.e. same
   5QI characteristics, share a DRB)
3. At the SDAP layer, packets are tagged with QFI headers; at the GTP-U
   layer, uplink packets include QFI in the PDU Session Container for
   core network mapping.

Documentation

- Added comprehensive QoS flows handling documentation with 3GPP
  standards references
- Updated PDU Session Modify sequence diagrams

Note

* This implementation aligns with:
  - 3GPP TS 23.501 (QoS framework)
  - 3GPP TS 38.413 (NGAP)
  - 3GPP TS 37.324 (SDAP)
  - 3GPP TS 38.463 (F1AP)
  - 3GPP TS 29.281 (GTP-U)
  - 3GPP TS 38.331 (RRC)
* Multi-QoS and PDU Session Modify are both added to this MR since QoS
  flows update is triggered by PDU Session Modify in some scenarios
  (e.g. with Open5gs, used for validation).
* Core functional changes introduced by commits with prefix "QoS
  Handling" or "PDU Session Modify"
* This MR replaces the old !2703.
* This MR adopts relevant CU changes from !2836, namely:
  * Adds new shared QoS types
  * Add standardized 5QI table and helper
  * Dynamic5QI support + validation
  * Computation of DRB QoS (aggregate QoS) and priority/GBR
  * F1AP DRB-level QoS
* Closes #541 (5QI validation)
* Closes #1075 (Optional NAS-PDU in PDU Session Setup Request)

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-07 14:52:10 +02:00
Jaroslava Fiedlerova
c42928773b CI: Add RAN-SA-FHI72-MPLANE-CN5G into CI doc
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:05:31 +02:00
Jaroslava Fiedlerova
439d0d9d8d CI: Add RAN-SA-FHI72-MPLANE-CN5G into parent CI pipeline
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:05:31 +02:00
Teodora Vladić
695cdb4b38 Update RU FW versions
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-07 14:05:31 +02:00
Teodora Vladić
9050b3eb3c Update Benetel FW to the latest stable
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-07 14:05:31 +02:00
Jaroslava Fiedlerova
7827ac38b0 CI: Test with AmariUE in RAN-SA-FHI72-MPLANE-CN5G pipeline
- new configuration for PLMN 00105, 40MHz and 100 MHz added on AmariUE
- helm charts for OC CN with PLMN 00105 added to cacofonix, new CN added
  to ci_infra.yaml, IP address range of the new CN: 172.21.6.116-118
- test with 1 AmariUE (to be extended in future MR), with SISO

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:01:48 +02:00
Teodora Vladić
4ab102c680 [M-plane] Add new Benetel FW supported and reference the new CI pipeline
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-07 14:00:09 +02:00
Jaroslava Fiedlerova
4f74b2c7eb CI: Increase service deployment timeout
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:00:09 +02:00
Jaroslava Fiedlerova
3959e80f4e CI: add configs for 4x4 40 MHz test with M-plane
Running container in privileged mode and host network mode is needed
for running fhi72 gNB with M-plane

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:00:09 +02:00
Jaroslava Fiedlerova
8d6e3e4387 CI: add configs for 2x2 100 MHz test with M-plane
Running container in privileged mode and host network mode is needed
for running fhi72 gNB with M-plane

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:00:09 +02:00
Jaroslava Fiedlerova
df21063668 docker: enable FHI72 mplane build
- Build and install libyang (v2.1.111) and libnetconf2 (v2.1.37) from source.
- Build ran-build-fhi72 image with M-plane enabled
- Copy YANG models required for M-plane operation into the container.
- Update gNB Dockerfile to work without M-plane

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 14:00:09 +02:00
Jaroslava Fiedlerova
73d2b9c7e2 Merge remote-tracking branch 'origin/update-cn5g' into integration_2026_w19 (!4103)
Update CN5G images to stable release `v2.2.1`

- Updated CN5G image tags to a stable release v2.2.1
- Upgrade MySQL image to 9.6
- Updated image oaisoftwarealliance/trf-gen-cn5g to latest tag
  supporting multi-architecture platforms

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-07 10:07:36 +02:00
Guido Casati
51c773f411 CI (n2-ho): ping ext-DN from NR-UE before ext-DN toward UE
After attach, run UL ping so N3 UL traffic hits the UPF
before the first DL ping. That triggers UL PDR classification
in the UPF and avoids DL GTP-U using the default QFI.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-07 10:02:02 +02:00
Guido Casati
4a325ccf09 RRC: add UE ID to QoS-related logs and fix LOG_W in Modify transaction
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-07 10:02:02 +02:00
Guido Casati
f02c84fcfc CI: enable PCF-based dual-DNN multi-QoS in 25PRB RFSim
Expand the 25PRB RFSim CI scenario to validate two PDU sessions with
PCF-driven QoS rules and deterministic per-flow traffic ports. Update
iperf execution to use `-B`/`-p` from test args.

Changes:
- Update `container_5g_rfsim_u0_25prb.xml` to add dual-session
  validation steps, routing setup, and multi-flow UDP iperf
  for UL/DL with explicit bind/port arguments.
- Change `cls_oaicitest.py` to parse bind/port from
  `iperf_args`, and return a clear error when the iperf client exits non-zero.
- Update `nrue.uicc.2pdu.conf` to configure
  session 2 on `openairinterface` with distinct `nssai_sd` values.
- Extend `5g_rfsimulator/mini_nonrf_config.yaml`
  with PCF endpoints, dual slices, dual DNN entries, and PCF policy paths,
  and disable local PCC rules in SMF.
- Update `5g_rfsimulator_u0_25prb/docker-compose.yaml`
  to add the `oai-pcf` service, mount policy directories, and add ext-dn
  route for the second UE subnet.
- Add PCF policy data files for this scenario, in :
  `policies/policy_decisions/policy_decision.yaml`
  `policies/pcc_rules/pcc_rules.yaml`
  `policies/qos_data/qos_data.yaml`

About the ci-scripts:

Co-authored-by: Robert Schmidt <robert.schmidt@openairinterface.org>

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-07 10:02:02 +02:00
Jaroslava Fiedlerova
b11cf166af Merge remote-tracking branch 'origin/ue-pbch-symbol-rx' into integration_2026_w19 (!3810)
UE symbol based PBCH, PSBCH receiver

This is a subset of !2895 with PBCH & PSBCH refactoring to process
symbol by symbol.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-07 09:38:57 +02:00
Jaroslava Fiedlerova
ea1376a5c1 Merge remote-tracking branch 'origin/issue-1054-fix' into integration_2026_w19 (!3960)
fix: issue 1054 - Config: Creating New Array members via command line arguments

Before this change, if you wanted to use --rfsimulator.[0].serveraddr
server on the command line but if you had no rfsimulator block in your
config file, it would just ignore it and print:

[CONFIG] unknown option: --rfsimulator.[0].serveraddr
[CONFIG] unknown option: server

This is because the config module only processes array members that
already exist in the config file. If the array is empty, the command
args are never checked.

What I changed is, in config_getlist() in config_userapi.c, after it
finishes processing existing array parameters, it checks whether a new
parameter is needed. If so, it matches with the list of parameters
first, then the highest index is found for that parameter using the
strtol,

"--rfsimulator.[0].serveraddr",
"--rfsimulator.[2].serveraddr",

What I changed is, in config_getlist() in config_userapi.c, after it
finishes processing existing array parameters, it checks whether a new
parameter is needed. If so, it matches with the list of parameters
first, then the highest index is found for that parameter using the
strtol,

"--rfsimulator.[0].serveraddr",
"--rfsimulator.[2].serveraddr",
"--rfsimulator.[4].serverport",
"--rfsimulator.[1].serveraddr",

It sets the index as 4, allocates 4 slots, memory is managed via
standard functions then it fills the gap of all the indices.

this closes #1054

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-07 09:36:58 +02:00
Jaroslava Fiedlerova
00d6cc034d Merge remote-tracking branch 'origin/microamp_update_doc' into integration_2026_w19 (!4077)
Update microamp FR2 doc for firmware 0.1.174

MR updates the documentation of the Microamp FR2 unit with firmware
0.1.174 or later.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Sagar Arora <sagar.arora@openairinterface.org>
2026-05-07 09:31:06 +02:00
karim
5eb2480122 Update microamp FR2 doc for firmware 0.1.174
Signed-off-by: karim <karim.boutiba@openairinterface.org>
2026-05-06 16:40:35 +02:00
Gabriele Gemmi
80088121d3 nfapi: fix UCI payload byte length for bit_len multiples of 8
bit_len / 8 + 1 overcounts by 1 when bit_len is an exact multiple of 8.
Replace with (bit_len + 7) / 8 across all pack/unpack sites for PUSCH
and PUCCH 2/3/4 SR/HARQ/CSI payloads, and in the utility functions
(eq, copy, size calculator, dump) and unit test fill functions.

Fixes UCI.indication unpack failure against cuBB when CSI-1 report
is 8 bits.

Signed-off-by: Gabriele Gemmi <g.gemmi@northeastern.edu>
2026-05-06 13:15:23 +00:00
Shubhika Garg
e95992927c chore: replace cn5g images with latest stable release v2.2.1
- upgrade MySQL image to 9.6
    - Update trf-gen-cn5g Docker images to latest tag

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-05-06 14:56:47 +02:00
Jaroslava Fiedlerova
04e9fcbef9 Merge remote-tracking branch 'origin/ubsan-load-test' into integration_2026_w19 (!4102)
nr-cuup-load-test: avoid unaligned memory access

Use memcpy() instead of unaligned pointer access

tests/nr-cuup/nr-cuup-load-test.c:431:16: runtime error: store to misaligned address 0x756d02eca1a2 for type 'uint32_t', which requires 4 byte alignment
tests/nr-cuup/nr-cuup-load-test.c:267:14: runtime error: load of misaligned address 0x756d095edcf6 for type 'uint32_t', which requires 4 byte alignment

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-06 12:16:19 +02:00
Jaroslava Fiedlerova
22bc4f690a Merge remote-tracking branch 'origin/moreunused' into integration_2026_w19 (!4092)
Remove more unused parameters

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-06 12:13:32 +02:00
Robert Schmidt
4103703f56 nr-cuup-load-test: avoid unaligned memory access
Use memcpy() instead of unaligned pointer access

    tests/nr-cuup/nr-cuup-load-test.c:431:16: runtime error: store to misaligned address 0x756d02eca1a2 for type 'uint32_t', which requires 4 byte alignment
    tests/nr-cuup/nr-cuup-load-test.c:267:14: runtime error: load of misaligned address 0x756d095edcf6 for type 'uint32_t', which requires 4 byte alignment

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-06 09:07:32 +02:00
Teodora Vladić
d843877123 [M-plane] FFT offset is a positive value in [Ts]
In the future, adapt to long PRACH format.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 16:50:32 +02:00
Teodora Vladić
3c4081489a [M-plane] Get the right interface name
A RU can have multiple interfaces but for the M-plane purposes,
we need the ethernet interface.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 16:50:32 +02:00
Teodora Vladić
188d186ef9 [M-plane] Extract the current running U-plane configuration
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 16:50:27 +02:00
Jaroslava Fiedlerova
1b1b5397c1 Merge remote-tracking branch 'origin/fix_test_simulators' into integration_2026_w19 (!4089)
Fix broken nr-cu-nrppa-test simulator and nr-ue-nas-simualtor

This MR fixes the simulators broken due to the changes from !3993 and
!4010. The changes were related to storing service network name in nas
and routing indicator in uicc

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-05 14:01:36 +02:00
Jaroslava Fiedlerova
1df35d7994 Merge remote-tracking branch 'origin/dlsim_tx_amp' into integration_2026_w19 (!4099)
Allow control signal level at input of OFDM modulator

This simple fix removes an error floor in nr_dlsim tests with 256QAM. It
adds -Q to nr_dlsim to control gNB TX amplitude (like tx_backoff_dB in
gNB RU section). With

./nr_dlsim -R273 -b273 -s30 -e27 -I10 -q1 -n100 -Q30

has 0 errors. The default in nr_dlsim remains -36 dBFs.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-05-05 14:00:28 +02:00
Jaroslava Fiedlerova
9e95495c36 Merge remote-tracking branch 'origin/fix-build-unit-test-all' into integration_2026_w19 (!4091)
Fix build with unit tests, build all in CI

- Fix a build error when ENABLE_TESTS=ON
- Build everything in unit test build

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-05 13:59:19 +02:00
Jaroslava Fiedlerova
3016ada216 Merge remote-tracking branch 'origin/oai-wnc' into integration_2026_w19 (!4052)
Add OAI - WNC gNB config file

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-05-05 13:58:07 +02:00
Reem Bahsoun
0a58095692 Add WNC RU configuration section to FHI 7.2 tutorial.
Signed-off-by: Reem Bahsoun <reem.bahsoun@openairinterface.org>
2026-05-05 12:08:16 +02:00
Teodora Vladić
8097b4a479 [M-plane] Pass the default operation for <edit-config> RPC
Possible options:
```bash
typedef enum {
    NC_RPC_EDIT_DFLTOP_UNKNOWN = 0, /**< unknown default operation */
    NC_RPC_EDIT_DFLTOP_MERGE,       /**< default operation merge */
    NC_RPC_EDIT_DFLTOP_REPLACE,     /**< default operation replace */
    NC_RPC_EDIT_DFLTOP_NONE         /**< default operation none */
} NC_RPC_EDIT_DFLTOP;
```

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 11:23:24 +02:00
Teodora
275afe7d16 [M-plane] Set variable PRACH offset for Benetel RU
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 11:23:24 +02:00
Teodora
89deb54384 [M-plane] Hack 2x2 mode to use every other antenna
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-05-05 11:23:19 +02:00
Reem Bahsoun
12d6f6eccd Add a OAI-WNC config file in targets.
Signed-off-by: Reem Bahsoun <reem.bahsoun@openairinterface.org>
2026-05-05 11:15:21 +02:00
Rakesh Mundlamuri
c9587a8876 Modify nr-cu-nrppa-test simulator to use UE data from config file
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-05 10:18:43 +05:30
Raymond Knopp
12ac3ace12 add -Q to nr_dlsim to control gNB TX amplitude (tx_backoff_dB). with -Q30
./nr_dlsim -R273 -b273 -s30 -e27 -I10 -q1 -n100 -Q30

has 0 errors.
2026-05-04 21:09:57 +02:00
Jaroslava Fiedlerova
534ec1755f Merge remote-tracking branch 'origin/hotfix_SIMO' into integration_2026_w19 (!4088)
fixing wrong logic for SIMO and MISO AWGN channels

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-05-04 16:50:25 +02:00
Shubhika Garg
084b02a38b ci: skip merge-commit validation for integration branches
- use regex to match integration_YYYY_wWW branches

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-05-04 16:49:20 +02:00
Shubhika Garg
95a9438d44 ci: post MR validation comments only on warnings or errors in pre-ci-check
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-05-04 16:49:12 +02:00
Guido Casati
5a1af1a7db doc: add QoS flows handling and PDU Session Modify documentation
Also, move PDU Session Release diagram to new PDU Session Management section in rrc-dev.md.

Changes:

PDU Session Modify:
- Update PDU session modification sequence diagram
- Add DRB-To-Remove/To-Modify/To-Setup list handling
- Document E1AP Bearer Context Modification flow
- Add RRC reconfiguration trigger after E1AP response

QoS Flows Handling:

- Complete overview with 3GPP standards references (TS 23.501, 37.324, 38.463, 29.281, 38.331)
- Comprehensive Mermaid sequence diagram covering control and data plane

Technical details:
- Multiple QoS flows per DRB supported
- QFI to DRB mapping at RRC and SDAP layer
- F1-U tunnels: 1 per DRB, no QFI marking
- N3 tunnels: 1 per PDU session, with QFI marking

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:28 +02:00
Guido Casati
5f0f68caf8 PDU Session Modify: list only add/modify QFIs in NGAP modify response
Issue:

With a commercial UE, NGAP traces showed the PDU Session Resource Modify
Response carrying a QoSFlowAddOrModifyResponseList that did not match the
preceding Modify Request: the gNB listed every QoS flow already stored on the
PDU session instead of only the flows present in QoS Flow Add or Modify Request
List for that modify.

e.g. QoSFlowAddOrModifyResponseList including QFI 1,2
QoSFlowAddOrModifyRequestList QFI 2
AMF ErrorIndication with semantic_error after modify

That breaks the intended semantics in TS 38.413, which
expects QoSFlowAddOrModifyResponseList to carry the QFIs from this
procedure’s QoS Flow Add or Modify Request List only.

Changes:

Add a per-flow ngap_pending flag set in nr_rrc_update_qos when a request
item is applied, clear it before each new modify, and build the response
from marked flows only.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:28 +02:00
Guido Casati
3ad1b5c701 PDU Session Modify: add PDU Session Modify to the delayed transactions list
* Extend delay_transaction() and rrc_delay_transaction() to support NGAP_PDUSESSION_MODIFY_REQ

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:28 +02:00
Guido Casati
74decfcf30 PDU Session Modify: complete E1AP/RRC integration with DRB lifecycle management
This commit extends PDU Session Modify handling with E1AP Bearer Context
Modification integration for QoS flow add/modify/release and DRB lifecycle
updates (setup, modification, removal).

E1AP Bearer Context Modification - DRB Setup Support:
- Extend CU-UP handler (e1_bearer_context_modif) to process DRBs to setup/to remove:
  create F1-U tunnels, create/release PDCP-SDAP resources, and update QoS flow mappings
- Extend CU-CP response handler to process DRBs to setup: save F1-U tunnel info,
  mark PDU sessions for RRC reconfiguration, and trigger F1 UE Context
  Modification Request with both setup and release DRBs
- Update rrc_gNB_process_e1_bearer_context_modif_resp to collect DRBs to
  setup/release from E1 response, send F1 UE Context Modification when needed,
  and otherwise trigger direct RRC reconfiguration or NGAP modify response.
- Populate the E1AP Bearer Context Modification request with DRB-To-Setup and
  DRB-To-Modify lists derived from QoS flow processing. For CP->UP E1 Bearer
  Context Modification, Flow Mapping Information in DRB-To-Modify carries
  the QoS Flow QoS Parameters List for that DRB: per TS 38.463, when present,
  CU-UP replaces the previous mapping for that DRB, and this behavior is now enforced.

RRC PDU Session Modify - QoS and DRB Management:
- Add nr_rrc_apply_qos_add_modify() to process QoS add/modify and map flows to
  existing or new DRBs
- Add nr_rrc_apply_qos_release() and nr_rrc_apply_pdusession_modify() to
  process QoS release, build E1 DRB setup/modify/remove lists, and prepare
  bearer-context modification.
- Refactor nr_rrc_update_qos() processing for QoS add/modify,
  with DRB mapping to existing or new DRBs through the modify path helpers
- Extend nr_rrc_update_pdusession() with helpers for QoS
  add/modify/release, building E1 DRB modify/setup/remove lists, and
  sending bearer-context modification.
- Integrate E1 Bearer Context Modification into PDU Session Modify flow to
  propagate DRB changes to CU-UP
- Update default DRB in SDAP configuration after QoS changes
- Add `nr_sdap_entity_update_qos_flows` to replace DRB flow
  mappings from E1 flow information and clear stale SDAP role/mapping
  state when QFIs are removed.

Code Refactoring:
- Add find_or_add_pdu_session_mod() to create/reuse session entries
  in the E1 Bearer Context Modification request while building message.
- Add nr_rrc_send_e1_after_qos_update() to detect DRBs left without mapped
  QoS flows, remove them from UE state, and append them to E1 DRBs-to-remove.
- Use rrc_gNB_generate_dedicatedRRCReconfiguration instead of
  rrc_gNB_modify_dedicatedRRCReconfiguration, drop the old function

End-to-End PDU Session Modify Flow:

1. AMF -> CU-CP: NGAP PDU Session Resource Modify Request (QoS add/modify/release)
2. CU-CP (RRC): Process QoS flows -> map to DRBs (reuse existing or create new) ->
   populate E1AP Bearer Context Modification Request (DRBs to setup/modify/remove)
3. CU-CP -> CU-UP: E1AP Bearer Context Modification Request
4. CU-UP: Create F1-U tunnels for new DRBs -> create/release PDCP-SDAP
   resources -> update DRB/QFI mapping information
5. CU-UP -> CU-CP: E1AP Bearer Context Modification Response (F1-U tunnel info)
6. CU-CP: If DRB setup/release exists, send F1 UE Context Modification Request
7. CU-CP: Else if QoS/NAS requires it, send direct RRC Reconfiguration
8. CU-CP: Else complete modify directly with NGAP PDU Session Resource Modify Response
9. UE -> CU-CP: RRC Reconfiguration Complete
10. CU-CP -> AMF: NGAP PDU Session Resource Modify Response

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:28 +02:00
Guido Casati
8125f8a2eb PDU Session Modify: extend PDU Session Modify Transfer (NGAP/RRC) with QoS add/modify/release
This commit extends the PDU Session Resource Modify Request handling (within
PDU Session Modify) to fully support QoS Flow add/modify and release operations
(3GPP TS 38.413 section 9.3.4.3) via the Transfer IE. This is propagated to
RRC which performs PDU Session update, which occurs upon PDU Session Modify.

Refactoring was necessary since type `pdusession_transfer_t` and
`pdusession_resource_item_t` both for setup, were inaccurately used for the modify procedure.

The implementation adds proper type definitions, decoding logic,
and error handling for QoS flow management during PDU session modification transfer,
namely:
- Adds QoS add/modify and release lists to the NGAP Modify Request Transfer type
- Introduces QoS Flow With Cause IE
- Decode new IEs and propagate QoS changes to RRC session state

NGAP PDU Session Modify Request Transfer:
- Add qos_flow_to_release_t structure to represent QoS Flow to Release Items IE
  with QFI and release cause (per 3GPP TS 38.413 section 9.3.1.13)
- Introduce pdusession_mod_req_transfer_t structure to properly represent
  PDU Session Resource Modify Request Transfer IEs:
  * QoS Flow Add or Modify Request List (mandatory)
  * QoS Flow to Release List (optional)
- Add pdusession_resource_mod_item_t structure for PDU Session Resource
  Modify Request Items, replacing pdusession_resource_item_t, which is for setup
- Update ngap_pdusession_modify_req_t to use the new type-specific structure
- Refactor decodePDUSessionResourceModify() to return
  pdusession_mod_req_transfer_t instead of pdusession_transfer_t
  - Implement proper decoding of QosFlowAddOrModifyRequestList IE
  - Add decoding support for QosFlowToReleaseList (QosFlowListWithCause) IE:
    * Extract QFI and cause for each QoS flow to be released
- Improve error handling throughout

ngap_msg_includes.h:
- Add includes for NGAP_QosFlowListWithCause.h and
  NGAP_QosFlowWithCauseItem.h to support QoS flow release decoding

NGAP/RRC:
- Update nr_rrc_update_pdusession() function signature to accept
  pdusession_resource_mod_item_t instead of pdusession_resource_item_t
- Remove references to unnecessary pdu_session_type and n3_incoming
  fields that are not part of the Modify Request Transfer structure
- Update QoS flow update logic to use nb_qos_to_add_modify and
  qos_to_add_modify fields from the new transfer structure

This implementation aligns NGAP with 3GPP TS 38.413 specifications
and provides a foundation for handling QoS flow modifications and releases
during PDU session resource modification procedures.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:28 +02:00
Guido Casati
652a9e455b QoS Handling: add multi-QoS unit tests
- Update F1AP test cases to support multiple QoS flows
- Extend RRC bearers test with comprehensive multi-QoS testing
- Extend PDU sessions test to 2 PDU sessions per test
- Introduce template helpers to minimize duplicated code

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
c8b99ce011 GTPU: move QFI handling to send path and update tunnel API usage
Refactor GTP-U tunnel and send APIs so QFI is handled when sending packets,
not stored in tunnel creation state. Update SDAP and CU-UP integration to
use PDU-session keyed N3 mappings and explicit QFI-marked sends.

This commit clarifies layering ownership: GTP-U stays transport-only (TEID
lookup, decapsulation, extension parsing, callback dispatch), while SDAP
owns QoS semantics (QFI handling, QoS-flow-to-DRB policy, default DRB behavior,
and mapping updates); GTP-U does not perform runtime QFI-to-DRB mapping or
synthesize QFI.

Changes:
- remove `outgoing_qfi` from `gtpv1u_gnb_create_tunnel_req_t` and stop storing
  QFI as tunnel creation metadata; `newGtpuCreateTunnel(...)` now carries only
  transport/tunnel identity parameters (incoming_bearer_id, outgoing_bearer_id,
  outgoing_teid, remote address, callbacks)
- add `gtpv1uSendDirectWithQFI()` and pass QFI into `_gtpv1uSendDirect`
  to build UL PDU Session Container extensions
- shift QFI handling from tunnel provisioning to per-packet TX APIs: QFI is passed
  explicitly only when sending (`gtpv1uSendDirectWithQFI(...)`) and is absent from
  non-SDAP/F1 sends (`gtpv1uSendDirect(...)`)
- align N3 tunnel request semantics with session-level keys by setting incoming_rb_id
  to PDU session ID on N3 paths, while F1 paths keep DRB ID
- keep `gtpv1uSendDirect()` and `gtpv1uSendDirectWithNRUSeqNum()` on
  `NO_QFI`, and enforce non-SDAP RX callback path only when QFI is absent
- update `nr_sdap_rx_entity` to extract/validate QFI from SDAP UL headers,
  send UL data with `gtpv1uSendDirectWithQFI`, and use non-QFI send when
  SDAP header is disabled
- add disabled-SDAP safety checks in SDAP entity setup/mapping to enforce
  single-DRB and single-flow constraints per PDU session
- extend `test_gtp.cpp` with a `multi_qos_flows` scenario and QFI-aware send calls
- update `nr-cuup-load-test.c` bearer setup fields and tunnel creation calls
  to match the new API
- update tests/nr-cuup/nr-cuup-load-test.c to set explicit PDU session and
  QoS/SDAP parameters (sessionType, qosFlows[0], SDAP header flags), and to
  migrate both N3 and F1 tunnel creation calls to the new newGtpuCreateTunnel(...)
  signature (without outgoing_qfi)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
6786df3102 QoS handling: add F1 QoS Flow Information IE (GBR) handling in the CU
GBR information is optional in F1AP and is only present for GBR flows
(5QI < 5 for NonDynamic5QI, or Dynamic5QI flows with GBR characteristics).

Changes:

- Add optional gbr_qos_flow_information field to f1ap_qos_flow_param_t
  structure to propagate NGAP GBR QoS parameters to the DU, for scheduling
  resource allocation via nr_rrc_get_f1_qos_flow_param.

- Add GBR QoS flow information IE enc/dec in F1AP lib:
  - update encode_qos_flow_param() and decode_qos_flow_param()
  - update cp/eq/free
  - add GBR tests to f1ap_lib_test.c

This commit is a refactoring of commit 398ae02ab9 from !2836

Co-authored-by: Sriharsha Korada <sriharsha.korada@iis.fraunhofer.de>

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
17ff43168f QoS handling: add GBR QoS flow information decoding from NGAP QoS Flow Setup Request List
Implement GBR (Guaranteed Bit Rate) QoS flow information extraction from NGAP.
This enables handling in CU of GBR QoS flows
(e.g., voice, video) that require guaranteed and maximum bit rates.

Changes:
- Define qos_bitrate_t structure to encapsulate GFBR and MFBR
- Define gbr_qos_flow_information_t structure for GBR QoS parameters
- Add optional gbr_qos_flow_information field to pdusession_level_qos_parameter_t
- Extract GBR information from NGAP_QosFlowLevelQosParameters in fill_qos()
- Add NGAP_GBR-QosInformation.h include to ngap_msg_includes.h

GBR information is optional in NGAP and is only present for GBR flows
(5QI < 5 for NonDynamic5QI, or Dynamic5QI flows with GBR characteristics).
Bit rates are in kbps.

This commit is a refactoring of commit 398ae02ab9 from !2836

Co-authored-by: Sriharsha Korada <sriharsha.korada@iis.fraunhofer.de>

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
ef971849bd QoS handling: add Dynamic5QI QoS support/validation, split QoS characteristics by 5QI type
Model non-dynamic vs dynamic 5QI characteristics explicitly and propagate the
new layout through NGAP decode and RRC bearer/QoS handling.

Changes:
- Define `non_dynamic_5qi_t`/`dynamic_5qi_t`, PER/PDB bounds, and embed a
  `qos_characteristics` union in `pdusession_level_qos_parameter_t`
- Populate the new QoS structures in `fill_qos()`, including optional
  allocations for Dynamic 5QI `fiveQI` and NonDynamic `priorityLevelQos`
- Map QoS params to F1AP with `nr_rrc_get_f1_qos_flow_param()` and add range
  validation for dynamic priority/PDB/PER and non-dynamic 5QI
- Populate E1 QoS characteristics from the new layout and update QoS modify
  handling to manage optional pointer fields (`openair2/RRC/NR/rrc_gNB_NGAP.c`)
- Derive a numeric 5QI via `get_qos_fiveqi()`, handle missing-5QI dynamic flows
  conservatively, and extend dedicated-DRB decisions to fall back to dynamic
  characteristics
- Add a 5QI range assert in F1AP QoS encoding and extend bearer tests with a
  Dynamic 5QI flow

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
4ad557a0e3 QoS handling: add F1AP DRB QoS (IE) aggregation
DRB QoS IE (mandatory in DRB-Information IE) represents the DRB level QoS,
which shall be computed from multiple QoS flows mapped to the DRB instead
of using only the first QoS flow.

ARP is for admission control/preemption (1-15, 1 = highest priority) and
DRB-level QoS selection should use ARP for admission control decisions.

Changes:
- Use ARP priority (not 5QI priority) for selection (admission control decision)
- Iterate through all flows to find the flow with highest ARP priority (lowest ARP priority_level value
- Add fill_f1_drb_qos to return DRB QoS by value, i.e. QoS characteristics (5QI, priority, delay budget, error rate)
- Replace 'drb.nr.drb_qos = drb.nr.flows[0].param' with proper aggregation

Example: DRB with Flow1 (ARP=10) and Flow2 (ARP=5)
- Old: Would incorrectly use Flow1's QoS
- New: Correctly uses Flow2 (ARP=5) which has higher priority

This commit is a refactoring of commit 398ae02ab9 from !2836

Co-authored-by: Sriharsha Korada <sriharsha.korada@iis.fraunhofer.de>

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
e041a2a078 QoS Handling: set default DRB in SDAP entity to first DRB added
According to 3GPP TS 37.324, there shall be only one default DRB per
SDAP entity (per PDU session). The previous implementation incorrectly
marked all DRBs as default, violating this requirement.

Changes:
- Add default_drb field to nr_sdap_configuration_t to track the default
  DRB ID per PDU session
- Arbitrary set default DRB to the first DRB added when creating bearers in a
  PDU session
- Update nr_rrc_build_sdap_config_ie() to accept defaultDRB (bool) parameter
  instead of hardcoding true
- Use tracked default_drb value when building RRC SDAP Config IE
- Use tracked default_drb value when building E1AP DRB setup structure

Also:
- Fix include style in nr_sdap_configuration.h (<stdbool.h> instead of
  "stdbool.h")

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
04126759aa QoS handling: add 5QI validation during QoS flow setup/modify
Move 5QI validation from RRC reconfiguration message generation to
where QoS flows are actually added/updated, ensuring validation
happens early in the process.

Changes:
- Remove redundant 5QI validation from rrc_gNB_modify_dedicatedRRCReconfiguration():
  that was checking values right before RRC message generation
- Add 5QI validation in add_qos() to reject unsupported 5QI values
  during PDU session setup
- Add 5QI validation in nr_rrc_update_qos to skip unsupported 5QI
  values during PDU session modify (continues to next flow)
- Add is_5qi_supported() function in rrc_gNB_radio_bearers.c:
  validates standardized 5QI values (1-9, 65-90) per 3GPP TS 23.501
  Table 5.7.4-1 and checks against the classification map

Also, remove remaining limit to 1 QoS flow per DRB.

Closes #541

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
8ceb7ec37b QoS Handling: implement intelligent QoS-to-DRB mapping based on 3GPP TS 23.501
Implement QoS flow multiplexing logic that optimizes DRB usage by classifying
5QI values per 3GPP TS 23.501 Table 5.7.4-1 and applying resource-type-aware
multiplexing limits. The changes are adopted in nr_rrc_add_bearers, which
is the RRC function responsible for adding PDU Sessions and DRBs in RRC.

Key features:
- Classify 5QI by resource type (DC-GBR, GBR, Non-GBR)
- Reuse existing DRBs when QoS characteristics are compatible
- Dedicated DRBs for DC-GBR (5QI 82-90) and high-priority services
- Per-type multiplexing limits: DC-GBR=1, GBR=2, Non-GBR=5
- Aggregate cap: max 5 flows per DRB

Implementation:
- nr_rrc_get_5qi_resource_type():
  Maps 5QI values to resource types using lookup table.
  DC-GBR: 5QI 82-90, GBR: 5QI 1-4,65-67,71-76, Non-GBR: 5QI 5-11,69-70,79-80.
  Unknown 5QIs default to Non-GBR with warning.

- nr_rrc_qos_dedicated_drb():
  Identifies 5QIs requiring isolated DRBs (high priority, low-PER).
  Includes: DC-GBR: 5QI 82-90, 5QI 4,6-10 (video), 5QI 70 (mission-critical),
  5QI 71-73 (live streaming), 5QI 80 (low-latency).

- nr_rrc_count_qos_flows_by_type():
  Counts QoS flows mapped to a specific DRB, grouped by resource type.
  Used to check capacity and enforce multiplexing limits.

- nr_rrc_find_suitable_drb_for_qos():
  Searches existing DRBs in the same PDU session for available capacity.
  Checks resource type compatibility, per-type limits, and aggregate cap.
  Returns DRB ID if suitable, -1 if new DRB needed.
  DC-GBR flows always return -1 (require dedicated DRB).

- nr_rrc_assign_drb_to_qos_flow(), which either reuses a DRB
  selected by nr_rrc_find_suitable_drb_for_qos() or creates a new DRB via
  nr_rrc_add_drb, assigns its ID to the QoS flow

Note: this commit is multi-QoS ready.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
302c1c0450 CU-UP: refactor DRB to be setup logic (E1 bearer context) into reusable helper functions
The new bearer context setup logic is looping through the PDU sessions
to be setup list first and then through the DRB to be setup list.
The function has still room for improvement since e1_bearer_context_setup
is calling a getter for NR_DRB_ToAddModList_t which has another nested
DRB loop. The goal of this commit is to further simplify the logic
by minimizing unnecessary nested loops, improving clarity and preparing
for reuse in Bearer Context Modification.

Main changes:

- Build DRB_ToAddMod list in the DRBs loop and centralize SDAP/PDCP configuration
- Add helpers to fill DRB to be setup and QoS flow handling, to improve organization
  and enable reuse in bearer context modification procedures.

Implementation:
- Replace fill_DRB_configList_e1() with fill_rrc_drb_to_addmod()
  * Build one NR_DRB_ToAddMod item per DRB from E1 DRB_nGRAN_to_setup
  * Accumulate all DRBs into a single NR_DRB_ToAddModList_t per PDU session
- Call e1_add_bearers() once per PDU session with the aggregated list,
- Introduce fill_e1_qos_flows_setup() to populate DRB_nGRAN_setup_t
  * Iterate E1AP QoS flows and copy QFIs into the E1 response
- Introduce fill_e1_drb_setup() to encapsulate DRB_nGRAN_setup_t filling
  via fill_rrc_drb_to_addmod()
- Refactor e1_bearer_context_setup() to use new helper functions

Note: supports multiple QoS flows per DRB (TS 38.331 compliant)
  * Move security parameters setup to caller scope
  * Simplify DRB loop logic for better readability

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
773e62077b GTPU: refactor tunnel creation API for N3 (per PDU session) and F1-U (per DRB)
Refactor GTP-U tunnel creation to align for 3GPP architecture where N3
tunnels are created per PDU session (supporting multiple QoS flows) and
F1-U tunnels are created per DRB. This change simplifies the API and
prepares the codebase for supporting multiple DRBs and QoS flows per
PDU session.

This is the first step of a final design that will consist of:
- 1 GTP-U tunnel create call for each PDU session, each with a DRB mapping
- 1 GTP-U tunnel create call for each DRB (i.e. no QFI, no internal mapping)

API Changes:
- Convert gtpv1u_gnb_create_tunnel_req_t and gtpv1u_gnb_create_tunnel_resp_t
  from array-based to scalar fields
  * Remove num_tunnels field and array fields (outgoing_teid[], pdusession_id[], etc.)
  * Use scalar fields: outgoing_teid, pdusession_id, incoming_rb_id, etc.
- Simplify gtpv1u_create_ngu_tunnel() to handle single tunnel per call
  * Remove internal loop that processed multiple tunnels
  * Call newGtpuCreateTunnel() once per invocation
  * Update response handling to fill single tunnel response

Function Refactoring:
- Split generic drb_gtpu_create() into specialized functions:
  * n3_gtpu_create(): Creates N3 tunnel (CU-UP to UPF/core network)
    - callback assigned internally: nr_pdcp_data_req_drb, sdap_data_req
    - One tunnel per PDU session
    - QFI marking
    - Supports multiple QoS flows per PDU session
  * f1_drb_gtpu_create(): Creates F1-U tunnel (CU-UP to DU or DU side)
    - callback assigned internally (cu_f1u_data_req or DURecvCb)
    - One tunnel per DRB
    - No QFI marking (QFI not used in F1-U)
    - Each DRB can carry multiple QoS flows
- Update e1_bearer_context_setup() to loop over DRBs per PDU session
  Each item in the PDU Session list contains a list of DRBs, thus
  the function helper was adjusted to reflect that:
  * Move DRB loop inside PDU session loop
  * Create F1-U tunnel for each DRB individually
  * Create single N3 tunnel per PDU session (outside DRB loop, with DRB mapping)

Also:
- Add comments to document and improve log messages for clarity
- Consistent error handling with AssertFatal checks
- Update NSA code path (rrc_gNB_nsa.c) to use new scalar API

Note: this commit is already taking into account the multi-QoS flows design

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
f17b2bb805 GTPU: reduce inter-dependencies between LTE and NR in GTP-U header
This header is pulled by both LTE and NR libraries, therefore is
advised to minimise cross dependencies in LTE/NR. With this change
the only include is common/platform_types.h

Also, remove redundant comments.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
f1b8d45bf9 fix (nas): call set_qfi before PDU session interface setup on Session Accept
Apply QoS flow id as soon as the accept message is parsed, before interface
setup and before any code path that starts the per-session interface thread,
so the first SDUs are not sent with the default 0-initialized QFI 0.

Changes:
- In handle_pdu_session_accept, move set_qfi(...) to immediately before
  interface setup.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
43ec237a4f fix (NGAP): encode optional QoS list when not empty
Only build QoSFlowAddOrModifyResponseList when at least one QFI is present
and fail fast if PDUSessionResourceModifyResponseTransfer encoding does not
produce a valid buffer. This avoids sending malformed modify responses and
makes encoding failures explicit in the NGAP modify response path.

Changes:
- do QoS add/modify response list allocation when nb_of_qos_flow > 0
- add ASN.1 encode result validation

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
428859f218 fix (NGAP): decode PDUSessionResourceHandoverList with correct IE id
Use the PDUSessionResourceHandoverList protocol IE identifier when
decoding HandoverCommand optional session resources.

Changes:
- replace IE lookup id_HandoverType with
  id_PDUSessionResourceHandoverList in decode_ng_handover_command()

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
c97820622a fix (NGAP): guard optional PDUSessionNAS-PDU in PDU Session Setup Request handler
Avoid NULL dereference when AMF omits the optional pDUSessionNAS-PDU
IE in PDUSessionResourceSetupRequest.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
77aff40af7 fic (NGAP): double allocation and ASN free in PDU Session Modify Response
Remove redundant allocation and duplicate add for AMF_UE_NGAP_ID (asn1cSequenceAdd
already allocates), fixing encoding failure and leak. Fix ASN_STRUCT_FREE_CONTENTS_ONLY
in the unsuccessful-transfer path to free the correct struct.

Changes:
- In ngap_gNB_pdusession_modify_resp(), for AMF_UE_NGAP_ID IE: remove redundant
  calloc() and duplicate asn1cSeqAdd() after asn1cSequenceAdd (avoids uninitialized
  list element, encoding assertion, and leak).
- In PDU Session Resource Modify Unsuccessful Transfer handling: pass
  &pdusessionTransfer to ASN_STRUCT_FREE_CONTENTS_ONLY instead of the unused
  NULL pointer pdusessionTransfer_p;

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
835c80b39d SDAP: fix PDU session ID range validation logic
Replace incorrect mathematical range check with explicit bounds
validation in nr_sdap_delete_entity(). The previous condition
(pdusession_id) * (pdusession_id - NR_MAX_NB_PDU_SESSIONS) > 0
was mathematically incorrect and could fail to properly validate
PDU session IDs.

PDU session ID validation now correctly rejects values outside
the valid range [0, NR_MAX_NB_PDU_SESSIONS]

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
522fb88c6e E1AP: make ActivityNotificationLevel configurable in Bearer Context Setup
This is a mandatory IE.

Replace hardcoded ActivityNotificationLevel value with configurable field
in e1ap_bearer_setup_req_t structure, enabling proper encoding/decoding
and testability of different notification levels. ActivityNotificationLevel
is now properly encoded from message structure instead of hardcoded value,
enabling different notification levels (DRB, PDU Session, UE) to be
specified per bearer setup request.

Changes:
- Add activity_notification_level_t enum with values: drb, pdu_session, ue
- Add anl field to e1ap_bearer_setup_req_s structure
- Replace hardcoded E1AP_ActivityNotificationLevel_pdu_session with
  dynamic value from msg->anl in encode_E1_bearer_context_setup_request
- Add ActivityNotificationLevel IE dec/eq/cp
- Update test and RRC call sites to initialize anl field
- Add E1AP_ActivityNotificationLevel.h include to e1ap_lib_includes.h

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
971436030d E1AP: convert PDU session arrays to dynamic allocation
Replace fixed-size PDU session arrays with dynamically allocated
pointers in E1AP bearer context request/response types to reduce
memory footprint and improve scalability.

Memory usage is now proportional to actual PDU session count
instead of always allocating for maximum capacity.

Impact:
- Callers must allocate and free PDU session arrays; E1AP decode/cp
  allocate, free_e1ap_context_setup_request/mod_request free them.

Major changes:
- Request/response types (e1ap_messages_types.h): pduSession, pduSessionMod,
  pduSessionRem as pointers in setup/mod request and setup/modif response structs.
- E1AP lib: decode allocates with bounds check; cp/eq/free handle pointer members;
  e1ap.c frees setup request after handling.
- CU-UP (cucp_cuup_handler): allocate resp.pduSession and modif.pduSessionMod.
- CU-CP side (cuup_cucp_direct, rrc_gNB, rrc_gNB_NGAP): allocate at E1 request
  build and call free after send where needed; rrc_gNB_NGAP includes E1AP free decls.
- Tests (e1ap_lib_test, nr-cuup-load-test): allocate PDU session arrays in helpers.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
d085bb0ba0 fix (E1AP): allocate DRB-To-Modify list once in PDU session encode
When building E1AP PDU Session Resource To Modify items, create
dRB_To_Modify_List_NG_RAN only once if there are DRBs to modify, and add
modify items to that list.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
ab00b2ff94 fic (E1AP): remove single-DRB limit in PDU session DRB setup list decode (PDU session setup/modify)
Decode paths for PDU Session Resource To Setup and To Setup/Modify must
accept multiple DRB list entries, aligned with multi-DRB RRC behaviour.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
24dd7b5b9e fix (E1AP): iterate DRB Setup List by numDRBSetup in mod response
Fix wrong bound in the setup list loop: Bearer Context Modification
Response encoding must populate dRB_Setup_List_NG_RAN from
DRBnGRanSetupList[0..numDRBSetup), not numDRBModified.

In encode_E1_bearer_context_mod_response(), change the DRB Setup List
encoding loop from pdu->numDRBModified to pdu->numDRBSetup.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
d4c1731e97 Platform types (QoS): refactor QoS Priority Level type per 3GPP TS 23.501
QoS Priority Level range is 1-127, fits in uint8_t (3GPP TS 23.501 §5.7.3.3).

Refactor qos_priority field in pdusession_level_qos_parameter_t to use
qos_priority_level_t typedef (uint8_t) instead of uint64_t. Also,
improve documentation.

Changes:
- Add qos_priority_level_t typedef (uint8_t), more efficient than uint64_t
- Change pdusession_level_qos_parameter_t.qos_priority from uint64_t
  to qos_priority_level_t
- Add MIN_QOS_PRIORITY_LEVEL (1) and MAX_QOS_PRIORITY_LEVEL (127)
  defines for range checks

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
6560403d3c Platform types (QoS): refactor ARP priority level type
Since qos_priority_t reprensents the ARP priority level, an integer
(1..15), this commit is renaming it to qos_arp_priority_level_t
typedef (uint8_t) for better clarity and cleaning up unnecessary enum definition.

Changes:
- Rename qos_priority_t to qos_arp_priority_level_t (uint8_t typedef)
  for type safety and semantic meaning
- Remove enum with 15 explicit values (not needed, matches spec as integer)
- Add MIN_QOS_ARP_PRIORITY_LEVEL (1) and MAX_QOS_ARP_PRIORITY_LEVEL (15) defines
  for bound checks, simpler and more efficient
- Update qos_arp_t struct to use new typedef

References:
- 3GPP TS 23.501 §5.7.2.2!

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
4b5d5c1ae6 Platform constants: consolidate PDU session limit constants
Replace NGAP_MAX_PDU_SESSION and E1AP_MAX_NUM_PDU_SESSIONS with
shared NR_MAX_NB_PDU_SESSIONS constant. The value (256) is consistent
with both TS 38.331, TS 38.413 and TS 38.463

All protocol layers (NGAP, E1AP, RRC, SDAP) now use a single
shared constant for maximum PDU sessions per UE, ensuring
consistency across the codebase

E1AP_MAX_NUM_PDU_SESSIONS (was 4) is removed from e1ap_messages_types.h

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
67e646aa7c RRC: remove warning when UP params are missing
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
9327b4be6b RRC: simplify PDU session status tracking, remove intermediate states
- Remove PDU_SESSION_STATUS_DONE and PDU_SESSION_STATUS_REESTABLISHED
  from enum and from get_pdusession_status_text().
- Add rrc_gNB_action_from_pdusession_status(): sets xid for all PDU
  sessions and derives RRC transaction action from session status
  (NEW, TOMODIFY, TORELEASE) or reestablishment. Add macro
  ASSERT_PDU_ACTION_SINGLE to enforce at most one active status
  (or reestablishment) per transaction.
- rrc_gNB_generate_dedicatedRRCReconfiguration: replace inline loop
  and xid/action logic with a single call to the new helper; stop
  transitioning NEW to DONE here.
- rrc_gNB_modify_dedicatedRRCReconfiguration: set xid at loop start;
  bypass sessions with status != TOMODIFY (instead of >= DONE); remove
  FAILED handling and DONE/xid assignments from loop; add xid to log.
- NGAP: INITIAL_CONTEXT_SETUP_RESP and PDUSESSION_SETUP_RESP check
  NEW (not DONE) and set ESTABLISHED when reporting success;
  PDUSESSION_MODIFY_REQ sets TOMODIFY (not NEW); PDUSESSION_MODIFY_RESP
  checks TOMODIFY (not DONE), drops NEW/ESTABLISHED branch, improves
  warning text; HANDOVER_REQUIRED considers only ESTABLISHED (drop DONE).

PDU sessions now move directly from NEW or TOMODIFY to ESTABLISHED in
the NGAP response handlers. One RRC reconfiguration corresponds to one
NGAP procedure (single active PDU session status per transaction).
rrc_gNB_modify_dedicatedRRCReconfiguration remains the separate
entry point for PDU Session Modify.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
01add4ab43 RRC: rrc_gNB_radio_bearers.c - add QoS flow and DRB removal utilities
PDU Session Modify procedures enable the teardown of a subset of DRBs
and, together with multi-QoS handling, of a subset of QoS flows.
Thus, it is necessary to introduce (1) `rm_qos` to remove a specific QFI
from the RRC list of QoS flows and (2) `nr_rrc_remove_drb_by_id`
to remove a specific DRB by ID, upon successful retrieval of the pointer
to its RRC list item.

- Add rm_qos() to remove QoS flows by QFI from PDU session QoS list
- Add nr_rrc_remove_drb_by_id() to remove DRBs by ID from UE DRB list
- Export find_drb_by_pdusession_id() as public functions for reuse
  in PDU session modify procedures

These utilities enable QoS flow release and DRB cleanup operations
required for PDU Session Resource Modify Request handling.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
7f13e04f75 RRC: consolidate F1 UE Context Modification Request handling
UE Context Modification Request can be triggered by either DRB setup
(from either PDU Session Setup or Modify) or modify (setup/release in PDU Session Modify).
The goal of this commit is to refactor the handling of UE Context Modification Request
messages by improving clarity and reducing code duplication.

Changes:

- Remove overlapping DRB release function `rrc_gNB_send_f1_drb_release_request` and replace with
  unified `rrc_send_f1_ue_context_modification_request` that handles both DRB setup and release.
- Remove inefficient `rrc_gNB_generate_UeContextModificationRequest` and inline
  its logic into callers using the new unified helper.
- Fix type safety in DRB release handling: change from `int *drb_to_release` to
  `f1ap_drb_to_release_t *rel_drbs` to use proper struct type instead of raw int array.
- `rrc_send_f1_ue_context_modification_request` copies DRB arrays internally before freeing,
   allowing safe use of stack-allocated arrays by callers.
- Update `rrc_gNB_process_e1_bearer_context_setup_resp` to use unified helper for DRB setup
  when F1 context is already active.
- Update `rrc_gNB_process_e1_bearer_context_modif_resp` to DRB release in a single F1 UE
  Context Modification Request call (will be later used for DRB setup, improving efficiency)
- Replace magic number `32` with `E1AP_MAX_NUM_DRBS` constant for better maintainability.
- Update E1AP procedures documentation to reflect unified function name.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
226266743c RRC: refactor trigger_bearer_setup to split RRC bearers lists setup from E1 trigger
The current trigger_bearer_setup is mixing 2 functionalities: (1) setting up
RRC lists in the UE context (e.g. PDU Sessions, DRBs) (2) filling the E1AP
message to trigger Bearer Context Setup.

This commit is separating the two functionalities.
This is necessary to improve the readability of the code flow supporting
the setup of new bearers before the upcoming QoS-related changes.

* Introduce nr_rrc_add_bearers(rrc, UE, n, sessions) to populate UE context
  lists (PDU Sessions, one DRB per session, one QoS per DRB).
* Early CU-UP rejection: apply early is_cuup_associated(rrc) checks at call
  sites to fail fast before mutating UE context (this check is the reason
  why the original function was a bool).
* trigger_bearer_setup goal is to fill Bearer Context setup message upon reception
  of PDU sessions to setup, therefore it was changed to fill the bearer context setup
  for all PDU session with status PDU_SESSION_STATUS_NEW.
  Signature was changed to return void and read the lists from UE context.
  The function is also deriving UP keys and building PDU Session and DRB items
  to setup via helpers (e.g. fill_e1_pdusession_to_setup to fill PDU Session to
  setup items).
* Enforce single DRB per PDU Session, will be updated in a later commit.

Impact
* Each function has now limited and self-conteined scope:
  nr_rrc_add_bearers is responsible for update of RRC lists in the UE context.
  trigger_bearer_setup is responsible for preparing the E1 Bearer Context message.
* E1 Bearer Context Setup construction is centralized and clearer.
* The flow is now following these steps:
  1) receive NG request to setup PDU Sessions
  2) check CUUP association first, early failure if check fails
  3) add PDU Session and DRBs to UE context lists
  4) trigger Bearer Context Setup Request

The same flow applies to PDU Sessions from the Initial Context Setup (initial_PDUs)
with the only difference that those are stored in a separate list (legacy behavior)
and to NG Handover Request processing.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
ef505b55d7 fix (RRC): include NR ASN constants in DU SIB builder
Include NR_asn_constant.h in rrc_gNB_du.c so NR_maxCellIntra and NR_maxCellInter
are defined during compilation.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Guido Casati
89c9e89a9d PDCP build: exclude CU-CP/CU-UP from UE, guard E1 init for gNB only
- Common source in NR_PDCP_SRC and derive NR_PDCP_SRC_GNB (gNB) and
  NR_PDCP_SRC_UE from it; UE no longer compiles cucp_cuup_handler,
  cuup_cucp_if, cuup_cucp_direct, cuup_cucp_e1ap.
- NR_L2_SRC_UE uses NR_PDCP_SRC_UE so UE links only common PDCP sources.
- Add L2_NR compile definition PDCP_CUCP_CUUP; wrap nr_pdcp_e1_if_init
  call in nr_pdcp_oai_api.c with #ifdef PDCP_CUCP_CUUP so UE does not
  reference it.

UE build no longer depends on E1/CU-CP/CU-UP code or symbols; gNB
keeps full PDCP and E1 interface init.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-05-04 12:03:27 +02:00
Rakesh Mundlamuri
fa4bb92d03 Fixing broken nr-cu-nrppa-test simulator and nr-ue-nas-simualtor
Signed-off-by: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org>
2026-05-03 19:26:13 +05:30
Sakthivel Velumani
f9b0dd6af3 fix: perform FFT only on PBCH symbols
Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-05-03 01:55:44 +00:00
Sakthivel Velumani
4b85b71523 log: fix debug logging
Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-05-03 01:55:43 +00:00
Sakthivel Velumani
e4b2125f1e Refactor PBCH & PSBCH UE procedures
1. Refactor channel estimation and freq domain data extraction functions
   to take rxdataF of only one symbol.
2. Split the PBCH and PSBCH decoding into two parts. Fisrt part is
   called for every symbol and generates LLRs of each symbol. Second
   part is called in the last PBCH/PSBCH symbol to decode and send the
   payload to MAC.
3. In channel estimation, the loop around rx antennas is taken out to
   make the code more modular.

Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-05-03 01:55:32 +00:00
francescomani
d5c0e580ab removed few more unused parameters
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 14:29:58 +02:00
francescomani
c307e20ab9 remove unnecessary rrc_enb_process_msg hack in nr-uesoftmodem
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 14:27:26 +02:00
francescomani
6c39ea2b0b remove unnecessary pdcp_run hack in nr-softmodem
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 13:54:33 +02:00
francescomani
7e9d3cc328 remove unused dump_uci_stats
Signed-off-by: francescomani <email@francescomani.it>
2026-05-01 13:23:06 +02:00
Robert Schmidt
4ab525aca1 unit test: build everything
The previous commit is only necessary because not everything is built in
unit tests...

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-30 19:56:18 +02:00
Robert Schmidt
d8962fa270 spsc_q: fix call to spsc_q_alloc() in benchmark
Fixes: 4d67a6e513 ("spsc_q: make compile on older compilers")

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-30 19:55:09 +02:00
Robert Schmidt
d420f8c123 Merge branch 'integration_2026_w18' into 'develop'
Integration `2026.w18`

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

Closes #1072

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

We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length of
the samples is no longer samples_per_slot.  # Please enter a commit
message to explain why this merge is necessary,
2026-04-30 11:50:37 +02:00
Florian Kaltenberger
d1b777e0a5 fixing wrong logic for SIMO and MISO AWGN channels
Signed-off-by: Florian Kaltenberger <florian.kaltenberger@eurecom.fr>
2026-04-30 10:59:17 +02:00
Rakesh Mundlamuri
35965a1418 Remove unnecessary log
We already check for number of received samples above after receiving.
When aligned to the slot boundary for synchronization, this length
of the samples is no longer samples_per_slot.

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

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

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

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

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

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

Changes:

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

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

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

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

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

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

Changes:

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

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

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

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

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

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

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

Arguments for removing:
- There are no resources to maintain it. Including no machine with the
  board.
- There is no clue that someone is actually using it.
- The library is not standard/cannot be generalized. It was designed for
  a specific solution and cannot be used for another.
- The standard AAL (BBDev or other) should be privileged for LDPC offload
  to a hardware accelerator.
2026-04-27 09:56:35 +02:00
Jaroslava Fiedlerova
bfb177c38e Merge branch 'integration_2026_w17' into 'develop'
Integration `2026.w17`

* !4054 Minor fixes to gNB/UE behavior in RFsim
* !4062 Minor fixes/cleanup in NGAP lib
* !4036 Fix CSI-RS estimation
* !4055 fix (RRC): do not trigger N2 HO when no active PDU sessions are present
* !3988 asn1c inside local tree
* !3762 L1 gNB type0 PDSCH
* !4069 ctest: fix tests when compiling with sanitizers, ignore build directories, fix spsc_q build
* !4067 Small compilation fixes
* !3444 7.2 FHI with XRAN K release
* !4061 T-Tracer & Data Recording v1.1: UL PHY trace modularization and recording enhancements
* !3975 ZMQ radio
* Increase PRACH queue capacity from 8 to 16
* !4059 CI: Various CI adjustments

Closes #1074 and #1073

See merge request oai/openairinterface5g!4063
2026-04-24 20:23:02 +00:00
Jaroslava Fiedlerova
434cd8ca3d Merge remote-tracking branch 'origin/ci-adjustments' into integration_2026_w17 (!4059)
CI: Various CI adjustments

- RAN-SA-FHI72-CN5G: activate/inactive carriers on VVDN RU - to check if
  it improves reliability and stability of the pipeline
- RAN-SA-FHI72-CN5G: update CN with new IP range - avoid IP address
  overlap with CN used in RAN-SA-AERIAL-XXX pipelines
- RAN-SA-B200-Module-SABOX-Container: test with deltaMCS in SC-FDMA test
  case
- RAN-NSA-B200-Module-LTEBOX-Container: adjust PRACH DTX threshold to
  reduce number of false RA attempts on eNB
- remove retx check in RAN-SA-Multi-Antenna-CN5G and RAN-SA-AERIAL-CN5G -
  to avoid false CI failure caused by unsuccessful retx check of
  interfering UE
- adjust RAN-SA-Multi-Antenna-CN5G and RAN-SA-AERIAL-CN5G configs to
  improve UL throughput
2026-04-24 20:46:46 +02:00
Jaroslava Fiedlerova
38167e50f8 CI: Adjust config file in UL heavy test of SA-AERIAL-CN5G
Manual testing showed that increasing pusch_TargetSNRx10 by 3 dB improves
uplink throughput in UL-heavy test cases of SA-AERIAL-CN5G
2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
d1129d1f90 CI: Reduce RX gain in 100 MHz test of SA-Multi-Antenna-CN5G
Manual testing showed that lowering the RX gain reduces UL
retransmissions and improves overall UL throughput in the 100 MHz 2x2
test case of the SA-Multi-Antenna CN5G pipeline.
2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
6a6f200790 CI: Change OC CN for RAN-SA-FHI72-CN5G 2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
1c26f372aa CI: Disable machine performance tuning in RAN-SA-FHI72-CN5G
Performance tuning is done automatically after the reboot
2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
0da440017e CI: Test deltaMCS power control mode in SA-B200-Module-SABOX-Container 2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
e0a2b4837c CI: Configuration file adjustment for NSA-B200-Module-LTEBOX-Container
Try to reduce number of false RA attempts on eNB
2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
11dc86f59c CI: Add scripts to (in)activate carriers on VVDN
Add vvdn-activate-carriers.sh to verify RU PTP synchronization and
configuration. Activates carriers on VVDN RU by editing sysrepo running
datastore (INACTIVE->ACTIVE).

Add vvdn-inactive-carriers.sh to reverse the operation, setting all
carriers back to INACTIVE via sysrepocfg.
2026-04-24 17:18:28 +02:00
Jaroslava Fiedlerova
2d34e6cb9a Increase PRACH queue capacity from 8 to 16
After the changes introduced in !3991, multiple PRACH PDUs can be
pushed from L2 to L1 before being processed. This leads to the PRACH
queues filling up and triggering "PRACH occ queue is full" issues on
the gNB.

This commit increases the queue size from 8 to 16, providing additional
buffering and preventing queue overflows as reported in #1072.
2026-04-24 14:26:42 +02:00
Jaroslava Fiedlerova
0054b496d4 Merge remote-tracking branch 'origin/zmq-radio-3' into integration_2026_w17 (!3975)
ZMQ radio

This is a zmq radio implementation that attempts to integrate NR UE with
the ocudu project.

OAI integration status:
This can currently be used to connect OAI gnb and OAI UE.

Compilation:
cmake --build . --target nr-softmodem nr-uesoftmodem ldpc params_libconfig zmq_radio

Running
- gNB command:
sudo ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name zmq_radio --zmq.[0].tx_channels tcp://127.0.0.1:4556 --zmq.[0].rx_channels tcp://127.0.0.1:4557

- UE command:
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --device.name zmq_radio --zmq.[0].tx_channels tcp://127.0.0.1:4557 --zmq.[0].rx_channels tcp://127.0.0.1:4556

A new CI testcase for 2x2 configuration was added.

OCUDU integration status:
The OAI NR UE enters RRC Connected state.

For more details, please refer to the description of MR !3975.
2026-04-24 14:17:12 +02:00
Jaroslava Fiedlerova
6b06bda933 Merge remote-tracking branch 'origin/data_recording_app_optimization_part1' into integration_2026_w17 (!4061)
T-Tracer & Data Recording v1.1: UL PHY trace modularization and recording
enhancements

Based on the plan with @schmidtr  and @roux-cedric, we agreed to enhance
the data recording in two phases:

- Phase 1: Update Data Recording framework to get All internal Massive
  Updates and enhancements through the whole framework public
- Phase2: Restructure T-Tracer Apps and remove all Mems in gNB and UE
  Softmodems to Get data on T-tarcer Apps for all activated Messages
  symbol-by-symbol, and related Meta-data, then do the aggregation on
  the T-tracer App. So, there is No Memory Creation at all on the 5G NR
  Stack.

This Merge Request is for Phase 1. We address the comments given in: !3632

Summary: Refactor T-Tracer instrumentation for UL PHY data capture and
improve the data recording application (v1.0 → v1.1). This MR extracts
inline T() macro calls into modular wrapper functions, adds Unix timestamp
fields to trace messages, implements a 4-state shared memory protocol for
gNB/UE tracers, and delivers an improved synchronized recording architecture
in Python.

Changes:
- T-Tracer: Modular UL PHY trace wrappers
  - Add T_messages_creator.c/.h with 7 wrapper functions (log_ul_fd_dmrs,
    log_ul_fd_chan_est_dmrs_pos, log_ul_fd_pusch_iq,
    log_ul_fd_chan_est_dmrs_interpl, log_ul_payload_rx_bits,
    log_ul_payload_tx_bits, log_ul_scrambled_tx_bits) and a static
    log_ul_common() helper
  - Register T_messages_creator.c in the T library CMake build
  - Update T_messages.txt definitions: Remove string timestamps and get
    time stamp directly from sending_time of T-macro.
  - PHY: Replace inline T() calls with wrappers
  - gNB (nr_ulsch_demodulation.c, nr_ul_channel_estimation.c,
    phy_procedures_nr_gNB.c): replace inline T() calls with
    T_messages_creator wrappers
  - UE (nr_ulsch_coding.c, nr_ulsch_ue.c): replace inline T() calls with
    log_ul_payload_tx_bits and log_ul_scrambled_tx_bits wrappers
  - Remove deprecated log_tools.c/.h and its CMake reference

- T-Tracer gNB & UE apps: Enhancements
  - Implement 4-state shared memory protocol (WAIT → CONFIG → RECORD → STOP)
    for coordination with the data recording app
  - Add event_trace_msg_ul_data struct with 27 UL metadata fields
  - Refactor event loop to poll()-based with proper signal handling
  - Separate project IDs (gNB=2335, UE=2336) and ftok paths for independent
    shared memory segments
  - Update shared memory sizing: Limit number of records to
    NUM_MESSAGES_PER_SLOT=5, 100 slots

- Data Recording App v1.1
  - Improved architecture with thread pool and barrier synchronization
  - Frame/slot-based message grouping with per-slot validation
  - atexit/signal-based cleanup for graceful shared memory detach
  - Delete legacy data_recording_app_v1.0.py

- Library improvements
  - config_interface.py: remove legacy YAML config reader, streamline to
    JSON-only
  - shared_memory_interface.py: new module for shared memory
    attach/read/protocol
  - wireless_parameters_mapper.py: new module for NR parameter mapping
  - data_recording_messages_def.py: updated message definitions for
    27-field struct
  - sigmf_interface.py, sync_service.py, common_utils.py: remove unused
    imports and dead code

- Documentation
  - Update data_recording.md for v1.1 architecture and usage
2026-04-24 14:09:41 +02:00
Romain Beurdouche
0538ea4937 fix(nrLDPC_coding): Remove ldpc_xdma
Arguments for removing:
1. There are no resources to maintain it.
   Including no machine with the board.
2. There is no clue that someone is actually using it.
3. AAL is privileged for LDPC offload to a hardware accelerator.
2026-04-24 12:09:34 +00:00
Bartosz Podrygajlo
ff4284050d CI testcases with OCUDU
Added two testcases with OCUDU split8 gnb
2026-04-24 13:42:14 +02:00
Jaroslava Fiedlerova
7487638d72 Merge remote-tracking branch 'origin/oran_fhi_k' into integration_2026_w17 (!3444)
7.2 FHI with XRAN K release

Purpose:
Implement the O-RAN 7.2 Fronthaul interface using O-RAN Software Community's
XRAN release K. The new features of the release K can be leveraged and it is
compatible with DPDK version 24.11.4.

For more details please refer to the MR !3444 description.
2026-04-24 11:38:43 +02:00
Jaroslava Fiedlerova
6b111384e5 Merge remote-tracking branch 'origin/compilation_fixes' into integration_2026_w17 (!4067)
Small compilation fixes

Signed-off-by: francescomani email@francescomani.it
2026-04-24 11:38:09 +02:00
abgaber
bb11b8eafe Add hint about the maximum number of records and where to oincrease the shared memory 2026-04-24 10:31:38 +02:00
abgaber
c834a694ae doc: update data recording documentation for v1.1 architecture 2026-04-24 10:31:38 +02:00
abgaber
2f23dd2444 data-recording v1.1: Major rewrite of the synchronized real-time data recording system.
Application (v1.1):
- Generic N-node architecture replacing hardcoded gNB/UE structure
- Node-based JSON config with per-node shared memory paths
- Thread pool with barrier sync for multi-node recording
- Frame/slot-based message grouping in read_and_store_tracer_messages()
- Robust cleanup via atexit and signal handlers
- Remove v1.0 legacy application

Shared Memory Interface (new):
- System V shared memory attach/detach/remove with ftok-based keys
- Structured binary read/write for tracer control messages
- Frame/slot-aware data reading with configurable timeout

Config Interface:
- JSON-based config with node definitions (id, type, shared_mem_config)
- Per-node tracer message parsing and index resolution

SigMF Interface:
- Timestamp refactor: Unix epoch sec/nsec with nanosecond precision
- Wireless parameter mapping via YAML-driven parameter map
- System components metadata (TX/channel/RX) in SigMF annotations

Sync Service:
- Generic multi-node sync replacing 2-node sync
- Frame-wrap-aware comparison (1024-frame wrap)

Wireless Parameters Mapper (new):
- YAML-driven OAI-to-SigMF parameter mapping
- 5G NR metadata derivation (DMRS, modulation, link direction)
2026-04-24 10:31:38 +02:00
abgaber
46a4097f6a T-Tracer: update shared memory sizing for 5 messages/slot
Set NUM_MESSAGES_PER_SLOT=5, 100 slots buffer depth (~234 MiB).
Add separate project IDs and ftok paths for gNB and UE.
2026-04-24 10:31:38 +02:00
abgaber
b242eb3093 T-Tracer gNB and UE: massive enhancements
- refactor timestamp handling
- Replace string-based SENDING_TIME with integer SEC/NSEC fields.
- Designed the shared memory protocol with a 4-state machine (STATE_WAIT=0, STATE_CONFIG=1, STATE_RECORD=2, STATE_STOP=3) for command/control between the recording app and the T-Tracer service
- Implemented two shared memory segments: one for reading commands (addr_rd) and one for writing captured data (addr_wr)
- Created event_trace_msg_ul_data struct mapping all 27 UL metadata fields (frame, slot, datetime, MCS, DMRS parameters, etc.) by name from the T-Tracer database
- Implemented setup_trace_msg_ul_data() using the G() macro to map field names to indices at startup
- Wrote the main event loop using poll() to avoid busy-waiting, with get_event() to receive T-Tracer events from the softmodem socket
2026-04-24 10:31:38 +02:00
abgaber
d40f56ddd2 PHY/UE: replace inline T() calls with log_ul_scrambled_tx_bits wrapper
Remove redundant T_ACTIVE() guard and old timestamp formatting.
2026-04-24 10:31:38 +02:00
abgaber
6cd797c64e PHY/UE: replace inline T() calls with log_ul_payload_tx_bits wrapper
Remove redundant T_ACTIVE() guard and old timestamp formatting.
2026-04-24 10:31:38 +02:00
abgaber
5558f98154 PHY/gNB: replace inline T() calls with T_messages_creator wrappers
Use log_ul_fd_dmrs, log_ul_fd_pusch_iq, log_ul_fd_chan_est_dmrs_pos,
log_ul_fd_chan_est_dmrs_interpl, log_ul_payload_rx_bits.
Remove redundant T_ACTIVE() guards and old timestamp formatting.
2026-04-24 10:31:38 +02:00
abgaber
3f5520d792 PHY: remove deprecated log_tools.c/.h
Timestamp formatting moved into T_messages_creator wrappers.
2026-04-24 10:31:38 +02:00
abgaber
c70ed20ca7 T-Tracer: update trace message definitions with Unix timestamp fields
Sending time field of T() is used as a time stamp
2026-04-24 10:31:38 +02:00
abgaber
80ad56af63 T-Tracer: add T_messages_creator.c to T library build 2026-04-24 10:31:38 +02:00
abgaber
b75ddf0e85 T-Tracer: add T_messages_creator wrapper functions for UL PHY trace logging
Introduce T_messages_creator.h/.c with 7 modular wrapper functions
(log_ul_fd_dmrs, log_ul_fd_pusch_iq, log_ul_fd_chan_est_dmrs_pos,
log_ul_fd_chan_est_dmrs_interpl, log_ul_payload_rx_bits,
log_ul_payload_tx_bits, log_ul_scrambled_tx_bits) that encapsulate
T() macro calls with structured PHY metadata fields.
Replaces scattered inline T() calls across PHY source files.
2026-04-24 10:31:38 +02:00
Bartosz Podrygajlo
b5aa2d87fb fix(radio): Make write_reorder context thread-safe 2026-04-24 06:49:20 +02:00
Jaroslava Fiedlerova
430e336e15 Merge remote-tracking branch 'origin/ctest-physim-sanitizers' into integration_2026_w17 (!4069)
ctest: fix tests when compiling with sanitizers, ignore build directories,
fix spsc_q build

- Fix the directory to the current build directory to enable running with
  sanitizers with ctest
- add build* to .gitignore to close #1074
- fix spsc_q compilation on non-C++23 compilers to close #1073
2026-04-24 00:23:42 +02:00
Aswanth KC
ef0b4460b6 Config: Creating New Array members via command line arguments
Before this change, if you wanted to use --rfsimulator.[0].serveraddr server
on the command line but if you had no rfsimulator block in your config file,
it would just ignore it and print:
[CONFIG] unknown option: --rfsimulator.[0].serveraddr
[CONFIG] unknown option: server

This is because the config module only processes array members that already
exist in the config file. If the array is empty, the command args are never checked.

What I changed is, in config_getlist() in config_userapi.c, after it finishes
processing existing array parameters, it checks whether a new parameter is given
in CLI.

The array number in the parameter(num) should match with the valid index of
that specific array

--cfg.[0].value 1 creates one array with one element
valid_index = num = 1

--cfg.[0].value 1 --cfg.[0].value 2 creates an array with two elements
if the previous CLI number repeats increments the "num"
valid_index = num = 2

--cfg.[1].value 1 --cfg.[0].value 2 error
valid_index < num, expects 0.
2026-04-23 17:45:34 +00:00
Teodora Vladić
3afda6aea1 Add a warning for F release support
Similarly to: https://gitlab.eurecom.fr/oai/openairinterface5g/-/merge_requests/3936/diffs#57257e70cd9cf83d562fe56bd6d173cb45785f74_466_422
2026-04-23 18:42:25 +02:00
Teodora Vladić
7af713a1d1 Remove polling support for fronthaul interface
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
9a2a313515 Make debug logging for PRACH nRxPkt = 0
This commit was tested with MicroAmp FR2 with 2x2 MIMO,
where PRACH Rx antennas used is 1.

OAI L1 sets the number of PRACH antennas to be the same as for PUSCH.
Ideally, this should be properly fixed. In the meantime, I modified the
logging to debug to reduce noise.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Jaroslava Fiedlerova
9fb78e1b34 Install libatomic in FHI72 gNB images
libatomic is required by DPDK 24.11.4

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
82f57789a5 Revert "feat(FHI72): Fix lcore count on Arm targets"
This reverts commit 8d78c97513.

Not relevant for DPDK >= 24.
2026-04-23 18:42:25 +02:00
Romain Beurdouche
c5e1c4f34f feat(CI): Build FHI with K release
Update the dockerfiles for building DPDK 24.11.4 and xran K release.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
b92936f7d7 Move the Tx/Rx counters logging in one function
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
84a1eb8b07 Reverse the Rx processing order
First process PUSCH and then PRACH because the PUSCH callback
is called before PRACH callback.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
d06bd3d49f Separate PRACH and PUSCH packet processing
* rename read_prach_data() to xran_rx_prach_read_slot()
* in F release, there is no PRACH queue so `frame` and `slot` are
  passed as input; in K release, `frame` and `slot` are taken from the PRACH
queue

Note: since the PUSCH callback is the called before PRACH callback, this
commit is correct but the timing for PRACH processing is just a little
bit late. In previous state, PRACH processing was done inside the
function of PUSCH processing.
=> next commit will just reverse the order.

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
1bc8e64641 Process PRACH packets
* Move PRACH callback in the oaioran.h/c since now used in K release.
* Reset the number of PRACH packets.
* Handle PRACH queue in the same manner as for PUSCH (95c8fa46).

Co-authored-by: Romain Beurdouche <romain.beurdouche@eurecom.fr>
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Romain Beurdouche
6b665a851e Process PUSCH packets
xran K release introduces a new parameter nRxPkt describing the
fragmentation of Rx symbols.

Co-authored-by: Teodora Vladić <teodora.vladic@openairinterface.org>
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Romain Beurdouche
ba4239653e feat(oran_fhlib_5g): RX error counters
K releases added RX error counters
This commits adds a printing of these counters with LOG_I

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:25 +02:00
Teodora Vladić
7b7afae05b feat(oran_fhlib_5g): Changes to run K release
* Initialize leak detector
* Fill IO config field holding the number of mbufs to be allocated
* Allocate and fill `activeMUs` in the FH config
* Start timing source, worker thread and activate CCs upon starting the FH
* Fill `mu_number[0]` in FH config
* Invert trx_oran end and stop
* Set `neAxcUl`
* Delete duplicated oran_eth_state_t struct definition
* gxran_handle is an array which length is equal to the number of RUs
* Fill BBDev VF token in fh_init

Co-authored-by: Romain Beurdouche <romain.beurdouche@eurecom.fr>
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 18:42:13 +02:00
Robert Schmidt
74bc9bf0cc spsc_q: fix use of stdatomic.h
stdatomic.h is a C++23 feature [1]. Thus, "older" compilers such as
gcc-11 (default in Ubuntu 22) do not know this, and can therefore not
correctly compile spsc_q. Fix this by alternatively including atomic for
C++.

Fixes errors such as

    stdatomic.h:40:9: error: ‘_Atomic’ does not name a type

[1] https://en.cppreference.com/cpp/header/stdatomic.h

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-23 14:31:51 +02:00
Robert Schmidt
b0f63bf83c .gitignore: ignore build directories
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-23 13:57:48 +02:00
Robert Schmidt
d4f039a326 ctest: fix tests when compiling with sanitizers
When compiling with sanitizers that require LD_LIBRARY_PATH (see
documentation), ctest could not drive tests as the executables did not
find shared objects. Fix the directory to the current build directory to
enable running with sanitizers.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-23 13:56:16 +02:00
Jaroslava Fiedlerova
0ecfd11d92 Merge remote-tracking branch 'origin/type0dlsch' into integration_2026_w17 (!3762)
L1 gNB type0 PDSCH

PDSCH type0 allows frequency allocation via PRB bitmap (type1 is via
start and number of PRBs) allowing for non contiguous allocation in
frequency domain
2026-04-23 10:19:40 +02:00
Teodora Vladić
77bb143b04 feat(oran_fhlib_5g): Adapt oran_fhlib_5g for K release
Adapt the oran_fhlib_5g code to compile with xran K release.
Like previously E and F, uses preprocessor directives to separate codes for different xran releases.

Notes:
1. Release K of the FHI library supports many numerologies on one instance.
   As of now the integration supports only one numerology.
2. Several arrays in the xran interface are indexed by the numerology.
   It was ambiguous whether they were indexed by the actual numerology number or by the index of the relevant numerology within the numerology array.
   They are actually indexed by the numerology number so that when one is using only numerlogy 1 for example, he should only use perMu[1], fftSize[1], ...

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 10:06:26 +02:00
Teodora Vladić
e7c4d80a5d Create a cmake API for O_RAN SC xran forked repo
* create the xran forked repo as a cmake external project - easier to maintain and track the changes
  we applied to the original repo
* add xran_DOWNLOAD option - clones and builds latest supported K release
* update the FHI README for K release

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-23 10:06:14 +02:00
Jaroslava Fiedlerova
6b437c5187 Merge remote-tracking branch 'origin/add-asn1c-local-possibility' into integration_2026_w17 (!3988)
asn1c inside local tree

when /opt/asn1c/bin/asn1c does not exist, we download and use the right
asn1c version locally in the build tree. This simplifies dependancies
management
2026-04-23 09:55:14 +02:00
Jaroslava Fiedlerova
66a3328b81 Merge remote-tracking branch 'origin/fix-ho-required-no-up' into integration_2026_w17 (!4055)
fix (RRC): do not trigger N2 HO when no active PDU sessions are present

Prevent triggering N2 handover when the UE has no active PDU session by
adding an early check in nr_rrc_trigger_n2_ho(), so we avoid building an
invalid HO Required. This aligns with 3GPP TS 38.413 (§9.2.3.1), where
HANDOVER REQUIRED carries a PDU Session Resource List with at least one
PDU Session Resource Item (range 1..maxnoofPDUSessions).

Relates to #1062 (closed)
2026-04-23 09:54:17 +02:00
Jaroslava Fiedlerova
02f7ecf861 Merge remote-tracking branch 'origin/fix-csirs-estimation' into integration_2026_w17 (!4036)
Fix CSI-RS estimation

Changed channel estimates buffer to store RB 0 from start of buffer instead
of FFT shift offset. This simplifies channel estimation implementation. There
is also no need to add padding because each symbol/antenna start from offset
0 and/or OFDM symbol size always 32 byte aligned.

This also fixes buffer overflow detected when running UE with OCUDU gNB.
2026-04-23 09:44:45 +02:00
francescomani
fb60743b26 remove unused defines in nfapi_nr_interface_scf
Signed-off-by: francescomani <email@francescomani.it>
2026-04-23 09:09:54 +02:00
francescomani
6539889821 remove unnecessary nfapi_nr_interface_scf dependence in nr_mac_common_tdd
Signed-off-by: francescomani <email@francescomani.it>
2026-04-23 09:04:35 +02:00
Sakthivel Velumani
2c34d84d6f phy: fix CSI-RS estimation
Changed channel estimates buffer to store RB 0 from start of buffer instead of
FFT shift offset. This simplifies channel estimation implementation. There is
also no need to add padding because each symbol/antenna start from offset 0
and/or OFDM symbol size always 32 byte aligned.

This also fixes buffer overflow detected when running UE with OCUDU gNB.
2026-04-23 03:04:39 +00:00
Bartosz Podrygajlo
aa37846454 ZMQ radio 2x2 testcase 2026-04-22 22:34:02 +02:00
Bartosz Podrygajlo
ef632db840 Add ZMQ radio to OAI build scripts 2026-04-22 22:34:02 +02:00
Bartosz Podrygajlo
384886ce48 Ensure trx_stop is called before trx_end
Make sure the radio libs are stopped before `end()` call. This allows
for a cleaner exit under most circumstances. Some libraries like zmq_radio
don't exit cleanly without it.
2026-04-22 22:34:02 +02:00
Bartosz Podrygajlo
9ef4efe10a Fix memory leak in unit tests 2026-04-22 22:34:02 +02:00
Bartosz Podrygajlo
46c120651f ZMQ radio
This commit introduces ZMQ-based radio library. Each pair of RX/TX antennas is
represented by a ZMQ REQ/REP socket pair which streams continuous IQ samples
from radio start until stop.

Usage:
Simplest configuration is to connect OAI NR UE to OAI GNB with the same number
of antennas - by inverting the RX and TX channels in ZMQ radio configuration the
gNBs TX is mapped to UEs RX antennas and vice versa.
2026-04-22 22:34:02 +02:00
Jaroslava Fiedlerova
9b0b9dc066 Merge remote-tracking branch 'origin/minor-fixes-ngap-lib' into integration_2026_w17 (!4062)
Minor fixes/cleanup in NGAP lib

This MR is about a small NGAP-focused cleanup, mainly to reduce clang
warnings and remove dead code.
- Handle unused input parameters in ngap_gNB.c and ngap_gNB_handlers.c.
- Fix clang warning in common/utils/time_meas.h (extra semicolon).
- Removes unused NGAP trace/overload code and related references
  (ngap_gNB_trace, ngap_gNB_overload).
2026-04-22 13:22:59 +02:00
Jaroslava Fiedlerova
be74bfb437 Merge remote-tracking branch 'origin/fixes-rfsim-testing' into integration_2026_w17 (!4054)
Minor fixes to gNB/UE behavior in RFsim

While testing with RFsim thw following issues where found:
- gNB config: wrong PCI was printed during F1 Setup Request preparation
- UE PHY: missing rxdata allocation was causing a crash during intra-freq
  measurements
2026-04-21 18:16:42 +02:00
Guido Casati
5902635d2d Remove unused ret variable in ngap_gNB_generate_ng_setup_request 2026-04-21 18:05:08 +02:00
Guido Casati
4a36f46a38 NGAP: remove unnecessary void * cast in ngap_gNB_task 2026-04-21 17:59:43 +02:00
Guido Casati
7f6e5ff38e Fix clang warning: unused parameters in ngap_gNB.c
- Fixed [-Wunused-parameter] warnings by adopting UNUSED macro
2026-04-21 17:49:21 +02:00
Guido Casati
711c632001 Cleanup dead NGAP code
The goal of this commit is to keep the NGAP library tidy and
remove all handlers that are not currently implemented and other
dead code.
2026-04-21 17:47:13 +02:00
Guido Casati
f4c4e02d78 Fix clang warning: sign comparison in ngap_gNB_handlers.c
- Fixed [-Wsign-compare] warning by casting sizeof result to long
- Resolves comparison between signed NGAP_ProcedureCode_t and unsigned size_t
2026-04-21 17:47:13 +02:00
Guido Casati
ca31590282 Fix clang warning: unused parameters in ngap_gNB_handlers.c
- Fixed [-Wunused-parameter] warnings by adopting UNUSED macro
2026-04-21 17:46:53 +02:00
Guido Casati
210b9089b9 Fix clang warning: remove extra semicolon in time_meas.h line 36
- Fixed [-Wextra-semi] warning by removing duplicate semicolon
2026-04-21 17:39:57 +02:00
abgaber
a12050203b Memory optimization for Data Recording and Move tracers log messages to separate function 2026-04-21 16:47:10 +02:00
abgaber
c3b2b4d30e Trace DMRS and Channel Coff of layer one 2026-04-21 16:43:15 +02:00
abgaber
a2230079d8 Remove non-used package by Data recording App after update 2026-04-21 16:40:18 +02:00
Romain Beurdouche
5b1f09faf1 feat(oran_fhlib_5g): update Findxran
Update the Cmake find script for xran to detect K release (11.1.1).

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-21 12:11:42 +02:00
Robert Schmidt
57c4633fd6 Take the long PRACH into account in queues
Fixes the commit ID: 03096dd495

Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-04-21 12:11:35 +02:00
francescomani
644ba5cdcf set last bit of frequency_domain_assignment.val only if there is more than 1 2026-04-20 19:15:27 +02:00
francescomani
1b7be0c35a fixes at UE to make number of RBs independent from PDSCH type 2026-04-20 19:15:27 +02:00
francescomani
6bb7e3cb68 fix setting number of RBs in TBS computation for PDSCH scheduled by DCI11 at UE 2026-04-20 19:15:27 +02:00
francescomani
29457abe71 reworking the handling of freq_alloc_bitmap_t to make it more efficient
Co-Authored-By: @schmidtr
Assisted-by: Gemini:3 Flash
2026-04-20 19:15:27 +02:00
francescomani
0dd1243e6e L1 gNB type0 PDSCH 2026-04-18 09:48:40 +02:00
francescomani
27158b9244 rename reverse_bits to bits and move functions to count bits in there 2026-04-18 09:48:39 +02:00
Guido Casati
389d7a0246 fix (RRC): do not trigger N2 HO when no active PDU sessions are present
Prevent triggering N2 handover when the UE has no active PDU session
by adding an early check in nr_rrc_trigger_n2_ho(), so we avoid building
an invalid HO Required. This aligns with 3GPP TS 38.413 (§9.2.3.1), where
HANDOVER REQUIRED carries a PDU Session Resource List with at least one
PDU Session Resource Item (range 1..maxnoofPDUSessions).
2026-04-17 16:46:26 +02:00
Jaroslava Fiedlerova
547f683ebe CI: Remove retx check from OTA setups with Quectel
Retx check frequently fails in OTA pipelines, particularly for interfering
UEs attempting to connect to the cell. This leads to job failures in OTA
pipelines.

Remove the retx check from  Multi-Antenna and Aerial pipelines to improve
CI stability and avoid false failures. In these pipelines, iperf test
already validates correct funtionality, making the retx check redundant.
2026-04-17 14:09:22 +02:00
Guido Casati
32cb3401b4 fix (gNB config): cell ID and PCI from outgoing F1 Setup Request 2026-04-16 15:07:47 +02:00
Guido Casati
f6b6ce2da3 fix (NR UE): allocate/free rxdata pointer for neighbor measurements
Fix a UE crash in the neighbor-measurement task setup by allocating the per-antenna
rxdata pointer array before indexing it in pbch_processing(), and freeing it in
nr_ue_meas_neighboring_cell() after measurements complete.

The crash is triggered by intra-frequency neighbor measurements.
2026-04-16 15:01:13 +02:00
Laurent THOMAS
3a935bc9f7 When asn1c is not found, allow to download and use asn1c in build tree
This simplifies dependancies management

Co-authored-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-04-16 09:34:35 +02:00
Merkebu Girmay
58f00921c8 Add AoA selection to vrtsim in-process CIRDB path 2026-04-15 11:19:55 -07:00
288 changed files with 15311 additions and 7306 deletions

1
.gitignore vendored
View File

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

View File

@@ -83,25 +83,40 @@ 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")
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
")
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)
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 #
#########################################################
@@ -420,7 +435,6 @@ 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
)
@@ -868,7 +882,6 @@ 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
@@ -1123,12 +1136,20 @@ set(NR_PDCP_SRC
${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_integrity_nia2.c
${OPENAIR2_DIR}/LAYER2/nr_pdcp/nr_pdcp_integrity_nia1.c
${OPENAIR2_DIR}/LAYER2/nr_pdcp/asn1_utils.c
)
# gNB: PDCP + CU-CP/CU-UP interface
set(NR_PDCP_SRC_GNB
${NR_PDCP_SRC}
openair2/LAYER2/nr_pdcp/cucp_cuup_handler.c
openair2/LAYER2/nr_pdcp/cuup_cucp_if.c
openair2/LAYER2/nr_pdcp/cuup_cucp_direct.c
openair2/LAYER2/nr_pdcp/cuup_cucp_e1ap.c
)
# UE build: PDCP only; CU-CP/CU-UP interface is gNB-only.
set(NR_PDCP_SRC_UE ${NR_PDCP_SRC})
set(NR_SDAP_SRC
${OPENAIR2_DIR}/SDAP/nr_sdap/nr_sdap.c
${OPENAIR2_DIR}/SDAP/nr_sdap/nr_sdap_entity.c
@@ -1172,7 +1193,7 @@ set(L2_LTE_SRC
)
set(L2_NR_SRC
${NR_PDCP_SRC}
${NR_PDCP_SRC_GNB}
${NR_SDAP_SRC}
${NR_RRC_DIR}/rrc_gNB.c
${NR_RRC_DIR}/mac_rrc_dl_direct.c
@@ -1208,7 +1229,7 @@ set(L2_RRC_SRC_UE
)
set(NR_L2_SRC_UE
${NR_PDCP_SRC}
${NR_PDCP_SRC_UE}
${NR_SDAP_SRC}
${NR_UE_RRC_DIR}/L2_interface_ue.c
${NR_UE_RRC_DIR}/main_ue.c
@@ -1352,6 +1373,7 @@ add_library(e1_if
target_link_libraries(e1_if PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs asn1_f1ap SECURITY ${OPENSSL_LIBRARIES} e1ap GTPV1U)
target_link_libraries(L2_NR PRIVATE f1ap x2ap s1ap ngap nr_rrc e1ap nr_rlc nr_common)
target_compile_definitions(L2_NR PRIVATE PDCP_CUCP_CUUP)
if(OAI_AERIAL)
target_compile_definitions(L2_NR PRIVATE ENABLE_AERIAL)
endif()
@@ -1885,7 +1907,7 @@ add_executable(nr-cuup
executables/nr-cuup.c
${NR_RRC_DIR}/rrc_gNB_UE_context.c
${OPENAIR2_DIR}/E1AP/e1ap_setup.c
${NR_PDCP_SRC}
${NR_PDCP_SRC_GNB}
${NR_SDAP_SRC}
)
@@ -1896,6 +1918,7 @@ target_link_libraries(nr-cuup PRIVATE
alg
dl pthread ${T_LIB})
target_link_libraries(nr-cuup PRIVATE asn1_lte_rrc_hdrs asn1_nr_rrc_hdrs)
target_compile_definitions(nr-cuup PRIVATE PDCP_CUCP_CUUP)
if(E2_AGENT)
target_link_libraries(nr-cuup PRIVATE e2_agent e2_agent_arg e2_ran_func_cuup)
target_compile_definitions(nr-cuup PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)

View File

@@ -1,6 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# Contributing to OpenAirInterface #
# Contributing to OpenAirInterface
We want to make contributing to this project as easy and transparent as possible.
@@ -11,10 +11,10 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
if you do not have any.
- We recommend that you register with a professional or student email address.
- If your email domain (`@domain.com`) is not whitelisted, please contact us
(mailto:contact@openairinterface.org).
(mailto:oaicicdteam@openairinterface.org).
- Eurecom GitLab does NOT accept public email domains.
3. Provide the OAI team with the **username** of this account to
(mailto:contact@openairinterface.org) ; we will give you the developer
(mailto:oaicicdteam@openairinterface.org) ; we will give you the developer
rights on this repository.
4. The contributing policies are described in the [corresponding documentation
page](doc/code-style-contrib.md).
@@ -24,8 +24,43 @@ Please refer to the steps described on our website: [How to contribute to OAI](h
request from a forked repository.
* This decision was made for the license reasons.
* The Continuous Integration will reject your merge request.
5. Mandatory signing of all the commits using the email address used for CLA.
# License #
## Commit Guidelines
### Signing Commits
To sign commits:
You can also get the verified label
on your commits via using [SSH KEYS or GPG KEYS](https://docs.gitlab.com/user/project/repository/signed_commits/)
```
# Edit .git/config in the git repository you are working on
# Add the user section
[user]
name = YOUR NAME
email = YOUR EMAIL ADDRESS
# If you use a signing key, use the below configuration instead
[user]
name = YOUR NAME
email = YOUR EMAIL ADDRESS
signingkey = LOCATION OF SSH KEYS or GPG KEY
[gpg]
format = ssh
[commit]
gpgsign = true
```
> **NOTE:** If your commits are not signed the CI framework will not accept the MR.
For more information regarding contribution guidelines
please check [this document](doc/code-style-contrib.md)
## License
By contributing to OpenAirInterface, you agree that your contributions will be
licensed under

8
NOTICE
View File

@@ -84,3 +84,11 @@ 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

@@ -89,6 +89,21 @@ pipeline {
echo "GitLab Usermail is ${gitCommitAuthorEmailAddr}"
// GitLab-Jenkins plugin integration is lacking to perform the merge by itself
// Doing it manually --> it may have merge conflicts
// Validate MR commits: checks for missing Signed-off-by and merge commits
def mrValidationLog = "signedCommit_${env.BUILD_NUMBER}.log"
def mrValidationExitCode = sh(
script: "bash ./ci-scripts/pre-ci-check.sh -s origin/${env.gitlabSourceBranch} -t origin/${env.gitlabTargetBranch} > ${mrValidationLog} 2>&1",
returnStatus: true
)
def mrValidationMessage = readFile(mrValidationLog).trim()
sh "rm -f ${mrValidationLog}"
if (mrValidationExitCode >= 1) {
addGitLabMRComment comment: "${mrValidationMessage}"
}
if (mrValidationExitCode >= 2) {
error("${mrValidationMessage}")
}
sh "./ci-scripts/doGitLabMerge.sh --src-branch ${env.gitlabSourceBranch} --src-commit ${env.gitlabMergeRequestLastCommit} --target-branch ${env.gitlabTargetBranch} --target-commit ${GIT_COMMIT}"
} else {
echo "Git Branch is ${GIT_BRANCH}"
@@ -748,6 +763,29 @@ pipeline {
}
}
}
stage ("SA-FHI72-MPLANE-CN5G") {
when { expression {do5Gtest} }
steps {
script {
triggerSlaveJob ('RAN-SA-FHI72-MPLANE-CN5G', 'SA-FHI72-MPLANE-CN5G')
}
}
post {
always {
script {
// Using a unique variable name for each test stage to avoid overwriting on a global variable
// due to parallel-time concurrency
saFHI72MplaneStatus = finalizeSlaveJob('RAN-SA-FHI72-MPLANE-CN5G')
}
}
failure {
script {
currentBuild.result = 'FAILURE'
failingStages += saFHI72MplaneStatus
}
}
}
}
stage ("SA-Handover-CN5G") {
when { expression {do5Gtest} }
steps {

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -79,11 +79,19 @@ oc-cn5g:
oc-cn5g-20897:
Host: cacofonix
NetworkScript: echo "inet 172.21.6.105"
NetworkScript: echo "inet 172.21.6.115"
RunIperf3Server: False
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%%"
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%%"
oc-cn5g-00105:
Host: cacofonix
NetworkScript: echo "inet 172.21.6.118"
RunIperf3Server: False
Deploy: "! scripts/oc-cn5g-deploy.sh /opt/oai-cn5g-fed-develop-2026-apr-00105 oaicicd-core-for-fhi72"
Undeploy: "! scripts/oc-cn5g-undeploy.sh /opt/oai-cn5g-fed-develop-2026-apr-00105 oaicicd-core-for-fhi72"
LogCollect: "! scripts/oc-cn5g-logcollect.sh /opt/oai-cn5g-fed-develop-2026-apr-00105 oaicicd-core-for-fhi72 %%log_dir%%"
oc-cn5g-00103-ho:
Host: groot
@@ -183,6 +191,16 @@ amarisoft_ue_2x2:
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_aw2s_asue_20MHz_2x2/aw2s-multi-00102-2x2-v2.cfg &
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
NetworkScript: ip netns exec ue1 ip a show dev pdn0
amarisoft_00105_40MHz:
Host: amariue
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_fhi72_mplane_40MHz/multi-00105-40.cfg &
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
NetworkScript: ip netns exec ue1 ip a show dev pdn0
amarisoft_00105_100MHz:
Host: amariue
InitScript: /root/lteue-linux-2025-03-15/lteue /root/oaicicd/ran_sa_fhi72_mplane_100MHz/multi-00105-100.cfg &
TermScript: /root/lteue-linux-2025-03-15/ws.js -t 10 127.0.0.1:9002 '{"message":"quit"}' || killall -KILL lteue-avx2
NetworkScript: ip netns exec ue1 ip a show dev pdn0
amarisoft_ue_1:
Host: amariue
AttachScript: /root/lteue-linux-2025-03-15/ws.js 127.0.0.1:9002 '{"message":"power_on","ue_id":1}'

View File

@@ -648,7 +648,7 @@ class Containerize():
raise ValueError(f'Invalid value for num_attempts: {num_attempts}, must be greater than 0')
for attempt in range(num_attempts):
logging.info(f'will start services {services}')
status = ssh.run(f'docker compose -f {wd_yaml} up -d --wait --wait-timeout 60 -- {services}')
status = ssh.run(f'docker compose -f {wd_yaml} up -d --wait --wait-timeout 120 -- {services}')
info = ssh.run(f"docker compose -f {wd_yaml} ps --all --format=\'table {{{{.Service}}}} [{{{{.Image}}}}] {{{{.Status}}}}\' -- {services} | column -t")
deployed = status.returncode == 0
if not deployed:

View File

@@ -65,6 +65,23 @@ def Iperf_ComputeTime(args):
raise Exception('Iperf time not found!')
return int(result.group('iperf_time'))
def Iperf_UpdateBindPort(opts, ueIP, idx):
# search if bind address present. Extract if yes, add if no.
bind_m = re.search(r'(-B|--bind)\s+(?P<ip>\d+\.\d+\.\d+\.\d+)', opts)
if bind_m:
bindIP = bind_m.group('ip')
else:
bindIP = ueIP
opts += f" -B {ueIP}"
# search if port present. Extract if yes, add if no.
port_m = re.search(r'(-p|--port)\s+(?P<port>\d+)', opts)
if port_m:
port = port_m.group('port')
else:
port = 5002 + idx
opts += f" -p {port}"
return bindIP, port, opts
def convert_to_mbps(value, magnitude):
value = float(value)
if magnitude == 'K' or magnitude == 'k':
@@ -457,7 +474,6 @@ class OaiCiTest():
svrIP = cn.getIP()
if not svrIP:
return (False, f"Iperf server {cn.getName()} has no IP address")
iperf_opt = self.iperf_args
jsonReport = "--json"
serverReport = ""
@@ -471,11 +487,11 @@ class OaiCiTest():
# note: enable server report collection on the UE side, no need to store and collect server report separately on the server side
serverReport = "--get-server-output"
iperf_time = Iperf_ComputeTime(self.iperf_args)
bindIP, port, iperf_opt = Iperf_UpdateBindPort(iperf_opt, ueIP, idx)
# hack: the ADB UEs don't have iperf in $PATH, so we need to hardcode for the moment
iperf_ue = '/data/local/tmp/iperf3' if re.search('adb', ue.getName()) else 'iperf3'
ue_header = f'UE {ue.getName()} ({ueIP})'
ue_header = f'UE {ue.getName()} ({bindIP})'
with cls_cmd.getConnection(ue.getHost()) as cmd_ue, cls_cmd.getConnection(cn.getHost()) as cmd_svr:
port = 5002 + idx
# note: some core setups start an iperf3 server automatically, indicated in ci_infra by runIperf3Server: False`
t = iperf_time * 2.5
cmd_ue.run(f'rm {client_filename}', reportNonZero=False, silent=True)
@@ -486,7 +502,9 @@ class OaiCiTest():
if ret.returncode == 0:
logging.warning(f'Iperf3 server on port {port} detected and terminated')
cmd_svr.run(f'{cn.getCmdPrefix()} timeout -vk3 {t} iperf3 -s -B {svrIP} -p {port} -1 {jsonReport} >> /dev/null &', timeout=t)
cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -B {ueIP} -c {svrIP} -p {port} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t)
client_ret = cmd_ue.run(f'{ue.getCmdPrefix()} timeout -vk3 {t} {iperf_ue} -c {svrIP} {iperf_opt} {jsonReport} {serverReport} -O 5 >> {client_filename}', timeout=t, reportNonZero=False)
if client_ret.returncode != 0:
return (False, f'{ue_header}\nIperf client command failed on {bindIP} -> {svrIP}:{port} (return code: {client_ret.returncode})')
dest_filename = archiveArtifact(cmd_ue, ctx, client_filename)
if udpIperf:
status, msg = Iperf_analyzeV3UDP(dest_filename, self.iperf_bitrate_threshold, self.iperf_packetloss_threshold, target_bitrate)

View File

@@ -215,6 +215,7 @@ 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 = 260; # 150;
pucch_TargetSNRx10 = 200; #200;
pucch_RSSI_Threshold = -270;
pusch_RSSI_Threshold = -260;
pusch_TargetSNRx10 = 290;
pucch_TargetSNRx10 = 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.103"; });
amf_ip_address = ({ ipv4 = "172.21.6.113"; });
NETWORK_INTERFACES :
{

View File

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

View File

@@ -0,0 +1,262 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 05; mnc_length = 2; snssaiList = ( { sst = 1; }); });
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pdsch_AntennaPorts_N1 = 2;
maxMIMO_layers = 4;
pusch_AntennaPorts = 4;
do_CSIRS = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 3350.01 MHz
# selected SSB frequency = 3349.92 MHz
absoluteFrequencySSB = 623328;
dl_frequencyBand = 78;
# frequency point A = 3330.93 MHz
dl_absoluteFrequencyPointA = 622062;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 106;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 28875; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 106;
pMax = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 28875;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 152;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 8;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 3;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -96;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
ssb_PositionsInBurst_Bitmap = 0x1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 5;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -10;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.21.6.116"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.150";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.150";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 250;
pucch_TargetSNRx10 = 200;
pusch_FailureThres = 1000;
}
);
L1s = (
{
tr_n_preference = "local_mac";
prach_dtx_threshold = 100;
pucch0_dtx_threshold = 10;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 12; # needs to match O-RU configuration
L1_rx_thread_core = 3;
L1_tx_thread_core = 4;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 4;
nb_rx = 4;
att_tx = 24;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 5;
sl_ahead = 10;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:c3:11.0"); # one VF can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("/opt/oai-gnb/etc/id_rsa.pub", "/opt/oai-gnb/etc/id_rsa");
du_addr = ("00:11:22:33:44:66");
vlan_tag = (3); # only one needed if one VF configured
ru_username = ("oranbenetel");
ru_ip_addr = ("192.168.81.4");
fh_config = ({
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};

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 = 200;
pusch_TargetSNRx10 = 100;
pucch_TargetSNRx10 = 200;
ul_prbblack_SNR_threshold = 10;
}

View File

@@ -0,0 +1,260 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
Active_gNBs = ( "gNB-OAI");
# Asn1_verbosity, choice in: none, info, annoying
Asn1_verbosity = "none";
gNBs =
(
{
////////// Identification parameters:
gNB_ID = 0xe00;
gNB_name = "gNB-OAI";
// Tracking area code, 0x0000 and 0xfffe are reserved values
tracking_area_code = 1;
plmn_list = ({ mcc = 001; mnc = 05; mnc_length = 2; snssaiList = ( { sst = 1; }); });
nr_cellid = 1;
////////// Physical parameters:
pdsch_AntennaPorts_XP = 2;
pusch_AntennaPorts = 2;
do_CSIRS = 1;
servingCellConfigCommon = (
{
#spCellConfigCommon
physCellId = 0;
# downlinkConfigCommon
#frequencyInfoDL
# center frequency = 3350.01 MHz
# selected SSB frequency = 3349.92 MHz
absoluteFrequencySSB = 623328;
dl_frequencyBand = 78;
# frequency point A = 3300.87 MHz
dl_absoluteFrequencyPointA = 620058;
#scs-SpecificCarrierList
dl_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
dl_subcarrierSpacing = 1;
dl_carrierBandwidth = 273;
#initialDownlinkBWP
#genericParameters
initialDLBWPlocationAndBandwidth = 1099; #38.101-1 Table 5.3.2-1
#
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialDLBWPsubcarrierSpacing = 1;
#pdcch-ConfigCommon
initialDLBWPcontrolResourceSetZero = 11;
initialDLBWPsearchSpaceZero = 0;
#uplinkConfigCommon
#frequencyInfoUL
ul_frequencyBand = 78;
#scs-SpecificCarrierList
ul_offstToCarrier = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
ul_subcarrierSpacing = 1;
ul_carrierBandwidth = 273;
pMax = 23;
#initialUplinkBWP
#genericParameters
initialULBWPlocationAndBandwidth = 1099;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
initialULBWPsubcarrierSpacing = 1;
#rach-ConfigCommon
#rach-ConfigGeneric
prach_ConfigurationIndex = 152;
#prach_msg1_FDM
#0 = one, 1=two, 2=four, 3=eight
prach_msg1_FDM = 0;
prach_msg1_FrequencyStart = 0;
zeroCorrelationZoneConfig = 0;
preambleReceivedTargetPower = -100;
#preamblTransMax (0...10) = (3,4,5,6,7,8,10,20,50,100,200)
preambleTransMax = 8;
#powerRampingStep
# 0=dB0,1=dB2,2=dB4,3=dB6
powerRampingStep = 3;
#ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR
#1=oneeighth,2=onefourth,3=half,4=one,5=two,6=four,7=eight,8=sixteen
ssb_perRACH_OccasionAndCB_PreamblesPerSSB_PR = 4;
#one (0..15) 4,8,12,16,...60,64
ssb_perRACH_OccasionAndCB_PreamblesPerSSB = 15;
#ra_ContentionResolutionTimer
#(0..7) 8,16,24,32,40,48,56,64
ra_ContentionResolutionTimer = 7;
rsrp_ThresholdSSB = 19;
#prach-RootSequenceIndex_PR
#1 = 839, 2 = 139
prach_RootSequenceIndex_PR = 2;
prach_RootSequenceIndex = 1;
# SCS for msg1, can only be 15 for 30 kHz < 6 GHz, takes precendence over the one derived from prach-ConfigIndex
#
msg1_SubcarrierSpacing = 1,
# restrictedSetConfig
# 0=unrestricted, 1=restricted type A, 2=restricted type B
restrictedSetConfig = 0,
# this is the offset between the last PRACH preamble power and the Msg3 PUSCH, 2 times the field value in dB
msg3_DeltaPreamble = 2;
p0_NominalWithGrant = -96;
# pucch-ConfigCommon setup :
# pucchGroupHopping
# 0 = neither, 1= group hopping, 2=sequence hopping
pucchGroupHopping = 0;
hoppingId = 0;
p0_nominal = -96;
ssb_PositionsInBurst_Bitmap = 0x1;
# ssb_periodicityServingCell
# 0 = ms5, 1=ms10, 2=ms20, 3=ms40, 4=ms80, 5=ms160, 6=spare2, 7=spare1
ssb_periodicityServingCell = 2;
# dmrs_TypeA_position
# 0 = pos2, 1 = pos3
dmrs_TypeA_Position = 0;
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
subcarrierSpacing = 1;
#tdd-UL-DL-ConfigurationCommon
# subcarrierSpacing
# 0=kHz15, 1=kHz30, 2=kHz60, 3=kHz120
referenceSubcarrierSpacing = 1;
# pattern1
# dl_UL_TransmissionPeriodicity
# 0=ms0p5, 1=ms0p625, 2=ms1, 3=ms1p25, 4=ms2, 5=ms2p5, 6=ms5, 7=ms10
dl_UL_TransmissionPeriodicity = 5;
nrofDownlinkSlots = 3;
nrofDownlinkSymbols = 6;
nrofUplinkSlots = 1;
nrofUplinkSymbols = 4;
ssPBCH_BlockPower = -11;
}
);
# ------- SCTP definitions
SCTP :
{
# Number of streams to use in input/output
SCTP_INSTREAMS = 2;
SCTP_OUTSTREAMS = 2;
};
////////// AMF parameters:
amf_ip_address = ({ ipv4 = "172.21.6.116"; });
NETWORK_INTERFACES :
{
GNB_IPV4_ADDRESS_FOR_NG_AMF = "172.21.16.150";
GNB_IPV4_ADDRESS_FOR_NGU = "172.21.16.150";
GNB_PORT_FOR_S1U = 2152; # Spec 2152
};
}
);
MACRLCs = (
{
tr_s_preference = "local_L1";
tr_n_preference = "local_RRC";
pusch_TargetSNRx10 = 200;
pucch_TargetSNRx10 = 200;
pusch_FailureThres = 1000;
}
);
L1s = (
{
tr_n_preference = "local_mac";
prach_dtx_threshold = 100;
pucch0_dtx_threshold = 10;
pusch_dtx_threshold = 10;
max_ldpc_iterations = 15;
tx_amp_backoff_dB = 12; # needs to match O-RU configuration
L1_rx_thread_core = 3;
L1_tx_thread_core = 4;
phase_compensation = 0; # needs to match O-RU configuration
}
);
RUs = (
{
local_rf = "no";
nb_tx = 2;
nb_rx = 2;
att_tx = 24;
att_rx = 0;
bands = [78];
max_pdschReferenceSignalPower = -27;
max_rxgain = 75;
sf_extension = 0;
eNB_instances = [0];
ru_thread_core = 5;
sl_ahead = 10;
tr_preference = "raw_if4p5"; # important: activate FHI7.2
do_precoding = 0; # needs to match O-RU configuration
}
);
security = {
# preferred ciphering algorithms
# the first one of the list that an UE supports in chosen
# valid values: nea0, nea1, nea2, nea3
ciphering_algorithms = ( "nea0" );
# preferred integrity algorithms
# the first one of the list that an UE supports in chosen
# valid values: nia0, nia1, nia2, nia3
integrity_algorithms = ( "nia2", "nia0" );
# setting 'drb_ciphering' to "no" disables ciphering for DRBs, no matter
# what 'ciphering_algorithms' configures; same thing for 'drb_integrity'
drb_ciphering = "yes";
drb_integrity = "no";
};
log_config :
{
global_log_level = "info";
hw_log_level = "info";
phy_log_level = "info";
mac_log_level = "info";
rlc_log_level = "info";
pdcp_log_level = "info";
rrc_log_level = "info";
ngap_log_level = "info";
f1ap_log_level = "info";
};
fhi_72 = {
dpdk_devices = ("0000:c3:11.0"); # one VF can be used as well
system_core = 0;
io_core = 1;
worker_cores = (2);
du_key_pair = ("/opt/oai-gnb/etc/id_rsa.pub", "/opt/oai-gnb/etc/id_rsa");
du_addr = ("00:11:22:33:44:66");
vlan_tag = (3); # only one needed if one VF configured
ru_username = ("oranbenetel");
ru_ip_addr = ("192.168.81.4");
fh_config = ({
T1a_cp_dl = (419, 470);
T1a_cp_ul = (285, 336);
T1a_up = (294, 345);
Ta4 = (0, 200);
});
};

View File

@@ -5,8 +5,8 @@ uicc0 = {
key = "fec86ba6eb707ed08905757b1bb44b8f";
opc= "C42449363BBAD02B66D16BC975D77CC1";
pdu_sessions = (
{ id = 1; dnn = "oai"; nssai_sst = 1; },
{ id = 2; dnn = "oai"; nssai_sst = 1; }
{ id = 1; dnn = "oai"; nssai_sst = 1; nssai_sd = 0xFFFFFF; },
{ id = 2; dnn = "openairinterface"; nssai_sst = 1; nssai_sd = 0x123456; }
)
}

View File

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

96
ci-scripts/pre-ci-check.sh Executable file
View File

@@ -0,0 +1,96 @@
#!/bin/bash
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
function usage {
echo "OAI GitLab MR validation script (Signed-off-by and merge commits)"
echo ""
echo "Usage:"
echo "------"
echo " $0 -s <source-branch> -t <target-branch>"
echo ""
echo "Options:"
echo "--------"
echo " -s"
echo " The source branch of the merge request. Default value is current Git Branch (HEAD)"
echo ""
echo " -t"
echo " The target branch of the merge request. Default value is develop"
echo ""
echo " -h"
echo " Print this help message."
echo ""
}
# Parse arguments properly
SOURCE_BRANCH=$(git rev-parse --abbrev-ref HEAD)
TARGET_BRANCH="origin/develop"
git fetch --quiet
while getopts ":s:t:h" opt; do
case "$opt" in
s)
SOURCE_BRANCH="$OPTARG"
;;
t)
TARGET_BRANCH="$OPTARG"
;;
h)
usage
exit 0
;;
:)
echo "Error: Option -$OPTARG requires a value."
echo ""
usage
exit 2
;;
\?)
echo "Error: Invalid option -$OPTARG"
echo ""
usage
exit 2
;;
esac
done
# ----------------------------
# Merged commits
# ----------------------------
mergeCommits=$(git rev-list --merges --abbrev-commit "$TARGET_BRANCH".."$SOURCE_BRANCH")
if [[ ! "$SOURCE_BRANCH" =~ ^(origin/)?integration_[0-9]{4}_w[0-9]{2}$ ]]; then
if [[ -n "$mergeCommits" ]]; then
message="> ERROR: Following merge commits are found in the source branch history. Please rebase your branch.\n>\n"
message+="> $(echo "$mergeCommits" | paste -sd ',' -)\n"
echo -e "$message"
exit 3
fi
fi
# ----------------------------
# Check unsigned commits
# ----------------------------
unsignedCommits=$(
for c in $(git rev-list "$TARGET_BRANCH".."$SOURCE_BRANCH"); do
if ! git log -1 --format=%B "$c" | grep -q "Signed-off-by:"; then
git log -1 --format='%h' "$c"
fi
done | paste -sd ","
)
# ----------------------------
# Report unsigned commits
# ----------------------------
message=""
if [ -n "$unsignedCommits" ]; then
message="> WARNING: The following commit(s) are missing a Signed-off-by:\n>\n> $unsignedCommits\n>\n"
message+="> Please use 'git commit -s' to sign your commits.\n>\n"
message+="> For detailed instructions, refer to the CONTRIBUTING file at the root of this repository."
echo -e "$message"
exit 1
else
message="> All commits are signed off using 'git commit -s'."
echo -e "$message"
exit 0
fi

View File

@@ -0,0 +1,48 @@
#!/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

@@ -0,0 +1,14 @@
#!/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

@@ -82,5 +82,17 @@ class TestPingIperf(unittest.TestCase):
success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file)
self.assertTrue(success)
def test_iperf_new_bindport(self):
self.ci.iperf_args = "-u -t 5 -b 22M -O 0 -p 10000 -B 127.0.0.3"
self.ci.svr_id = "test"
self.ci.svr_node = "localhost"
self.ci.iperf_packetloss_threshold = "0"
self.ci.iperf_bitrate_threshold = "0"
self.ci.iperf_profile = "balanced"
infra_file = "tests/config/infra_ping_iperf.yaml"
# TODO Should need nothing but options and UE(s) to use
success = self.ci.Iperf(self.ctx, self.node, self.html, infra_file=infra_file)
self.assertTrue(success)
if __name__ == '__main__':
unittest.main()

View File

@@ -41,6 +41,17 @@
<node>localhost</node>
</testCase>
<testCase>
<class>Ping</class>
<desc>UL 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 -i0.5</ping_args>
<ping_packetloss_threshold>80</ping_packetloss_threshold>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping NR-UE from ext-dn</desc>

View File

@@ -23,7 +23,7 @@
<desc>Deploy OAI 5G CoreNetwork</desc>
<node>localhost</node>
<yaml_path>ci-scripts/yaml_files/5g_rfsimulator_u0_25prb</yaml_path>
<services>mysql oai-amf oai-smf oai-upf oai-ext-dn</services>
<services>mysql oai-amf oai-smf oai-pcf oai-upf oai-ext-dn</services>
</testCase>
<testCase>
@@ -41,28 +41,6 @@
<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>Attach_UE</class>
<desc>Verify 2nd PDU session is up</desc>
@@ -71,14 +49,109 @@
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping ext-dn from NR-UE on PDU session ID 2</desc>
<class>Custom_Command</class>
<desc>Configure policy routing for PDU2 source IP</desc>
<may_fail>true</may_fail>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue sh -c "ip rule add from 12.1.2.2/32 table 1002 prio 1002; ip route replace default dev oaitun_ue1p2 src 12.1.2.2 table 1002"</command>
</testCase>
<testCase>
<class>Custom_Command</class>
<desc>Ping ext-dn from UE PDU session 1 interface</desc>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1 -c 10 192.168.72.135</command>
</testCase>
<testCase>
<class>Custom_Command</class>
<desc>Ping ext-dn from UE PDU session 2 interface</desc>
<node>localhost</node>
<command>docker exec rfsim5g-oai-nr-ue ping -I oaitun_ue1p2 -c 10 192.168.72.135</command>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 5201</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 52080</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.1.2 -p 52081</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (UL/1Mbps/UDP)(6 sec) default flow on PDU session 2 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -B 12.1.2.2 -p 5201</iperf_args>
<id>rfsim5g_ue_pdu_2</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>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 1 (default) flow on PDU session 1 (port 5201)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 5201</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 2 flow on PDU session 1 (port 52080)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 52080</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>Iperf (DL/1Mbps/UDP)(6 sec) QFI 3 flow on PDU session 1 (port 52081)</desc>
<iperf_args>-u -b 1M -t 6 -R -B 12.1.1.2 -p 52081</iperf_args>
<id>rfsim5g_ue</id>
<node>localhost</node>
<svr_id>rfsim5g_ext_dn</svr_id>
<svr_node>localhost</svr_node>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>10</iperf_bitrate_threshold>
</testCase>
<testCase>

View File

@@ -0,0 +1,109 @@
<!-- 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

@@ -0,0 +1,109 @@
<!-- 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

@@ -0,0 +1,109 @@
<!-- 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=RetxCheck=20,100,100,100 oai-gnb-aerial=EndsWithBye</services>
<services>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=RetxCheck=20,100,100,100 oai-gnb-aerial=EndsWithBye</services>
<services>oai-gnb-aerial=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -0,0 +1,149 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>test-5g-fhi72-benetel-2x2-100MHz-9b-mplane</htmlTabRef>
<htmlTabName>100 MHz 2x2 9b TDD SA Benetel M-plane</htmlTabName>
<htmlTabIcon>tasks</htmlTabIcon>
<testCase>
<class>DeployCoreNetwork</class>
<desc>Initialize 5G Core</desc>
<cn_id>oc-cn5g-00105</cn_id>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Configure system for low-latency RT performance</desc>
<node>cacofonix</node>
<script>yaml_files/sa_fhi_7.2_benetel550_2x2_100MHz_9b_mplane_gnb/setup_config.sh</script>
</testCase>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>cacofonix</node>
<images>oai-gnb-fhi72</images>
</testCase>
<testCase>
<class>Initialize_UE</class>
<desc>Initialize Amarisoft UE</desc>
<id>amarisoft_00105_100MHz</id>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<node>cacofonix</node>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy gNB (TDD/Band78/100MHz/Benetel) in a container</desc>
<node>cacofonix</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_benetel550_2x2_100MHz_9b_mplane_gnb</yaml_path>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach UE</desc>
<id>amarisoft_ue_1</id>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping: 100 pings in 10 sec</desc>
<id>amarisoft_ue_1</id>
<svr_id>oc-cn5g-00105</svr_id>
<ping_args>-c 100 -i 0.1</ping_args>
<ping_packetloss_threshold>1</ping_packetloss_threshold>
<ping_rttavg_threshold>15</ping_rttavg_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/250Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 250M -t 30 -R</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>balanced</iperf_profile>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/35Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 35M -t 30</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>balanced</iperf_profile>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/TCP)(30 sec)</desc>
<iperf_args>-t 30 -R</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_tcp_rate_target>40</iperf_tcp_rate_target>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/TCP)(30 sec)</desc>
<iperf_args>-t 30</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_tcp_rate_target>10</iperf_tcp_rate_target>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Detach_UE</class>
<always_exec>true</always_exec>
<desc>Detach UE</desc>
<id>amarisoft_ue_1</id>
</testCase>
<testCase>
<class>Terminate_UE</class>
<always_exec>true</always_exec>
<desc>Terminate UE</desc>
<id>amarisoft_00105_100MHz</id>
</testCase>
<testCase>
<class>IdleSleep</class>
<desc>Sleep</desc>
<idle_sleep_time_in_sec>5</idle_sleep_time_in_sec>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy gNB</desc>
<node>cacofonix</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_benetel550_2x2_100MHz_9b_mplane_gnb</yaml_path>
<analysis>
<services>oai-gnb=EndsWithBye</services>
</analysis>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>cacofonix</node>
<images>oai-gnb-fhi72</images>
</testCase>
<testCase>
<class>UndeployCoreNetwork</class>
<always_exec>true</always_exec>
<desc>Terminate 5G Core</desc>
<cn_id>oc-cn5g-00105</cn_id>
</testCase>
</testCaseList>

View File

@@ -0,0 +1,149 @@
<!-- SPDX-License-Identifier: LicenseRef-CSSL-1.0 -->
<testCaseList>
<htmlTabRef>test-5g-fhi72-benetel-40Mhz-4x4-9b-mplane</htmlTabRef>
<htmlTabName>40MHz 4x4 9b TDD SA Benetel M-plane</htmlTabName>
<htmlTabIcon>tasks</htmlTabIcon>
<testCase>
<class>DeployCoreNetwork</class>
<desc>Initialize 5G Core</desc>
<cn_id>oc-cn5g-00105</cn_id>
</testCase>
<testCase>
<class>Custom_Script</class>
<desc>Configure system for low-latency RT performance</desc>
<node>cacofonix</node>
<script>yaml_files/sa_fhi_7.2_benetel550_4x4_40MHz_9b_mplane_gnb/setup_config.sh</script>
</testCase>
<testCase>
<class>Pull_Local_Registry</class>
<desc>Pull Images from Local Registry</desc>
<node>cacofonix</node>
<images>oai-gnb-fhi72</images>
</testCase>
<testCase>
<class>Initialize_UE</class>
<desc>Initialize Amarisoft UE</desc>
<id>amarisoft_00105_40MHz</id>
</testCase>
<testCase>
<class>Create_Workspace</class>
<desc>Create new Workspace for server 0</desc>
<node>cacofonix</node>
</testCase>
<testCase>
<class>Deploy_Object</class>
<desc>Deploy gNB (TDD/Band78/100MHz/Benetel) in a container</desc>
<node>cacofonix</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_benetel550_4x4_40MHz_9b_mplane_gnb</yaml_path>
</testCase>
<testCase>
<class>Attach_UE</class>
<desc>Attach UE</desc>
<id>amarisoft_ue_1</id>
</testCase>
<testCase>
<class>Ping</class>
<desc>Ping: 100 pings in 10 sec</desc>
<id>amarisoft_ue_1</id>
<svr_id>oc-cn5g-00105</svr_id>
<ping_args>-c 100 -i 0.1</ping_args>
<ping_packetloss_threshold>1</ping_packetloss_threshold>
<ping_rttavg_threshold>15</ping_rttavg_threshold>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/130Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 130M -t 30 -R</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>balanced</iperf_profile>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/15Mbps/UDP)(30 sec)</desc>
<iperf_args>-u -b 15M -t 30</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_packetloss_threshold>20</iperf_packetloss_threshold>
<iperf_bitrate_threshold>80</iperf_bitrate_threshold>
<iperf_profile>balanced</iperf_profile>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (DL/TCP)(30 sec)</desc>
<iperf_args>-t 30 -R</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_tcp_rate_target>40</iperf_tcp_rate_target>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Iperf</class>
<desc>iperf (UL/TCP)(30 sec)</desc>
<iperf_args>-t 30</iperf_args>
<id>amarisoft_ue_1</id>
<iperf_tcp_rate_target>10</iperf_tcp_rate_target>
<svr_id>oc-cn5g-00105</svr_id>
</testCase>
<testCase>
<class>Detach_UE</class>
<always_exec>true</always_exec>
<desc>Detach UE</desc>
<id>amarisoft_ue_1</id>
</testCase>
<testCase>
<class>Terminate_UE</class>
<always_exec>true</always_exec>
<desc>Terminate UE</desc>
<id>amarisoft_00105_40MHz</id>
</testCase>
<testCase>
<class>IdleSleep</class>
<desc>Sleep</desc>
<idle_sleep_time_in_sec>5</idle_sleep_time_in_sec>
</testCase>
<testCase>
<class>Undeploy_Object</class>
<always_exec>true</always_exec>
<desc>Undeploy gNB</desc>
<node>cacofonix</node>
<yaml_path>ci-scripts/yaml_files/sa_fhi_7.2_benetel550_4x4_40MHz_9b_mplane_gnb</yaml_path>
<analysis>
<services>oai-gnb=EndsWithBye</services>
</analysis>
</testCase>
<testCase>
<class>Clean_Test_Server_Images</class>
<always_exec>true</always_exec>
<desc>Clean Test Images on Test Server</desc>
<node>cacofonix</node>
<images>oai-gnb-fhi72</images>
</testCase>
<testCase>
<class>UndeployCoreNetwork</class>
<desc>Terminate 5G Core</desc>
<always_exec>true</always_exec>
<cn_id>oc-cn5g-00105</cn_id>
</testCase>
</testCaseList>

View File

@@ -12,6 +12,13 @@
<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>
@@ -134,9 +141,9 @@
<testCase>
<class>Custom_Script</class>
<always_exec>true</always_exec>
<desc>Set CPU to idle state, set kernel parameters to default values</desc>
<desc>Inactivate carriers on VVDN RU</desc>
<node>cacofonix</node>
<script>yaml_files/sa_fhi_7.2_vvdn_gnb/setup_cleanup.sh</script>
<script>scripts/vvdn-inactivate-carriers.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=RetxCheck=dl=10,100,100,100;ul=25,100,100,100 oai-gnb=EndsWithBye</services>
<services>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=RetxCheck=dl=15,100,100,100;ul=20,100,100,100 oai-gnb=EndsWithBye</services>
<services>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=RetxCheck=10,100,100,100 oai-vnf=EndsWithBye oai-pnf=EndsWithBye</services>
<services>oai-vnf=EndsWithBye oai-pnf=EndsWithBye</services>
</analysis>
</testCase>

View File

@@ -43,6 +43,12 @@ nfs:
n4:
interface_name: eth0
port: 8805
pcf:
host: oai-pcf
sbi:
port: 8080
api_version: v1
interface_name: eth0
upf:
host: oai-upf
@@ -77,6 +83,10 @@ database:
snssais:
- &embb_slice1
sst: 1
sd: "FFFFFF"
- &embb_slice2
sst: 1
sd: "123456"
############## NF-specific configuration
amf:
@@ -103,6 +113,7 @@ amf:
tac: 0x0001
nssai:
- *embb_slice1
- *embb_slice2
supported_integrity_algorithms:
- "NIA0"
- "NIA1"
@@ -116,7 +127,7 @@ smf:
ue_mtu: 1500
support_features:
use_local_subscription_info: yes # Use infos from local_subscription_info or from UDM
use_local_pcc_rules: yes # Use infos from local_pcc_rules or from PCF
use_local_pcc_rules: no # Enforce PCC rules from PCF
upfs:
- host: oai-upf
port: 8805
@@ -144,6 +155,9 @@ smf:
- sNssai: *embb_slice1
dnnSmfInfoList:
- dnn: "oai"
- sNssai: *embb_slice2
dnnSmfInfoList:
- dnn: "openairinterface"
local_subscription_infos:
- single_nssai: *embb_slice1
dnn: "oai"
@@ -151,6 +165,19 @@ smf:
5qi: 9
session_ambr_ul: "200Mbps"
session_ambr_dl: "400Mbps"
- single_nssai: *embb_slice2
dnn: "openairinterface"
qos_profile:
5qi: 9
session_ambr_ul: "200Mbps"
session_ambr_dl: "400Mbps"
pcf:
local_policy:
policy_decisions_path: /openair-pcf/policies/policy_decisions
pcc_rules_path: /openair-pcf/policies/pcc_rules
traffic_rules_path: /openair-pcf/policies/traffic_rules
qos_data_path: /openair-pcf/policies/qos_data
upf:
support_features:
@@ -162,9 +189,15 @@ upf:
- sNssai: *embb_slice1
dnnUpfInfoList:
- dnn: "oai"
- sNssai: *embb_slice2
dnnUpfInfoList:
- dnn: "openairinterface"
## DNN configuration
dnns:
- dnn: "oai"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.1.0/24"
- dnn: "openairinterface"
pdu_session_type: "IPV4"
ipv4_subnet: "12.1.2.0/24"

View File

@@ -45,9 +45,26 @@ services:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-smf/etc/config.yaml
depends_on:
- oai-amf
- oai-pcf
networks:
public_net:
ipv4_address: 192.168.71.133
oai-pcf:
container_name: "rfsim5g-oai-pcf"
image: oaisoftwarealliance/oai-pcf:v2.2.1
environment:
- TZ=Europe/Paris
volumes:
- ../5g_rfsimulator/mini_nonrf_config.yaml:/openair-pcf/etc/config.yaml
- ./policies/policy_decisions:/openair-pcf/policies/policy_decisions
- ./policies/pcc_rules:/openair-pcf/policies/pcc_rules
- ./policies/traffic_rules:/openair-pcf/policies/traffic_rules
- ./policies/qos_data:/openair-pcf/policies/qos_data
depends_on:
- oai-amf
networks:
public_net:
ipv4_address: 192.168.71.142
oai-upf:
container_name: "rfsim5g-oai-upf"
image: oaisoftwarealliance/oai-upf:v2.2.1
@@ -78,7 +95,8 @@ services:
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"
"ip route add 12.1.1.0/24 via 192.168.72.134 dev eth0;"\
"ip route add 12.1.2.0/24 via 192.168.72.134 dev eth0; sleep infinity"
depends_on:
- oai-upf
networks:

View File

@@ -0,0 +1,29 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
gbr-rule-default-3mbps:
flowInfos:
- flowDescription: permit out ip from any to assigned
packetFilterUsage: true
precedence: 10
refQosData:
- gbr-qos-default-3mbps
gbr-rule-52080:
flowInfos:
- flowDescription: permit out ip from any to assigned 52080
packetFilterUsage: true
- flowDescription: permit in ip from assigned 52080 to any
packetFilterUsage: true
precedence: 7
refQosData:
- gbr-qos-52080-20mbps
gbr-rule-52081:
flowInfos:
- flowDescription: permit out ip from any to assigned 52081
packetFilterUsage: true
- flowDescription: permit in ip from assigned 52081 to any
packetFilterUsage: true
precedence: 8
refQosData:
- gbr-qos-52081-10mbps

View File

@@ -0,0 +1,35 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
decision_default:
default: true
pcc_rules:
- gbr-rule-default-3mbps
decision_dnn_oai:
dnn: oai
pcc_rules:
- gbr-rule-default-3mbps
- gbr-rule-52080
- gbr-rule-52081
decision_dnn_openairinterface:
dnn: openairinterface
pcc_rules:
- gbr-rule-default-3mbps
decision_supi2:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059450
decision_supi3:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059451
decision_supi4:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059452
decision_supi5:
pcc_rules:
- gbr-rule-default-3mbps
supi_imsi: 001010000059453

View File

@@ -0,0 +1,37 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
gbr-qos-default-3mbps:
5qi: 9
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 3 Mbps
gbrDl: 3 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 50
gbr-qos-52080-20mbps:
5qi: 67
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 20 Mbps
gbrDl: 20 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 30
gbr-qos-52081-10mbps:
5qi: 69
maxbrUl: 1000 Mbps
maxbrDl: 1000 Mbps
gbrUl: 10 Mbps
gbrDl: 10 Mbps
arp:
priorityLevel: 8
preemptCap: NOT_PREEMPT
preemptVuln: NOT_PREEMPTABLE
priorityLevel: 40

View File

@@ -0,0 +1,162 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:9.6
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.2.1
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.2.1
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.2.1
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:latest
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

@@ -0,0 +1,52 @@
# 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

@@ -0,0 +1,164 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:9.6
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.2.1
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.2.1
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.2.1
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:latest
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

@@ -0,0 +1,162 @@
# SPDX-License-Identifier: MIT
services:
mysql:
container_name: "rfsim5g-mysql"
image: mysql:9.6
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.2.1
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.2.1
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.2.1
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:latest
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

@@ -0,0 +1,54 @@
# 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,42 @@
# SPDX-License-Identifier: MIT
services:
dpdk-init:
image: docker.io/library/oai-dpdk-init:latest
network_mode: host
container_name: dpdk-init
entrypoint: "/tmp/setup_sriov.sh"
privileged: true
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ./setup_sriov_benetel.sh:/tmp/setup_sriov.sh
- /lib/modules:/lib/modules
- /usr/lib/modules:/usr/lib/modules
oai-gnb:
image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop}
privileged: true
network_mode: "host"
container_name: oai-gnb
environment:
TZ: Europe/Paris
USE_ADDITIONAL_OPTIONS: --thread-pool 7,8,9,10,11,12 --device.name oran_fhlib_5g_mplane --log_config.global_log_options level,nocolor,time,line_num,function
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ../../conf_files/gnb.sa.band78.273prb.fhi72.2x2-benetel550-9b-mplane.conf:/opt/oai-gnb/etc/gnb.conf
- /dev/hugepages:/dev/hugepages
- /home/oaicicd/.ssh/id_rsa.pub:/opt/oai-gnb/etc/id_rsa.pub
- /home/oaicicd/.ssh/id_rsa:/opt/oai-gnb/etc/id_rsa
# Please change these values based on your system
cpuset: "0-12"
depends_on:
dpdk-init:
condition: service_completed_successfully
healthcheck:
test: /bin/bash -c "/opt/oai-gnb/bin/check-prach-io.sh"
start_period: 60s
start_interval: 500ms
interval: 5s
timeout: 5s
retries: 10

View File

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

View File

@@ -0,0 +1,39 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
set -eu
pci_addr()
{
PF_IF=$1
VF_INDEX=$2
SYSFS_PATH="/sys/class/net/${PF_IF}/device/virtfn${VF_INDEX}"
if [ ! -e "$SYSFS_PATH" ]; then
echo "VF $VF_INDEX not found for interface $PF_IF"
exit 1
fi
PCI_ADDR=$(basename "$(readlink "$SYSFS_PATH")")
echo "$PCI_ADDR"
}
IF_NAME=ens7f1
## O-DU C Plane MAC ADDR and VLAN
C_U_PLANE_MAC_ADD=00:11:22:33:44:66
C_U_PLANE_VLAN=3
MTU=9000
DPDK_DEVBIND_PREFIX=/usr/local/bin
NUM_VFs=1
ethtool -G $IF_NAME rx 8160 tx 8160
sh -c "echo 0 > /sys/class/net/$IF_NAME/device/sriov_numvfs"
sh -c "echo $NUM_VFs > /sys/class/net/$IF_NAME/device/sriov_numvfs"
C_U_PLANE_PCI=$(pci_addr $IF_NAME 0)
# this next 2 lines is for C/U planes
ip link set $IF_NAME vf 0 mac $C_U_PLANE_MAC_ADD vlan $C_U_PLANE_VLAN spoofchk off mtu $MTU
sleep 1
modprobe iavf
${DPDK_DEVBIND_PREFIX}/dpdk-devbind.py --unbind $C_U_PLANE_PCI
modprobe vfio-pci
${DPDK_DEVBIND_PREFIX}/dpdk-devbind.py --bind vfio-pci $C_U_PLANE_PCI
echo "Successfully configured C-PLANE and U-PLANE:
- C-PLANE MAC: $C_U_PLANE_MAC_ADD, VLAN: $C_U_PLANE_VLAN, PCI: $C_U_PLANE_PCI"
exit 0

View File

@@ -0,0 +1,42 @@
# SPDX-License-Identifier: MIT
services:
dpdk-init:
image: docker.io/library/oai-dpdk-init:latest
network_mode: host
container_name: dpdk-init
entrypoint: "/tmp/setup_sriov.sh"
privileged: true
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ./setup_sriov_benetel.sh:/tmp/setup_sriov.sh
- /lib/modules:/lib/modules
- /usr/lib/modules:/usr/lib/modules
oai-gnb:
image: ${REGISTRY-oaisoftwarealliance/}oai-gnb-fhi72:${TAG:-develop}
privileged: true
network_mode: "host"
container_name: oai-gnb
environment:
TZ: Europe/Paris
USE_ADDITIONAL_OPTIONS: --thread-pool 7,8,9,10,11,12 --device.name oran_fhlib_5g_mplane --log_config.global_log_options level,nocolor,time,line_num,function
devices:
- /dev/vfio:/dev/vfio/
volumes:
- ../../conf_files/gnb.sa.band78.106prb.fhi72.4x4-benetel550-9b-mplane.conf:/opt/oai-gnb/etc/gnb.conf
- /dev/hugepages:/dev/hugepages
- /home/oaicicd/.ssh/id_rsa.pub:/opt/oai-gnb/etc/id_rsa.pub
- /home/oaicicd/.ssh/id_rsa:/opt/oai-gnb/etc/id_rsa
# Please change these values based on your system
cpuset: "0-12"
depends_on:
dpdk-init:
condition: service_completed_successfully
healthcheck:
test: /bin/bash -c "/opt/oai-gnb/bin/check-prach-io.sh"
start_period: 60s
start_interval: 500ms
interval: 5s
timeout: 5s
retries: 10

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

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

View File

@@ -0,0 +1,39 @@
#!/bin/sh
# SPDX-License-Identifier: MIT
set -eu
pci_addr()
{
PF_IF=$1
VF_INDEX=$2
SYSFS_PATH="/sys/class/net/${PF_IF}/device/virtfn${VF_INDEX}"
if [ ! -e "$SYSFS_PATH" ]; then
echo "VF $VF_INDEX not found for interface $PF_IF"
exit 1
fi
PCI_ADDR=$(basename "$(readlink "$SYSFS_PATH")")
echo "$PCI_ADDR"
}
IF_NAME=ens7f1
## O-DU C Plane MAC ADDR and VLAN
C_U_PLANE_MAC_ADD=00:11:22:33:44:66
C_U_PLANE_VLAN=3
MTU=9000
DPDK_DEVBIND_PREFIX=/usr/local/bin
NUM_VFs=1
ethtool -G $IF_NAME rx 8160 tx 8160
sh -c "echo 0 > /sys/class/net/$IF_NAME/device/sriov_numvfs"
sh -c "echo $NUM_VFs > /sys/class/net/$IF_NAME/device/sriov_numvfs"
C_U_PLANE_PCI=$(pci_addr $IF_NAME 0)
# this next 2 lines is for C/U planes
ip link set $IF_NAME vf 0 mac $C_U_PLANE_MAC_ADD vlan $C_U_PLANE_VLAN spoofchk off mtu $MTU
sleep 1
modprobe iavf
${DPDK_DEVBIND_PREFIX}/dpdk-devbind.py --unbind $C_U_PLANE_PCI
modprobe vfio-pci
${DPDK_DEVBIND_PREFIX}/dpdk-devbind.py --bind vfio-pci $C_U_PLANE_PCI
echo "Successfully configured C-PLANE and U-PLANE:
- C-PLANE MAC: $C_U_PLANE_MAC_ADD, VLAN: $C_U_PLANE_VLAN, PCI: $C_U_PLANE_PCI"
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.conf:/opt/oai-gnb/etc/gnb.conf
- ../../conf_files/gnb.sa.band78.106prb.usrpb200.sc-fdma-deltaMCS.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

@@ -18,7 +18,7 @@ HWs=""
BUILD_DIR=ran_build
CMAKE_BUILD_TYPE="RelWithDebInfo"
CMAKE_CMD="$CMAKE"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal ldpc_xdma websrv oai_iqplayer imscope imscope_record"
OPTIONAL_LIBRARIES="telnetsrv enbscope uescope nrscope ldpc_aal websrv oai_iqplayer imscope imscope_record"
TARGET_LIST=""
BUILD_TOOL_OPT="-j$(nproc)"
@@ -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, None (Default)
USRP, BLADERF, LMSSDR, IRIS, SIMU, AW2SORI, AERIAL, ZMQ, 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")
"USRP" | "BLADERF" | "LMSSDR" | "IRIS" | "ZMQ")
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}
DEPENDS ${ASN1C_GRAMMAR} asn1c
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_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
# - 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
#
# Required options
# ^^^^^^^^^^^^^^^^
@@ -46,13 +46,7 @@
# ``xran_LIBRARY``
# The path to the xran library.
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()
set(CACHE{xran_LOCATION} TYPE PATH HELP "directory of XRAN library" VALUE "")
find_path(xran_INCLUDE_DIR
NAMES
@@ -73,27 +67,26 @@ find_library(xran_LIBRARY
PATH_SUFFIXES build api
NO_DEFAULT_PATH
)
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}")
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")
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()
message(FATAL_ERROR "unrecognized xran version string: ${xran_VERSION_STRING}")
set(xran_VERSION "UNKNOWN")
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

@@ -36,25 +36,23 @@ typedef enum {
typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_t;
/* QoS Priority Level */
typedef enum {
QOS_PRIORITY_SPARE = 0,
QOS_PRIORITY_HIGHEST,
QOS_PRIORITY_2,
QOS_PRIORITY_3,
QOS_PRIORITY_4,
QOS_PRIORITY_5,
QOS_PRIORITY_6,
QOS_PRIORITY_7,
QOS_PRIORITY_8,
QOS_PRIORITY_9,
QOS_PRIORITY_10,
QOS_PRIORITY_11,
QOS_PRIORITY_12,
QOS_PRIORITY_13,
QOS_PRIORITY_LOWEST,
QOS_NO_PRIORITY
} qos_priority_t;
/* 5QI (5G QoS Identifier) - 3GPP TS 23.501 §5.7.2.1
* Range: 0..255
* - Standardized 5QI values: have one-to-one mapping to standardized 5G QoS characteristics (Table 5.7.4-1)
* - Pre-configured 5QI values: pre-configured in the AN
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile */
#define MIN_FIVEQI 0
#define MAX_STANDARDIZED_FIVEQI 90
#define MAX_FIVEQI 255
/* ARP Priority Level - 3GPP TS 23.501 §5.7.2.2
* The ARP priority level defines the relative importance of a QoS Flow.
* Range: 1 to 15, with 1 as the highest priority.
* ARP priority levels 1-8: authorized by serving network (prioritized treatment)
* ARP priority levels 9-15: authorized by home network (roaming scenarios) */
typedef uint8_t qos_arp_priority_level_t;
#define MIN_QOS_ARP_PRIORITY_LEVEL 1 // highest priority
#define MAX_QOS_ARP_PRIORITY_LEVEL 15 // lowest priority
/* Pre-emption Capability */
typedef enum {
@@ -70,19 +68,101 @@ typedef enum {
PEV_MAX,
} qos_pev_t;
/* Allocation Retention Priority */
/* Allocation and Retention Priority (ARP) - 3GPP TS 23.501 §5.7.2.2
* Contains information about priority level, pre-emption capability and vulnerability.
* Used for admission control of GBR traffic and pre-emption decisions. */
typedef struct {
qos_priority_t priority_level;
// ARP priority level (1-15, 1 = highest)
qos_arp_priority_level_t priority_level;
qos_pec_t pre_emp_capability;
qos_pev_t pre_emp_vulnerability;
} qos_arp_t;
/* QoS Priority Level - 3GPP TS 23.501 §5.7.3.3
* The Priority Level associated with 5G QoS characteristics indicates a priority
* in scheduling resources among QoS Flows. The lowest Priority Level value
* corresponds to the highest priority.
* Range: 1 to 127, with 1 as the highest priority and 127 as the lowest priority.
* Used for scheduling resources among QoS Flows (different from ARP priority level
* which is used for admission control/preemption).
* Every standardized 5QI is associated with a default Priority Level value. */
typedef uint8_t qos_priority_level_t;
#define MIN_QOS_PRIORITY_LEVEL 1 // highest priority
#define MAX_QOS_PRIORITY_LEVEL 127 // lowest priority
/* Packet Delay Budget (PDB) - 3GPP TS 23.501 §5.7.3.4
* Upper bound for packet delay between UE and UPF N6 termination point (0..1023 ms).
* Used for scheduling and link layer configuration (e.g. HARQ target operating points).
* For Delay-critical GBR flows, packets delayed more than PDB are counted as lost. */
#define MIN_PACKET_DELAY_BUDGET 0
#define MAX_PACKET_DELAY_BUDGET 1023
/* Packet Error Rate (PER) - 3GPP TS 23.501 §5.7.3.5
* Upper bound for rate of non-congestion related packet losses (0..9 for scalar/exponent).
* Used for link layer protocol configuration (e.g. RLC, HARQ).
* PER = Scalar * 10^(-Exponent) */
#define MIN_PACKET_ERROR_RATE_SCALAR 0
#define MAX_PACKET_ERROR_RATE_SCALAR 9
#define MIN_PACKET_ERROR_RATE_EXPONENT 0
#define MAX_PACKET_ERROR_RATE_EXPONENT 9
typedef struct {
// Packet Error Rate Scalar (0..9)
uint8_t scalar;
// Packet Error Rate Exponent (0..9)
uint8_t exponent;
} qos_per_t;
/** QoS Characteristics for a standardized or
* pre-configured 5QI for downlink and uplink*/
typedef struct {
uint16_t fiveQI;
qos_priority_level_t *qos_priority;
} non_dynamic_5qi_t;
/** QoS Characteristics for a Non-standardised or
* not pre-configured 5QI for downlink and uplink. */
typedef struct {
uint16_t *fiveQI;
qos_priority_level_t qos_priority;
// Packet Delay Budget (0..1023 ms)
int packet_delay_budget;
// Packet Error Rate (0..9 for scalar/exponent)
qos_per_t per;
} dynamic_5qi_t;
/* Bit Rate (kbps) - 3GPP TS 38.413 */
typedef struct qos_bitrate_s {
// Guaranteed Flow Bit Rate (GFBR) (kbps)
uint64_t guaranteedFlowBitRate;
// Maximum Flow Bit Rate (MFBR) (kbps)
uint64_t maximumFlowBitRate;
} qos_bitrate_t;
/* GBR QoS Flow Information - 3GPP TS 23.501 §5.7.1.2, TS 38.413 §9.3.1.19
* Present only for GBR QoS flows (5QI < 5 for NonDynamic5QI, or Dynamic5QI with GBR).
* For GBR QoS Flow only, the QoS profile SHALL include for DL and UL:
* - Guaranteed Flow Bit Rate (GFBR)
* - Maximum Flow Bit Rate (MFBR) */
typedef struct gbr_qos_flow_information_s {
// Downlink bit rates (kbps)
qos_bitrate_t dl;
// Uplink bit rates (kbps)
qos_bitrate_t ul;
} gbr_qos_flow_information_t;
typedef struct pdusession_level_qos_parameter_s {
uint8_t qfi;
uint64_t fiveQI;
uint64_t qos_priority;
// QoS Characteristics
fiveQI_t fiveQI_type;
union {
non_dynamic_5qi_t non_dynamic;
dynamic_5qi_t dynamic;
} qos_characteristics;
// NG-RAN Allocation and Retention Priority
qos_arp_t arp;
/* GBR QoS Flow Information (optional - only for GBR flows) */
gbr_qos_flow_information_t *gbr_qos_flow_information;
} pdusession_level_qos_parameter_t;
#endif

View File

@@ -282,12 +282,9 @@ 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
cfgptr = calloc(sizeof(configmodule_interface_t), 1);
configmodule_interface_t *cfgptr = calloc(sizeof(configmodule_interface_t), 1);
if (!cfgptr) {
fprintf(stderr, "ERROR: cannot allocate a memory for configuration\n");
return NULL;

View File

@@ -10,6 +10,9 @@
#ifndef INCLUDE_CONFIG_LOADCONFIGMODULE_H
#define INCLUDE_CONFIG_LOADCONFIGMODULE_H
#ifdef __cplusplus
extern "C" {
#endif
#include <string.h>
#include <stdlib.h>
@@ -117,5 +120,8 @@ 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,6 +181,11 @@ 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

@@ -126,37 +126,97 @@ int config_getlist(configmodule_interface_t *cfg, paramlist_def_t *ParamList, pa
}
const int ret = cfg->getlist(cfg, ParamList, params, numparams, prefix);
if (ret >= 0 && params) {
char *newprefix;
if (prefix) {
int rc = asprintf(&newprefix, "%s.%s", prefix, ParamList->listname);
if (rc < 0) newprefix = NULL;
} else {
newprefix = ParamList->listname;
}
char cfgpath[MAX_OPTNAME_SIZE*2 + 6]; /* prefix.listname.[listindex] */
for (int i = 0; i < ParamList->numelt; ++i) {
// TODO config_process_cmdline?
sprintf(cfgpath, "%s.[%i]", newprefix, i);
config_process_cmdline(cfg, ParamList->paramarray[i], numparams, cfgpath);
if (cfg->rtflags & CONFIG_SAVERUNCFG) {
cfg->set(ParamList->paramarray[i], numparams, cfgpath);
}
config_execcheck(cfg, ParamList->paramarray[i], numparams, cfgpath);
}
if (prefix)
free(newprefix);
/* build newprefix OUTSIDE the params check so we can use it below too */
char *newprefix = NULL;
if (prefix) {
int rc = asprintf(&newprefix, "%s.%s", prefix, ParamList->listname);
if (rc < 0)
newprefix = NULL;
} else {
newprefix = ParamList->listname;
}
return ret;
char cfgpath[MAX_OPTNAME_SIZE * 2 + 6]; /* prefix.listname.[listindex] */
if (ret >= 0 && params) {
for (int i = 0; i < ParamList->numelt; ++i) {
sprintf(cfgpath, "%s.[%i]", newprefix, i);
config_process_cmdline(cfg, ParamList->paramarray[i], numparams, cfgpath);
if (cfg->rtflags & CONFIG_SAVERUNCFG)
cfg->set(ParamList->paramarray[i], numparams, cfgpath);
config_execcheck(cfg, ParamList->paramarray[i], numparams, cfgpath);
}
}
if (params && newprefix && (ret >= 0 || ParamList->numelt == 0)) {
sprintf(cfgpath, "%s", newprefix);
char searchstr[MAX_OPTNAME_SIZE * 2 + 10];
snprintf(searchstr, sizeof(searchstr), "--%s.", cfgpath);
char *endptr;
int valid_idx = ParamList->numelt;
for (int i = 1; i < cfg->argc; i++) {
char *res = strstr(cfg->argv[i], searchstr);
if (res != NULL) {
char *bracket = strchr(res + strlen(searchstr), '[');
bracket++;
long num = strtol(bracket, &endptr, 10);
if (num < valid_idx)
continue;
if (valid_idx == num) {
valid_idx++;
} else if (num > valid_idx) {
LOG_E(HW, "Out of Order Element Creation\n index: %s, valid_idx: %d, num: %ld\n", cfg->argv[i], valid_idx, num);
return -1;
} else {
LOG_E(HW, "[CONFIG] Invalid Configuration\n index: %s, valid_idx: %d, num: %ld\n", cfg->argv[i], valid_idx, num);
return -1;
}
}
}
while (ParamList->numelt < valid_idx) {
int new_idx = ParamList->numelt;
sprintf(cfgpath, "%s.[%i]", newprefix, new_idx);
paramdef_t **old = ParamList->paramarray;
ParamList->paramarray = config_allocate_new(cfg, (new_idx + 1) * sizeof(paramdef_t *), true);
memcpy(ParamList->paramarray, old, new_idx * sizeof(paramdef_t *));
ParamList->paramarray[new_idx] = config_allocate_new(cfg, numparams * sizeof(paramdef_t), true);
memcpy(ParamList->paramarray[new_idx], params, sizeof(paramdef_t) * numparams);
for (int p = 0; p < numparams; p++) {
ParamList->paramarray[new_idx][p].voidptr = NULL;
}
ParamList->numelt++;
fprintf(stderr, "[CONFIG] Created new array parameter %s.[%d]\n", newprefix, new_idx);
config_process_cmdline(cfg, ParamList->paramarray[new_idx], numparams, cfgpath);
for (int p = 0; p < numparams; p++) {
paramdef_t *pd = &ParamList->paramarray[new_idx][p];
if (pd->paramflags & PARAMFLAG_PARAMSET)
continue;
config_common_getdefault(cfg, pd, cfgpath);
}
config_execcheck(cfg, ParamList->paramarray[new_idx], numparams, cfgpath);
for (int p = 0; p < numparams; p++) {
if (ParamList->paramarray[new_idx][p].paramflags & PARAMFLAG_PARAMSET)
fprintf(stderr, "[CONFIG] New parameter set: %s\n", ParamList->paramarray[new_idx][p].optname);
}
}
}
if (prefix)
free(newprefix);
/* when added parameters via CLI, return numelt instead of original ret */
return (ParamList->numelt > 0) ? (int)ParamList->numelt : ret;
}
int config_isparamset(paramdef_t *params,int paramidx) {
int config_isparamset(paramdef_t *params, int paramidx)
{
if ((params[paramidx].paramflags & PARAMFLAG_PARAMSET) != 0) {
return 1;
} else {

View File

@@ -43,7 +43,7 @@
#define NB_RB_MAX (11 + 3) /* LTE_maxDRB from LTE_asn_constant.h + 3 SRBs */
#define NR_NB_RB_MAX (29 + 3) /* NR_maxDRB from NR_asn_constant.hm + 3 SRBs */
#define NGAP_MAX_PDU_SESSION (256) /* As defined in TS 38.413 9.2.1.1 Range Bound for PDU Sessions. */
#define NR_MAX_NB_PDU_SESSIONS (256)
#define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE
* defined for NGAP in TS 38.413 - maxnoofDRBs */

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 reverse_bits.c fsn.c)
add_library(utils utils.c system.c time_meas.c time_stat.c tuntap_if.c 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_IDs.h T_messages.txt.h)
add_library(T STATIC T.c local_tracer.c T_messages_creator.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 {
void *addr;
const 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,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
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
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,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
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
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,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
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
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,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
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
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,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
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
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,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
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
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,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
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
#for debug/test - not used
ID = first

View File

@@ -0,0 +1,220 @@
/*
* 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

@@ -0,0 +1,195 @@
/*
* 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,10 +18,13 @@
#ifndef _SHARED_MEMORY_CONFIG_H_
#define _SHARED_MEMORY_CONFIG_H_
#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
#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
// for gNB T Tracer App
#define GETKEYDIR1_gNB ("/tmp/gnb_app1")

View File

@@ -3,7 +3,7 @@
*/
/*!
* \brief T-Tracer gnb service to capture tracee Messages from gNB, it is used by data recording application
* \brief T-Tracer gnb service to capture trace Messages from gNB, it is used by data recording application
*/
#include <stdio.h>
@@ -34,6 +34,21 @@
// 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)
{
@@ -44,19 +59,11 @@ int combine_bytes(const uint8_t *bytes, size_t num_bytes)
return result;
}
// Convert an integer to an array of bytes - little-endian format
void int_to_bytes(int num, uint8_t *bytes, size_t num_bytes)
// 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)
{
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]) {
for (int i = 0; i < n_msgs; i++) {
if (msg_id == msg_id_list[i]) {
return true;
}
}
@@ -71,12 +78,6 @@ 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 +89,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 --This part should be moved to inilization part
// initialization to measure time stamp
time_t my_time;
struct tm *timeinfo;
time(&my_time);
@@ -104,31 +105,12 @@ 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;
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);
uint32_t usec = (tv.tv_usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06u", 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);
@@ -153,7 +135,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 memeory already exist\n");
printf("Error: shared memory already exists\n");
shm_id = shmget(key, 0, 0);
printf("reference shm_id = %d\n", shm_id);
} else {
@@ -210,75 +192,28 @@ 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 vararibles of each message
// you need to define the variables 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_size, data;
} event_trace_msg_data;
int data;
} event_trace_msg_ul_data;
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
{
database_event_format f;
int i;
/* 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;
// Initialize all fields to -1 (marks unset indices)
memset(d, -1, sizeof(*d));
/* this macro looks for a particular element and checks its type */
#define G(var_name, var_type, var) \
@@ -291,35 +226,14 @@ void setup_trace_msg_data(event_trace_msg_data *d, void *database)
continue; \
}
/* ------------------------------*/
/* 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
// Get a template of any UL 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);
/* 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
// Map each field name to its index in the event structure
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)
@@ -345,20 +259,106 @@ void setup_trace_msg_data(event_trace_msg_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;
}
// Function to check if a value is in the array
int isValueInArray(int value, int arr[], int size)
// -------------------------------------------------------------------
// 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)
{
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
return 1; // Value found
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);
}
*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,40 +386,24 @@ 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_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.
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.
// if you have network delay, you can increase the number of records to capture
int max_sync_offset = 6; // 6 frames ~ 60 ms
// 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];
}
// 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]);
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;
@@ -432,18 +416,19 @@ 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_data trace_msg_data;
event_trace_msg_ul_data trace_msg_ul_data;
// initlization variables
// initialization variables
unsigned int bufIdx_wr = 0;
unsigned int bufIdx_rd = 0;
uint8_t num_req_tracer_msgs = 0;
// initilaze shared memory
// initialize shared memory
char *addr_wr, *addr_rd;
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);
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);
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);
@@ -500,20 +485,18 @@ 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
printf("\n Data Collection Service: Waiting for messages request ...\n");
// 0: Wait
// 1: config
// 2: record
// 3: stop
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for config message ...\n");
// Wait for Action Config
while (1) {
if ((uint8_t)(addr_rd[0]) == 0) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// config state
else if ((uint8_t)(addr_rd[0]) == 1) {
else if ((uint8_t)(addr_rd[0]) == STATE_CONFIG) {
get_time_stamp_usec(trace_time_stamp_str);
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
PRINT_STATE_BANNER("CONFIG");
printf(" Received config message. Time Stamp: %s\n", trace_time_stamp_str);
// get the IP address length in bytes
bufIdx_rd = 1;
@@ -530,11 +513,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 = start frame number
bufIdx_rd += 2; // + 2 bytes = port number
port = combine_bytes(port_number_bytes, 2);
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
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
break;
}
}
@@ -545,37 +528,39 @@ int main(int n, char **v)
/* connect to the nr-softmodem */
socket = connect_to(ip, port);
printf("\n Connected to nr-softmodem");
printf(" Connected to nr-softmodem\n");
// Read Action record or stop
printf("\n Data Collection Service: Waiting for record message ...\n");
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for record/stop message ...\n");
while (1) {
// wait for Action record
if ((uint8_t)(addr_rd[0]) == 0) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// quit state
else if ((uint8_t)(addr_rd[0]) == 3) {
printf("\n Received 'stop' command. Exiting...");
printf("\n ");
else if ((uint8_t)(addr_rd[0]) == STATE_STOP) {
PRINT_STATE_BANNER("STOP");
printf(" Received 'stop' command. Exiting...\n");
// Clean up and exit
break;
}
// record state
else if ((uint8_t)(addr_rd[0]) == 2) {
else if ((uint8_t)(addr_rd[0]) == STATE_RECORD) {
// clear remote buffer if there is
clear_remote_config();
get_time_stamp_usec(trace_time_stamp_str);
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
PRINT_STATE_BANNER("RECORD");
printf(" Received record message. Time Stamp: %s\n", trace_time_stamp_str);
// read number of requested messages
bufIdx_rd = 1;
num_req_tracer_msgs = addr_rd[bufIdx_rd];
bufIdx_rd += 1;
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
printf(" Number of requested tracer messages: %d\n", num_req_tracer_msgs);
// reset memory : action to wait for next record action
addr_rd[0] = 0;
addr_rd[0] = STATE_WAIT;
// 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++) {
@@ -607,40 +592,36 @@ int main(int n, char **v)
printf("start_frame: %d\n", start_frame_number);
/* activate the trace in this array */
printf("\n Activate Tracer messages in on_off array: ");
printf(" Activating tracer messages:\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, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
printf(" %d: %s\n", req_tracer_msgs_indices[i], on_off_name);
}
// 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]);
// 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]);
}
// setup data for the trace messages
setup_trace_msg_data(&trace_msg_data, database);
printf("\n Setup Trace Data Done");
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);
// Get the start time
struct timespec start_time = get_current_time();
/* activate the tracee in the nr-softmodem */
/* activate the traces in the nr-softmodem */
activate_traces(socket, number_of_events, is_on);
printf("\n Activated Traces in nr-softmodem");
printf(" Activated traces in nr-softmodem\n");
/* 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;
@@ -655,8 +636,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 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 ...");
// 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");
struct pollfd event_poll_fd;
event_poll_fd.fd = socket;
event_poll_fd.events = POLLIN;
@@ -676,27 +657,24 @@ 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 (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
if (is_message_in_list(req_tracer_msgs_indices, num_req_tracer_msgs, e.type)) {
// is it based on Data Collection Trace Messages Structure
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
if (is_message_in_list(ul_msg_ids, n_ul_msgs, e.type)) {
// 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_data.frame].i;
ref_slot = e.e[trace_msg_data.slot].i;
ref_frame = e.e[trace_msg_ul_data.frame].i;
ref_slot = e.e[trace_msg_ul_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_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_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",
@@ -709,129 +687,30 @@ 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_data.frame].i, e.e[trace_msg_data.slot].i);
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);
}
}
// 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
// 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));
// --- 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));
bufIdx_wr += sizeof(uint32_t);
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);
}
*/
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
// 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_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_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) {
@@ -844,43 +723,7 @@ 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 {
@@ -891,29 +734,28 @@ int main(int n, char **v)
} // end while loop of reading events
else {
// No data, just loop and check time
usleep(100); // optional: avoid busy-waiting
usleep(50); // optional: avoid busy-waiting
}
} // End of while loop to read events
// de-activate the tracee in the nr-softmodem
printf("\n De-activated Tracer message:\n");
// de-activate the traces in the nr-softmodem
printf("\n De-activating tracer messages\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("\n De-activated Traces");
printf(" De-activated traces\n");
// 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 difference: %.2f ms\n", time_diff);
printf("Time difference per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
printf(" Total time: %.2f ms\n", time_diff);
printf(" Time 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);
@@ -934,12 +776,11 @@ int main(int n, char **v)
}
} else {
// No data, just loop and check time
usleep(100); // optional: avoid busy-waiting
usleep(50); // optional: avoid busy-waiting
}
} // End of while loop to discard stale records
}
} // End a while loop to check for the "stop" command
// de-activate the tracee in the nr-softmodem
} // End of while loop for record/stop
free_database(database);
free(is_on);

View File

@@ -3,7 +3,7 @@
*/
/*!
* \brief T-Tracer UE service to capture tracee Messages from UE, it is used by data recording application
* \brief T-Tracer UE service to capture trace Messages from UE, it is used by data recording application
*/
#include <stdio.h>
@@ -32,7 +32,21 @@
#define DEBUG_BUFFER
// Duration to discard recording in milliseconds to mitigate stale or previous record data
#define DISCARD_RECORD_DURATION_MS 10
// #include <linux/time.h>
// 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)
@@ -44,19 +58,11 @@ int combine_bytes(const uint8_t *bytes, size_t num_bytes)
return result;
}
// 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)
// 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)
{
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]) {
for (int i = 0; i < list_size; i++) {
if (msg_id == msg_id_list[i]) {
return true;
}
}
@@ -71,12 +77,6 @@ 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 --This part should be moved to inilization part
// initialization to measure time stamp
time_t my_time;
struct tm *timeinfo;
time(&my_time);
@@ -104,31 +104,12 @@ 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;
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);
uint32_t usec = (tv.tv_usec);
sprintf(time_stamp_str, "%04d%02d%02d%02d%02d%02d%06u", 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);
@@ -153,7 +134,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 memeory already exist\n");
printf("Error: shared memory already exists\n");
shm_id = shmget(key, 0, 0);
printf("reference shm_id = %d\n", shm_id);
} else {
@@ -210,75 +191,28 @@ 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 vararibles of each message
// you need to define the variables 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_size, data;
} event_trace_msg_data;
int data;
} event_trace_msg_ul_data;
void setup_trace_msg_data(event_trace_msg_data *d, void *database)
void setup_trace_msg_ul_data(event_trace_msg_ul_data *d, void *database)
{
database_event_format f;
int i;
/* 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;
// Initialize all fields to -1 (marks unset indices)
memset(d, -1, sizeof(*d));
/* this macro looks for a particular element and checks its type */
#define G(var_name, var_type, var) \
@@ -291,35 +225,14 @@ void setup_trace_msg_data(event_trace_msg_data *d, void *database)
continue; \
}
/* ------------------------------*/
/* 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
// Get a template of any UL 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);
/* 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
// Map each field name to its index in the event structure
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)
@@ -347,20 +260,108 @@ void setup_trace_msg_data(event_trace_msg_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;
}
// Function to check if a value is in the array
int isValueInArray(int value, int arr[], int size)
// -------------------------------------------------------------------
// 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)
{
for (int i = 0; i < size; i++) {
if (arr[i] == value) {
return 1; // Value found
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);
}
*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)
@@ -388,40 +389,24 @@ 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_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.
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.
// if you have network delay, you can increase the number of records to capture
int max_sync_offset = 6; // 6 frames ~ 60 ms
// 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];
}
// 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]);
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;
@@ -434,18 +419,19 @@ 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_data trace_msg_data;
event_trace_msg_ul_data trace_msg_ul_data;
// initlization variables
// initialization variables
unsigned int bufIdx_wr = 0;
unsigned int bufIdx_rd = 0;
uint8_t num_req_tracer_msgs = 0;
// initilaze shared memory
// initialize shared memory
char *addr_wr, *addr_rd;
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);
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);
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);
@@ -502,20 +488,18 @@ 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
printf("\n Data Collection Service: Waiting for messages request ...\n");
// 0: Wait
// 1: config
// 2: record
// 3: stop
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for config message ...\n");
// Wait for Action Config
while (1) {
if ((uint8_t)(addr_rd[0]) == 0) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// config state
else if ((uint8_t)(addr_rd[0]) == 1) {
else if ((uint8_t)(addr_rd[0]) == STATE_CONFIG) {
get_time_stamp_usec(trace_time_stamp_str);
printf("\n Received config message. Time Stamp: %s", trace_time_stamp_str);
PRINT_STATE_BANNER("CONFIG");
printf(" Received config message. Time Stamp: %s\n", trace_time_stamp_str);
// get the IP address length in bytes
bufIdx_rd = 1;
@@ -532,11 +516,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 = start frame number
bufIdx_rd += 2; // + 2 bytes = port number
port = combine_bytes(port_number_bytes, 2);
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
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
break;
}
}
@@ -547,37 +531,39 @@ int main(int n, char **v)
/* connect to the nr-softmodem */
socket = connect_to(ip, port);
printf("\n Connected to nr-UEsoftmodem");
printf(" Connected to nr-uesoftmodem\n");
// Read Action record or stop
printf("\n Data Collection Service: Waiting for record message ...\n");
PRINT_STATE_BANNER("WAIT");
printf(" Data Collection Service: Waiting for record/stop message ...\n");
while (1) {
// wait for Action record
if ((uint8_t)(addr_rd[0]) == 0) {
if ((uint8_t)(addr_rd[0]) == STATE_WAIT) {
usleep(50); // sleep for 50 us: 0.5 ms slot duration
}
// quit state
else if ((uint8_t)(addr_rd[0]) == 3) {
printf("\n Received 'stop' command. Exiting...");
printf("\n ");
else if ((uint8_t)(addr_rd[0]) == STATE_STOP) {
PRINT_STATE_BANNER("STOP");
printf(" Received 'stop' command. Exiting...\n");
// Clean up and exit
break;
}
// record state
else if ((uint8_t)(addr_rd[0]) == 2) {
else if ((uint8_t)(addr_rd[0]) == STATE_RECORD) {
// clear remote buffer if there is
clear_remote_config();
get_time_stamp_usec(trace_time_stamp_str);
printf("\n Received record message. Time Stamp: %s", trace_time_stamp_str);
PRINT_STATE_BANNER("RECORD");
printf(" Received record message. Time Stamp: %s\n", trace_time_stamp_str);
// read number of requested messages
bufIdx_rd = 1;
num_req_tracer_msgs = addr_rd[bufIdx_rd];
bufIdx_rd += 1;
printf("\n Number of requested tracer messages: %d,", num_req_tracer_msgs);
printf(" Number of requested tracer messages: %d\n", num_req_tracer_msgs);
// reset memory : action to wait for next record action
addr_rd[0] = 0;
addr_rd[0] = STATE_WAIT;
// 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++) {
@@ -609,40 +595,36 @@ int main(int n, char **v)
printf("start_frame: %d\n", start_frame_number);
/* activate the trace in this array */
printf("\n Activate Tracer messages in on_off array: \n");
printf(" Activating tracer messages:\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, ", req_tracer_msgs_indices[i], on_off_name);
// on_off(database, "GNB_PHY_DL_OUTPUT_SIGNAL", is_on, 1);
printf(" %d: %s\n", req_tracer_msgs_indices[i], on_off_name);
}
// 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]);
// 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]);
}
// setup data for the trace messages
setup_trace_msg_data(&trace_msg_data, database);
printf("\n Setup Trace Data Done");
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);
// Get the start time
struct timespec start_time = get_current_time();
/* activate the tracee in the nr-softmodem */
/* activate the traces in the nr-uesoftmodem */
activate_traces(socket, number_of_events, is_on);
printf("\n Activated Traces in nr-UEsoftmodem");
printf(" Activated traces in nr-uesoftmodem\n");
/* 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;
@@ -656,8 +638,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 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 ...");
// 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");
struct pollfd event_poll_fd;
event_poll_fd.fd = socket;
event_poll_fd.events = POLLIN;
@@ -677,27 +659,23 @@ 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 (isValueInArray(e.type, req_tracer_msgs_indices, num_req_tracer_msgs)) {
if (is_message_in_list(req_tracer_msgs_indices, num_req_tracer_msgs, e.type)) {
// is it based on Data Collection Trace Messages Structure
if (isValueInArray(e.type, traces_support_data_Collection_format_idx, n_msgs_based_data_Collection_format)) {
if (is_message_in_list(ul_msg_ids, n_ul_msgs, e.type)) {
// 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_data.frame].i;
ref_slot = e.e[trace_msg_data.slot].i;
ref_frame = e.e[trace_msg_ul_data.frame].i;
ref_slot = e.e[trace_msg_ul_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_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_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",
@@ -710,130 +688,30 @@ 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_data.frame].i, e.e[trace_msg_data.slot].i);
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);
}
}
// 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
// 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));
// --- 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));
bufIdx_wr += sizeof(uint32_t);
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);
}
*/
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
// 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_data.frame].i;
current_slot = e.e[trace_msg_data.slot].i;
current_frame = e.e[trace_msg_ul_data.frame].i;
current_slot = e.e[trace_msg_ul_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) {
@@ -846,43 +724,6 @@ 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 {
@@ -893,28 +734,25 @@ int main(int n, char **v)
} // end while loop of reading events
else {
// No data, just loop and check time
usleep(100); // optional: avoid busy-waiting
usleep(50); // optional: avoid busy-waiting
}
} // End of while loop to read messages
// de-activate the tracee in the nr-softmodem
printf("\n De-activated Tracer message:\n");
// de-activate the traces in the nr-uesoftmodem
printf("\n De-activating tracer messages\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("\n De-activated Traces");
printf(" De-activated traces\n");
// 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 difference: %.2f ms\n", time_diff);
printf("Time difference per record: %.2f ms\n", time_diff / (number_records + max_sync_offset));
printf(" Total time: %.2f ms\n", time_diff);
printf(" Time 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;
@@ -941,10 +779,9 @@ int main(int n, char **v)
}
} // End of while loop to discard stale records
}
} // End a while loop to check for the "stop" command
// de-activate the tracee in the nr-softmodem
} // End of while loop for record/stop
// free_database(database); //Do on one app, for example on gNB App
// free_database(database); // Done on gNB App only
free(is_on);
close(socket);

View File

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

76
common/utils/bits.h Normal file
View File

@@ -0,0 +1,76 @@
/*
* 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,43 +42,59 @@
"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"
},
"tracer_service_baseStation_address": "127.0.0.1:2021",
"tracer_service_userEquipment_address": "127.0.0.1:2023"
"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
}
}
]
}
}
}

View File

@@ -1,803 +0,0 @@
# 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

@@ -0,0 +1,523 @@
# 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,12 +1,11 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Data Recording common utilities
# brief Common utilities of Data Recording App
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:
@@ -34,7 +33,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.json"
+ "config_data_recording_app_extended.json"
)
# Ensure the directory exists
os.makedirs(os.path.dirname(output_file), exist_ok=True)
@@ -43,6 +42,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"JSON file created successfully at {output_file}")
print(f" [Config] JSON file saved: {output_file}")
except Exception as e:
print(f"Failed to create JSON file: {e}")
print(f" [Config] Failed to create JSON file: {e}")

View File

@@ -1,18 +1,17 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Data Recording App Configuration Interface
# brief Configuration Interface of Data Recording App
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)
@@ -20,22 +19,11 @@ 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 = ", "")
@@ -65,12 +53,18 @@ def get_requested_tracer_msgs_indices(requested_tracer_messages, tracer_msgs_ide
return req_tracer_msgs_indices
# 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()
# 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.
def get_data_recording_config(config_meta_data):
# 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
@@ -81,44 +75,33 @@ 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
# 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"]
nodes = config_meta_data["data_recording_config"]["nodes"]
# 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)
# Build per-node args generically
node_args_dict = {}
for node in nodes:
parser = argparse.ArgumentParser(description="request messages IDs")
node_args = parser.parse_args()
# 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")
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", [])
# 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)
if node_args.requested_tracer_messages:
node["req_tracer_msgs_indices"] = get_requested_tracer_msgs_indices(
node_args.requested_tracer_messages, tracer_msgs_identities)
# 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.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")
return config_meta_data, gnb_args, ue_args
node_args_dict[node_id] = node_args
return config_meta_data, node_args_dict

View File

@@ -3,6 +3,32 @@
# ---------------------------------------------------------------------
# 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():
"""
@@ -11,8 +37,8 @@ def get_general_msg_header_list():
msg_id (uint8) message type ID
frame (uint16)
slot (uint8)
datetime_yyyymmdd (uint32)
datetime_hhmmssmmm (uint32)
unix_capture_ts_sec (uint32) Unix epoch seconds
unix_capture_ts_nsec (uint32) nanoseconds [0, 999999999]
frame_type (uint8)
freq_range (uint8)
subcarrier_spacing (uint8)
@@ -46,8 +72,8 @@ def get_general_msg_header_list():
"msg_id": 2,
"frame": 2,
"slot": 1,
"datetime_yyyymmdd": 4,
"datetime_hhmmssmmm": 4,
"unix_capture_ts_sec": 4,
"unix_capture_ts_nsec": 4,
"frame_type": 1,
"freq_range": 1,
"subcarrier_spacing": 1,

View File

@@ -0,0 +1,348 @@
# 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,11 +6,12 @@
import os
import sigmf
from sigmf import SigMFFile
# from sigmf.utils import get_data_type_str
import numpy as np
from datetime import datetime
import yaml
from datetime import datetime, timezone
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 = {
@@ -24,21 +25,29 @@ SERIALIZATION_SCHEMES = {
"tx-payload-bits": ["bits", "subcarriers", "ofdm_symbols"],
}
"""
STANDARDS = {"5gnr_oai": "5gnr"}
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")
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)
# 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
# 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
)
time_stamp_ms = time_stamp_ms_iso
time_stamp_ms_file_name = time_stamp_ms_iso.replace(":", "_").replace(".", "_")
time_stamp_ns_file_name = time_stamp_ns_iso.replace(":", "_").replace(".", "_")
return time_stamp_ms, time_stamp_ms_file_name
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 {}
def create_serialization_metadata(serialization_scheme, data_source: str,
@@ -46,6 +55,10 @@ 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
@@ -72,131 +85,73 @@ def create_serialization_metadata(serialization_scheme, data_source: str,
return serialization_dict
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):
# map OAI config data to Wireless Dictionary Parameter Map - SigMF metadata
def create_system_components_metadata(captured_data, config_meta_data):
"""Creates system components (TX, channel, RX) metadata that will reside in the annotations."""
# map metadata of waveform generator to SigMF format
signal_info, generator = map_metadata_to_sigmf_format(
"tx", waveform_generator, parameter_map_file, captured_data
)
# 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)
tx_metadata = {
"signal:detail": {
"standard": STANDARDS[waveform_generator],
"generator": generator,
STANDARDS[waveform_generator]: signal_info,
STANDARDS[waveform_generator]: signal_metadata,
}
}
channel_metadata = {} # will be filled later
rx_metadata = {} # will be filled later
return tx_metadata, channel_metadata, rx_metadata
# 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 = config_meta_data["data_recording_config"]["base_station"][
"meta_data"]
user_equipment_meta_data = config_meta_data["data_recording_config"][
"user_equipment"]["meta_data"]
base_station_meta_data = _get_node_meta_data(config_meta_data, "gnb")
user_equipment_meta_data = _get_node_meta_data(config_meta_data, "ue")
# Check the receive target path is valid, else create folder
data_storage_path = config_meta_data["data_recording_config"]["data_storage_path"]
@@ -206,111 +161,34 @@ def write_recorded_data_to_sigmf(captured_data, config_meta_data, global_info, i
# Write recorded data to file
# Get time stamp
time_stamp_ms, time_stamp_ms_file_name = time_stamp_formating(
captured_data["time_stamp"], global_info["datetime_offset"])
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"])
# 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_ms_file_name)
file_name_prefix + "-rec-" + str(idx) + "-" + time_stamp_file_name)
dataset_filename = recorded_data_file_name + ".sigmf-data"
dataset_file_path = os.path.join(data_storage_path, dataset_filename)
print(dataset_file_path)
if DEBUG_WIRELESS_RECORDED_DATA:
print(dataset_file_path)
captured_data["recorded_data"].tofile(dataset_file_path)
# map OAI config data to SigMF metadata
# ----------------------------------------------------
# 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"]
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")
tx_metadata, channel_metadata, rx_metadata = create_system_components_metadata(captured_data, config_meta_data)
# ----------------------------------------------------
# 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"]
# 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"]
sample_rate = config_meta_data["data_recording_config"]["common_meta_data"]["sample_rate"]
# get signal emitter info
signal_emitter = {
@@ -322,23 +200,14 @@ 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"})
@@ -346,7 +215,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"],
}
)
@@ -361,6 +230,7 @@ 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"})
@@ -397,16 +267,6 @@ 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
@@ -439,10 +299,9 @@ 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_ms,
sigmf.SigMFFile.DATETIME_KEY: time_stamp,
SigMFFile.FREQUENCY_KEY: freq,
**create_serialization_metadata(
serialization_scheme, waveform_generator, captured_data
@@ -486,7 +345,8 @@ 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
print(dataset_meta_file_path)
if DEBUG_WIRELESS_RECORDED_DATA:
print(dataset_meta_file_path)
return dataset_meta_file_path
@@ -513,8 +373,9 @@ def save_sigmf_collection(streams: list, global_info: dict, description: str, st
# save collection file
collection.tofile(collection_filepath)
print("")
print(collection_filepath + ".sigmf-collection")
if DEBUG_WIRELESS_RECORDED_DATA:
print("")
print(collection_filepath + ".sigmf-collection")
def load_sigmf(filename: str, storage_path: str, scope: str):
@@ -536,7 +397,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
@@ -551,4 +412,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,12 +1,10 @@
# SPDX-License-Identifier: LicenseRef-CSSL-1.0
# ---------------------------------------------------------------------
# brief Sync service between captured Data from 5GNR gNB and UE
# brief Sync service between captured Data from 5G NR gNB and UE
import struct
from lib import data_recording_messages_def
DEBUG_POW_MEAS_SYNC = True
from lib import shared_memory_interface
# check if first frame ahead:
@@ -29,34 +27,55 @@ def is_frame_ahead(frame1, frame2, max_frame=1023):
# find the frame and slot start
def find_frame_slot_start(dataset1_start, dataset2_start):
def find_frame_slot_start(dataset1_start_info, dataset2_start_info, is_dataset2_pow_monitoring):
"""
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["frame"] == dataset2_start["frame"]:
if dataset1_start_info["frame"] == dataset2_start_info["frame"] :
frame_diff = 0
frame_start = dataset1_start["frame"]
slot_start = max(dataset1_start["slot"], dataset2_start["slot"])
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"])
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.
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.
# 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:
@@ -70,54 +89,34 @@ def find_frame_slot_start(dataset1_start, dataset2_start):
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.
# 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
# Sync data between gNB and UE
def sync_gnb_ue_captured_data(shm_reading_gnb, shm_reading_ue):
"""
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())
node_shm_readings: dict {node_id: shm_reading}
# 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)
Returns:
sync_info: dict with 'frame' and 'slot' of the common sync point
"""
sync_header_msg, sync_header_msg_length = (
data_recording_messages_def.get_sync_header_msg_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']}")
return sync_info

View File

@@ -0,0 +1,189 @@
# 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.json"
main_config = "/home/user/workarea/oai_recorded_data/config_data_recording_app_extended.json"
# calculate BER stats
calculate_stats(main_config)

View File

@@ -8,7 +8,7 @@
#include "spsc_q.h"
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz)
{
/* internally, use one element more: the ringbuffer is full if
* (write_idx + 1) % cnt == read_idx, so it's full if one element is still
@@ -17,7 +17,8 @@ spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz)
cnt += 1;
spsc_q_t rb = {.cnt = cnt, .elsiz = elsiz};
rb.buf = calloc(cnt, elsiz);
return rb;
*rbn = rb;
return rb.buf != NULL;
}
void spsc_q_free(spsc_q_t *rb)

View File

@@ -7,11 +7,13 @@
#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 {
@@ -23,7 +25,7 @@ typedef struct spsc_q {
atomic_size_t read_idx;
} spsc_q_t;
spsc_q_t spsc_q_alloc(size_t cnt, size_t elsiz);
bool spsc_q_alloc(spsc_q_t *rbn, size_t cnt, size_t elsiz);
void spsc_q_free(spsc_q_t *rb);
bool spsc_q_put(spsc_q_t *rb, const void *src, size_t elsiz);

View File

@@ -8,7 +8,8 @@ extern "C" {
}
TEST(spsc_q, basic_test) {
spsc_q_t rb = spsc_q_alloc(2, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 2, sizeof(int)));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
@@ -26,7 +27,8 @@ TEST(spsc_q, basic_test) {
}
TEST(spsc_q, cont_write) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int a = 1;
for (int i = 0; i < 1111; ++i) {
@@ -57,7 +59,8 @@ bool count(const void *data, void *user)
return true; /* count all */
}
TEST(spsc_q, iterator) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -85,7 +88,8 @@ bool drop(const void *data, void *user)
return true; /* drop all */
}
TEST(spsc_q, drop) {
spsc_q_t rb = spsc_q_alloc(10, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, 10, sizeof(int)));
int n = 8;
for (int i = 0; i < n; ++i)
EXPECT_TRUE(spsc_q_put(&rb, &i, sizeof(i)));
@@ -104,7 +108,8 @@ TEST(spsc_q, drop) {
TEST(spsc_q, full) {
int n = 3;
spsc_q_t rb = spsc_q_alloc(n, sizeof(int));
spsc_q_t rb;
EXPECT_TRUE(spsc_q_alloc(&rb, n, sizeof(int)));
int a = 1;
EXPECT_TRUE(spsc_q_put(&rb, &a, sizeof(a)));
a++;

View File

@@ -40,9 +40,9 @@ static void *consumer(void *arg)
static void run_test(int range, int q_size)
{
q_args_t args = {
.q = spsc_q_alloc(q_size, sizeof(int)),
.range = range,
};
spsc_q_alloc(&args.q, q_size, sizeof(int));
pthread_t p, c;
int ret;

View File

@@ -262,28 +262,7 @@ 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

@@ -1,14 +0,0 @@
/*
* 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

@@ -301,7 +301,7 @@ static int trigger_ngap_pdu_session_release(char *buf, int debug, telnet_printfu
ERROR_MSG_RET("Missing input. Usage: trigger_pdu_session_release [ue_id=gNB_ue_ngap_id(int,opt)],pdusession_id(int)[,pdusession_id(int)...]\n");
}
char *tokens[NGAP_MAX_PDU_SESSION + 1];
char *tokens[NR_MAX_NB_PDU_SESSIONS + 1];
int count = 0;
for (char *tok = strtok(buf, ","); tok != NULL && count < (int)sizeofArray(tokens); tok = strtok(NULL, ",")) {

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

@@ -65,36 +65,3 @@ sequenceDiagram
u->>c: BEARER CONTEXT MODIFICATION RESPONSE
Note over c: e1apCUCP_handle_BEARER_CONTEXT_MODIFICATION_RESPONSE
```
## PDU Session Release
```mermaid
sequenceDiagram
participant AMF
participant CUCP
participant CUUP
participant DU
participant UE
AMF->>CUCP: NG PDU SESSION RESOURCE RELEASE COMMAND
Note over CUCP: ngap_gNB_handle_pdusession_release_command
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND
Note over CUCP: set status PDU_SESSION_STATUS_TORELEASE
CUCP->>CUUP: E1 Bearer Context Modification Request
Note over CUUP: release_gtpu_tunnel (GTP tunnel, PDCP, SDAP)
CUUP->>CUCP: E1 Bearer Context Modification Response
Note over CUCP: rrc_gNB_send_f1_drb_release_request
CUCP->>DU: F1 UE Context Modification Request
Note over DU: handle_ue_context_drbs_release (release in MAC/RLC)
DU->>CUCP: F1 UE Context Modification Response
Note over CUCP: rrc_CU_process_ue_context_modification_response
Note over CUCP: replace existing CellGroupConfig
Note over CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration
CUCP->>UE: RRCReconfiguration (DRB release list, NAS PDU)
UE->>CUCP: RRCReconfigurationComplete
Note over CUCP: handle_rrcReconfigurationComplete
Note over CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE
CUCP->>AMF: NG PDU SESSION RESOURCE RELEASE RESPONSE
Note over CUCP: rm_drbs_by_pdusession (from stored RRC list)
Note over CUCP: rm_pduSession (from stored RRC list)
```

View File

@@ -1,163 +0,0 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# LDPC offload with the XDMA driver
[TOC]
This documentation aims to provide a tutorial for using Xilinx PCIe-XDMA based FPGA LDPC decoding within OAI. LDPC decoding is offloaded to FPGA.
## XDMA Driver Build & Install
- Get XDMA driver source
```bash
git clone https://github.com/Xilinx/dma_ip_drivers.git
cd dma_ip_drivers/XDMA/linux-kernel
```
The *xdma_driver* directory contains the following:
```bash
dma_ip_drivers/XDMA/linux-kernel/
├── COPYING
├── include
├── LICENSE
├── readme.txt
├── RELEASE
├── tests
├── tools
└── xdma
```
Before building the driver, ensure that your system recognizes the Xilinx device. You can check this using the `lspci` command:
```bash
$ lspci | grep Xilinx
01:00.0 Serial controller: Xilinx Corporation Device 8038
```
Building and Installing the Driver
```bash
cd ~/dma_ip_drivers/XDMA/linux-kernel/xdma
make clean
make
# install to make the driver loading automatically at system startup
sudo make install
```
Load the Driver
```bash
cd ~/dma_ip_drivers/XDMA/linux-kernel/tests
sudo ./load_driver.sh
```
## OAI Build
```bash
# Get openairinterface5g source code
git clone https://gitlab.eurecom.fr/oai/openairinterface5g.git ~/openairinterface5g
cd ~/openairinterface5g
# Install OAI dependencies
cd ~/openairinterface5g/cmake_targets
./build_oai -I
# Build OAI gNB & UE
cd ~/openairinterface5g
source oaienv
cd cmake_targets
./build_oai --ninja -w SIMU --gNB --nrUE -P --build-lib "ldpc_xdma" -C -c
```
Shared object file *libldpc_xdma.so* is created during the compilation. This object is conditionally compiled. Selection of the library to compile is done using `--build-lib ldpc_xdma`.
## 5G PHY simulators
The simulated test uses the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface. Additionally, the option `--nrLDPC_coding_xdma.num_threads_prepare` is used to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
Another way to activate the feature is to add the `xdma.conf` file with the following content:
```
nrLDPC_coding_xdma : {
num_threads_prepare : 2;
};
loader : {
ldpc : {
shlibversion : "_xdma";
};
};
```
and use option `-O xdma.conf`.
### nr_ulsim test
Example command for running nr_ulsim with LDPC decoding offload to the FPGA:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```
sudo ./nr_ulsim -n100 -m28 -r273 -R273 -s22 -I10 -C8 -P -O xdma.conf
```
## Run
Both gNB and nrUE use the option `--loader.ldpc.shlibversion _xdma` to select the XDMA version for loading into the LDPC interface and `--nrLDPC_coding_xdma.num_threads_prepare` to specify the number of threads for preparing data before the LDPC processing, specifically for the deinterleaving and rate matching parts.
Another way to activate the feature is to add the following content to the `.conf` file you want to use:
```
nrLDPC_coding_xdma : {
num_threads_prepare : 2;
};
loader : {
ldpc : {
shlibversion : "_xdma";
};
};
```
and use option `-O *.conf`.
### gNB
Example command using rfsim:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```bash
sudo ./nr-softmodem --rfsim --log_config.global_log_options level,nocolor,time -O ../../../ci-scripts/conf_files/gnb.sa.band78.106prb.rfsim.conf
```
if you have added the configuration to the `.conf` file.
### UE
Example command using rfsim:
```bash
cd ~/openairinterface5g/cmake_targets/ran_build/build
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100 --loader.ldpc.shlibversion _xdma --nrLDPC_coding_xdma.num_threads_prepare 2
```
or
```bash
sudo ./nr-uesoftmodem --rfsim -r 106 --numerology 1 --band 78 -C 3319680000 --ue-nb-ant-tx 1 --ue-nb-ant-rx 1 -O ../../../ci-scripts/conf_files/nrue1.uicc.cluCN.conf --rfsimulator.[0].serveraddr 10.201.1.100
```
if you have added the configuration to the `.conf` file.

View File

@@ -62,19 +62,22 @@ We tested the category A radio units listed below.
|Vendor |Software Version |
|-----------------|---------------------------------------------|
|VVDN LPRU |03-v3.0.5 |
|LiteON RU |01.00.08/02.00.03/02.00.10 |
|Benetel 650 |RAN650-1v1.0.4-dda1bf5/RAN650-1v1.2.2-2fa04bc/RAN650-1v1.4.2-NM-c48047d|
|Benetel 550 |RAN550-1v1.0.4-605a25a/RAN550-1v1.2.2-2fa04bc/RAN550-1v1.4.1-M-25fa970/RAN550-1v2.0.5-M-92a9d2c|
|LiteON RU FR1 |01.00.08/02.00.03/02.00.10 |
|LiteON RU FR2 |02.00.07 |
|Metanoia RU FR1 |2.0.6 |
|Benetel 650 |RAN650-1v2.1.0-M-0820797 |
|Benetel 550 |RAN550-1v2.1.0-M-0820797 |
|Foxconn RPQN |v3.1.15q.551_rc10 |
|Microamp RU |0.1.174 |
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).
**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".
### Configure your server
@@ -274,7 +277,7 @@ timedatectl set-ntp false
### DPDK (Data Plane Development Kit)
Download DPDK version 20.11.9.
Download DPDK version 20.11.9 (F release) or 24.11.4 (K release).
```bash
# on debian
@@ -282,7 +285,8 @@ 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
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
```
#### DPDK Compilation and Installation
@@ -290,10 +294,11 @@ wget http://fast.dpdk.org/rel/dpdk-20.11.9.tar.xz
```bash
# Installing meson : it should pull ninja-build and compiler packages
# on debian
sudo apt install meson
sudo apt install python3-pyelftools meson
# on Fedora/RHEL
sudo dnf install meson
tar xvf dpdk-20.11.9.tar.xz && cd dpdk-stable-20.11.9
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
meson build
ninja -C build
@@ -359,7 +364,8 @@ pkg-config --libs libdpdk --static
Go back to the version folder you used to build and install
```
cd ~/dpdk-stable-20.11.9
cd ~/dpdk-stable-20.11.9 # F release
cd ~/dpdk-stable-24.11.4 # K release
sudo ninja deinstall -C build
```
@@ -384,6 +390,13 @@ 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
@@ -398,6 +411,7 @@ 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
@@ -483,23 +497,13 @@ 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.
@@ -616,9 +620,9 @@ jumboframe 1 # enable jumbo frame
###### FR2
The OAI configuration file [`gnb.sa.band257.66prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.66prb.fhi72.2x2-liteon.conf) corresponds to:
- TDD pattern `DDDDDDDSUU`, 1.25ms
- Bandwidth 100MHz
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-liteon.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-liteon.conf) corresponds to:
- TDD pattern `DDDSU`, 0.625ms
- Bandwidth 200MHz
- FW v02.00.07
- DL uses jumbo frame, UL uses standard MTU of 1500 bytes
@@ -629,82 +633,481 @@ eAXC_id 0 1 # set PRACH eAxC IDs
...
```
#### MICROAMP FR2
#### Microamp FR2
#### Firmware starting from 0.1.174
Requirements:
- sshpass:
```bash
#RHEL
sudo dnf install sshpass -y
#Ubuntu
sudo apt install sshpass -y
```
To check PTP status, you can use the following command:
```bash
sshpass -p microampcfg ssh remctl@<RU_IP_ADDR> "logs-ptp"
```
<details>
<summary>PTP output will be similar to:</summary>
```
Apr 27 11:58:35 bbv1 ptp4l[74545]: ptp4l[357120.237]: rms 8 max 16 freq -100 +/- 142 delay 1183 +/- 0
Apr 27 11:58:34 bbv1 ptp4l[74545]: ptp4l[357119.114]: rms 1 max 1 freq +189 +/- 25 delay 1183 +/- 0
Apr 27 11:58:33 bbv1 ptp4l[74545]: ptp4l[357117.991]: rms 1 max 1 freq +99 +/- 30 delay 1183 +/- 0
Apr 27 11:58:32 bbv1 ptp4l[74545]: ptp4l[357116.869]: rms 1 max 1 freq +22 +/- 18 delay 1183 +/- 0
Apr 27 11:58:31 bbv1 ptp4l[74545]: ptp4l[357115.746]: rms 1 max 1 freq -51 +/- 20 delay 1183 +/- 0
Apr 27 11:58:30 bbv1 ptp4l[74545]: ptp4l[357114.624]: rms 1 max 2 freq -126 +/- 29 delay 1183 +/- 0
Apr 27 11:58:29 bbv1 ptp4l[74545]: ptp4l[357113.501]: rms 8 max 16 freq -16 +/- 200 delay 1184 +/- 0
Apr 27 11:58:28 bbv1 ptp4l[74545]: ptp4l[357112.378]: rms 1 max 2 freq +164 +/- 29 delay 1183 +/- 0
Apr 27 11:58:26 bbv1 ptp4l[74545]: ptp4l[357111.256]: rms 1 max 2 freq +54 +/- 39 delay 1183 +/- 0
Apr 27 11:58:25 bbv1 ptp4l[74545]: ptp4l[357110.133]: rms 1 max 2 freq -34 +/- 19 delay 1183 +/- 0
Apr 27 11:58:24 bbv1 ptp4l[74545]: ptp4l[357109.010]: rms 1 max 2 freq -112 +/- 26 delay 1183 +/- 0
Apr 27 11:58:23 bbv1 ptp4l[74545]: ptp4l[357107.888]: rms 8 max 16 freq -48 +/- 174 delay 1183 +/- 0
Apr 27 11:58:22 bbv1 ptp4l[74545]: ptp4l[357106.766]: rms 1 max 1 freq +197 +/- 25 delay 1183 +/- 0
Apr 27 11:58:21 bbv1 ptp4l[74545]: ptp4l[357105.642]: rms 1 max 2 freq +149 +/- 26 delay 1183 +/- 0
Apr 27 11:58:20 bbv1 ptp4l[74545]: ptp4l[357104.519]: rms 1 max 2 freq +66 +/- 27 delay 1183 +/- 0
Apr 27 11:58:19 bbv1 ptp4l[74545]: ptp4l[357103.396]: rms 1 max 2 freq -17 +/- 24 delay 1183 +/- 0
Apr 27 11:58:17 bbv1 ptp4l[74545]: ptp4l[357102.273]: rms 1 max 1 freq -94 +/- 23 delay 1183 +/- 0
Apr 27 11:58:16 bbv1 ptp4l[74545]: ptp4l[357101.150]: rms 7 max 16 freq +107 +/- 180 delay 1183 +/- 0
Apr 27 11:58:15 bbv1 ptp4l[74545]: ptp4l[357100.027]: rms 1 max 2 freq +166 +/- 11 delay 1184 +/- 0
Apr 27 11:58:14 bbv1 ptp4l[74545]: ptp4l[357098.906]: rms 1 max 1 freq +140 +/- 21 delay 1183 +/- 0
```
</details>
###### RU configuration
You can use the following command to display the current RU configuration:
```bash
sshpass -p microampcfg ssh remctl@<RU_IP_ADDR> get-cfg
```
<details>
<summary>
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-microamp.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-microamp.conf) corresponds to the following RU configuration:
</summary>
```
PRACH0 CC ID: 0
PRACH0 RU port ID: 0
PRACH1 CC ID: 1
PRACH1 RU port ID: 1
Bandwidth: 200M [Hz]
Carrier frequency: 28049280000.0 [Hz]
Compression enable: True
TDD config: DDDSU
eCPRI VLAN support enable: True
eCPRI VLAN tag: 600
MAC address: 10:70:fd:b8:86:02
VLAN PTP status: ENABLED
VLAN MGMT enabled: ENABLED
State: ENABLED_WITH_VLAN
Dynamic beamforming with mirrored beams
PL MAC address:
RU MAC: 10-70-FD-B8-86-02
DU MAC: 50-7C-6F-31-00-61
RF Power level: -5 dB - relative to maximum
```
</details>
Execute the following command to check how to configure the RU:
```bash
sshpass -p microampcfg ssh remctl@<RU_IP_ADDR> "help"
```
<details>
<summary>Help output will be similar to:</summary>
```
Usage: remctl <subcommand> [options]
Allowed Subcommands:
set-cfg, get-cfg, stat, logs-ptp, reboot, set-power, help, clear-cfg
Allowed 'set-cfg' options:
--bandwidth
--tdd-cfg
--compression-enable
--compression-disable
--ecpri-vlan-tag
--ecpri-vlan-support-enable
--ecpri-vlan-support-disable
--ru-mac
--du-mac
--beamforming
--carrier-freq
--eth-ipv4
--prach0-cc-id
--prach0-ru-port-id
--prach1-cc-id
--prach1-ru-port-id
```
</details>
You can also execute the following command to show fronthaul statistics including on-time/late packet' counters:
```bash
sshpass -p microampcfg ssh remctl@<RU_IP_ADDR> "stat"
```
<details>
<summary>Statistics output will be similar to:</summary>
```
ORAN RX C-plane on time: 71645874
ORAN RX C-plane early: 0
ORAN RX C-plane late: 16
ORAN TX U-plane: 229686268
ORAN RX DL C-plane on time: 53987559
ORAN RX DL C-plane early: 0
ORAN RX DL C-plane late: 12
ORAN RX DL U-plane on time: 701836967
ORAN RX DL U-plane early: 0
ORAN RX DL U-plane late: 52
ORAN RX UL C-plane on time: 17658315
ORAN RX UL C-plane early: 0
ORAN RX UL C-plane late: 4
CLASSIFIER LLC: overflow protection: 0
CLASSIFIER LLC: frame err: 28
CLASSIFIER LLC: packet too long: 0
CLASSIFIER LLC: dropped: 28
CLASSIFIER LLC: passed: 804719078
CLASSIFIER ETH: dst MAC addr err: 0
CLASSIFIER ETH: VLAN LEGACY: 275571
CLASSIFIER ETH: VLAN Dot1Q: 804443507
CLASSIFIER ETH: VLAN Dot1AD: 0
CLASSIFIER ETH: VLAN Tag ID pass: 773482909
CLASSIFIER ETH: VLAN Tag ID error: 0
CLASSIFIER ETH: multicast: 31233828
CLASSIFIER ETH: PTP: 29502626
CLASSIFIER ETH: eCPRI: 773482909
CLASSIFIER ETH: to OS: 31236169
CLASSIFIER ETH: dropped: 0
CLASSIFIER ETH: passed: 804719078
CLASSIFIER ECPRI: prot rev unsupported: 0
CLASSIFIER ECPRI: concat unsupported: 0
CLASSIFIER ECPRI: msg type unsupported: 0
CLASSIFIER ECPRI: pld size err: 0
CLASSIFIER ECPRI: U-Plane: 701837019
CLASSIFIER ECPRI: C-Plane: 71645890
CLASSIFIER ECPRI: delay: 0
CLASSIFIER ECPRI: dropped: 0
CLASSIFIER ECPRI: passed: 773482909
CPLANE PARSER: ul early: 0
CPLANE PARSER: ul late: 4
CPLANE PARSER: ul on time: 17658315
CPLANE PARSER: dl early: 0
CPLANE PARSER: dl late: 12
CPLANE PARSER: dl on time: 53987559
CPLANE PARSER: timing dropped: 16
CPLANE PARSER: timing passed: 71645874
CPLANE PARSER: bad du port id: 0
CPLANE PARSER: bad bandsector id: 0
CPLANE PARSER: bad cc id: 663059
CPLANE PARSER: bad ru port id: 2794
CPLANE PARSER: bad sequence id: 0
CPLANE PARSER: bad e bit: 0
CPLANE PARSER: bad subsequence id: 0
CPLANE PARSER: ecpri transport header dropped: 665623
CPLANE PARSER: ecpri transport header passed: 70980251
CPLANE PARSER: bad payloadversion: 0
CPLANE PARSER: bad filterindex: 0
CPLANE PARSER: bad subframeid: 0
CPLANE PARSER: bad slotid: 0
CPLANE PARSER: bad startsymbolid: 0
CPLANE PARSER: bad numberofsections: 0
CPLANE PARSER: bad sectiontype: 0
CPLANE PARSER: bad timeoffset: 0
CPLANE PARSER: bad fftsize: 0
CPLANE PARSER: bad subcarrierspacing: 0
CPLANE PARSER: bad cplength: 0
CPLANE PARSER: bad udiqwidth: 0
CPLANE PARSER: radio aplication header dropped: 0
CPLANE PARSER: radio aplication header passed: 70980251
CPLANE PARSER: bad rb: 0
CPLANE PARSER: bad startprbc: 0
CPLANE PARSER: bad numprbc: 0
CPLANE PARSER: bad remask: 0
CPLANE PARSER: bad numsymbol: 0
CPLANE PARSER: bad ef: 0
CPLANE PARSER: bad freqoffset: 0
CPLANE PARSER: common section fields dropped: 0
CPLANE PARSER: common section fields passed: 70980251
CPLANE PARSER: bad section to symbol map: 0
CPLANE PARSER: dropped: 0
CPLANE PARSER: passed: 70980251
CPLANE PARSER: expected uplanes number: 701813698
UPLANE PARSER: sequence ID err: 0
UPLANE PARSER: IQ pld size err: 0
UPLANE PARSER: C-Plane match err: 114225
UPLANE PARSER: eAxC ID unsupported: 24631
UPLANE PARSER: any value unsupported: 24631
UPLANE PARSER: dropped: 138886
UPLANE PARSER: passed: 701698133
UPLANE ENCAPSULATOR 0: false drop error: 0
UPLANE ENCAPSULATOR 0: passed: 94598642
UPLANE ENCAPSULATOR 1: false drop error: 0
UPLANE ENCAPSULATOR 1: passed: 94598642
UPLANE ENCAPSULATOR 2: false drop error: 0
UPLANE ENCAPSULATOR 2: passed: 0
UPLANE ENCAPSULATOR 3: false drop error: 0
UPLANE ENCAPSULATOR 3: passed: 0
COMMON COMMON: symbol counter: 57757250505
DSP CHAIN 0: dl fft overflow: 0
DSP CHAIN 0: ul fft overflow: 0
DSP CHAIN 1: dl fft overflow: 0
DSP CHAIN 1: ul fft overflow: 0
```
</details>
##### Firmware older than 0.1.174
Interaction with RU is performed using `rucfg` utility provided by Microamp.
To check PTP status, you can use `rucfg ptp`. the output should be similar to:
```bash
[INFO] Check if RU is available
[INFO] RU available
[INFO] Check SSH to RU available
[INFO] SSH to RU available
[INFO] Getting PTP status
[INFO] ptp4l status =
{
Feb 12 16:26:02 bbv1 ptp4l[23822]: ptp4l[6280.658]: rms 0 max 1 freq +2 +/- 7 delay 1184 +/- 0
Feb 12 16:26:00 bbv1 ptp4l[23822]: ptp4l[6279.535]: rms 0 max 1 freq -8 +/- 13 delay 1184 +/- 0
Feb 12 16:25:59 bbv1 ptp4l[23822]: ptp4l[6278.413]: rms 1 max 1 freq -35 +/- 13 delay 1184 +/- 0
Feb 12 16:25:58 bbv1 ptp4l[23822]: ptp4l[6277.290]: rms 0 max 1 freq -68 +/- 9 delay 1184 +/- 0
Feb 12 16:25:57 bbv1 ptp4l[23822]: ptp4l[6276.167]: rms 1 max 1 freq -69 +/- 10 delay 1184 +/- 0
Feb 12 16:25:56 bbv1 ptp4l[23822]: ptp4l[6275.044]: rms 0 max 1 freq -46 +/- 9 delay 1184 +/- 0
Feb 12 16:25:55 bbv1 ptp4l[23822]: ptp4l[6273.921]: rms 1 max 1 freq -65 +/- 10 delay 1184 +/- 0
Feb 12 16:25:54 bbv1 ptp4l[23822]: ptp4l[6272.800]: rms 0 max 1 freq -91 +/- 6 delay 1184 +/- 0
Feb 12 16:25:53 bbv1 ptp4l[23822]: ptp4l[6271.677]: rms 4 max 17 freq -107 +/- 48 delay 1184 +/- 0
Feb 12 16:25:52 bbv1 ptp4l[23822]: ptp4l[6270.554]: rms 6 max 17 freq +179 +/- 106 delay 1184 +/- 0
Feb 12 16:25:50 bbv1 ptp4l[23822]: ptp4l[6269.431]: rms 1 max 2 freq +191 +/- 12 delay 1184 +/- 0
Feb 12 16:25:49 bbv1 ptp4l[23822]: ptp4l[6268.308]: rms 1 max 1 freq +119 +/- 21 delay 1184 +/- 0
Feb 12 16:25:48 bbv1 ptp4l[23822]: ptp4l[6267.186]: rms 1 max 1 freq +103 +/- 13 delay 1184 +/- 0
Feb 12 16:25:47 bbv1 ptp4l[23822]: ptp4l[6266.063]: rms 7 max 17 freq -101 +/- 160 delay 1184 +/- 0
Feb 12 16:25:46 bbv1 ptp4l[23822]: ptp4l[6264.940]: rms 7 max 17 freq -65 +/- 154 delay 1184 +/- 0
Feb 12 16:25:45 bbv1 ptp4l[23822]: ptp4l[6263.817]: rms 1 max 2 freq +176 +/- 33 delay 1183 +/- 0
Feb 12 16:25:44 bbv1 ptp4l[23822]: ptp4l[6262.695]: rms 0 max 1 freq +100 +/- 7 delay 1184 +/- 0
Feb 12 16:25:43 bbv1 ptp4l[23822]: ptp4l[6261.572]: rms 1 max 2 freq +56 +/- 34 delay 1184 +/- 0
Feb 12 16:25:41 bbv1 ptp4l[23822]: ptp4l[6260.449]: rms 1 max 1 freq -37 +/- 14 delay 1184 +/- 0
Feb 12 16:25:40 bbv1 ptp4l[23822]: ptp4l[6259.326]: rms 7 max 16 freq +114 +/- 149 delay 1183 +/- 0
}
To check PTP status, you can use `rucfg ptp`.
```
##### RU configuration
<details>
<summary>PTP output will be similar to:</summary>
You can use `rucfg show` to display the current RU configuration.
```
[INFO] Check if RU is available
[INFO] RU available
[INFO] Check SSH to RU available
[INFO] SSH to RU available
[INFO] Getting PTP status
[INFO] ptp4l status =
{
Feb 12 16:26:02 bbv1 ptp4l[23822]: ptp4l[6280.658]: rms 0 max 1 freq +2 +/- 7 delay 1184 +/- 0
Feb 12 16:26:00 bbv1 ptp4l[23822]: ptp4l[6279.535]: rms 0 max 1 freq -8 +/- 13 delay 1184 +/- 0
Feb 12 16:25:59 bbv1 ptp4l[23822]: ptp4l[6278.413]: rms 1 max 1 freq -35 +/- 13 delay 1184 +/- 0
Feb 12 16:25:58 bbv1 ptp4l[23822]: ptp4l[6277.290]: rms 0 max 1 freq -68 +/- 9 delay 1184 +/- 0
Feb 12 16:25:57 bbv1 ptp4l[23822]: ptp4l[6276.167]: rms 1 max 1 freq -69 +/- 10 delay 1184 +/- 0
Feb 12 16:25:56 bbv1 ptp4l[23822]: ptp4l[6275.044]: rms 0 max 1 freq -46 +/- 9 delay 1184 +/- 0
Feb 12 16:25:55 bbv1 ptp4l[23822]: ptp4l[6273.921]: rms 1 max 1 freq -65 +/- 10 delay 1184 +/- 0
Feb 12 16:25:54 bbv1 ptp4l[23822]: ptp4l[6272.800]: rms 0 max 1 freq -91 +/- 6 delay 1184 +/- 0
Feb 12 16:25:53 bbv1 ptp4l[23822]: ptp4l[6271.677]: rms 4 max 17 freq -107 +/- 48 delay 1184 +/- 0
Feb 12 16:25:52 bbv1 ptp4l[23822]: ptp4l[6270.554]: rms 6 max 17 freq +179 +/- 106 delay 1184 +/- 0
Feb 12 16:25:50 bbv1 ptp4l[23822]: ptp4l[6269.431]: rms 1 max 2 freq +191 +/- 12 delay 1184 +/- 0
Feb 12 16:25:49 bbv1 ptp4l[23822]: ptp4l[6268.308]: rms 1 max 1 freq +119 +/- 21 delay 1184 +/- 0
Feb 12 16:25:48 bbv1 ptp4l[23822]: ptp4l[6267.186]: rms 1 max 1 freq +103 +/- 13 delay 1184 +/- 0
Feb 12 16:25:47 bbv1 ptp4l[23822]: ptp4l[6266.063]: rms 7 max 17 freq -101 +/- 160 delay 1184 +/- 0
Feb 12 16:25:46 bbv1 ptp4l[23822]: ptp4l[6264.940]: rms 7 max 17 freq -65 +/- 154 delay 1184 +/- 0
Feb 12 16:25:45 bbv1 ptp4l[23822]: ptp4l[6263.817]: rms 1 max 2 freq +176 +/- 33 delay 1183 +/- 0
Feb 12 16:25:44 bbv1 ptp4l[23822]: ptp4l[6262.695]: rms 0 max 1 freq +100 +/- 7 delay 1184 +/- 0
Feb 12 16:25:43 bbv1 ptp4l[23822]: ptp4l[6261.572]: rms 1 max 2 freq +56 +/- 34 delay 1184 +/- 0
Feb 12 16:25:41 bbv1 ptp4l[23822]: ptp4l[6260.449]: rms 1 max 1 freq -37 +/- 14 delay 1184 +/- 0
Feb 12 16:25:40 bbv1 ptp4l[23822]: ptp4l[6259.326]: rms 7 max 16 freq +114 +/- 149 delay 1183 +/- 0
}
```
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-microamp.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-microamp.conf) corresponds to the following RU configuration:
</details>
###### RU configuration
You can use `rucfg show` command to display the current RU configuration.
<details>
<summary>
The OAI configuration file [`gnb.sa.band257.132prb.fhi72.2x2-microamp.conf`](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band257.132prb.fhi72.2x2-microamp.conf) corresponds to the following RU configuration:
</summary>
```
[INFO] Check if RU is available
[INFO] RU available
[INFO] Check SSH to RU available
[INFO] SSH to RU available
[INFO] Downloading oran_autostart
[INFO] Downloading ructl_config.sh
[INFO] Downloading udc_pll_configurator_startup
[INFO] Downloaded Cellbox config =
{
eCPRI Compression: True
RF Bandwidth: 200MHz
CC Bandwidth: 200MHz
CCs: 1
LO frequency: 7.91642667e9
UL compensation frequency: 28.04928e9
DL compensation frequency: -28.04928e9
RU MAC: 10:70:FD:B8:86:02
DU MAC: 50:7C:6F:31:00:61
TDD config: dddsu
RF Power level: -5 dB - relative to maximum
VLAN ORAN: Enabled, tag: 600
VLAN PTP: Enabled, tag: 1
VLAN MGMT: False
Beamforming: dynamic-mirrored-beam
}
```
</details>
```bash
[INFO] Check if RU is available
[INFO] RU available
[INFO] Check SSH to RU available
[INFO] SSH to RU available
[INFO] Downloading oran_autostart
[INFO] Downloading ructl_config.sh
[INFO] Downloading udc_pll_configurator_startup
[INFO] Downloaded Cellbox config =
{
eCPRI Compression: True
RF Bandwidth: 200MHz
CC Bandwidth: 200MHz
CCs: 1
LO frequency: 7.91642667e9
UL compensation frequency: 28.04928e9
DL compensation frequency: -28.04928e9
RU MAC: 10:70:FD:B8:86:02
DU MAC: 50:7C:6F:31:00:61
TDD config: dddsu
RF Power level: -5 dB - relative to maximum
VLAN ORAN: Enabled, tag: 600
VLAN PTP: Enabled, tag: 1
VLAN MGMT: False
Beamforming: dynamic-mirrored-beam
}
```
Execute `rucfg config -h` to check how to configure the RU.
<details>
<summary>Help output will be similar to:</summary>
```
usage: rucfg config [-h] [-f frequency] [-b bandwidth] [-t TDD pattern] [-p RU Power]
[--cc carrier components] [--compression compression] [--vlan-oran vlan-oran]
[--vlan-ptp vlan-ptp] [--mac DU MAC address]
options:
-h, --help show this help message and exit
-f frequency, --freq frequency
Sets the 5G NR frequency. Allowed range: 24.0-29.9 GHz, default: <no conf
change>
-b bandwidth, --bandwidth bandwidth
Sets RF bandwidth 100/200 [MHz] per CC, default: <no conf change>
-t TDD pattern, --tdd TDD pattern
Sets TDD pattern DDDSU/DDSUU/DSUUU, default: <no conf change>
-p RU Power, --power RU Power
Sets Power in (0-100), default: <no conf change>
--cc carrier components
Sets number of carrier components 1/2, default: <no conf change>
--compression compression
Sets eCPRI compression True/False, default: <no conf change>
--vlan-oran vlan-oran
Sets ORAN VLAN to None or <TAG>, default: <no conf change>
--vlan-ptp vlan-ptp Sets PTP VLAN to None or <TAG>, default: <no conf change>
--mac DU MAC address Sets DU MAC address (where RU sends eCPRI packets), format
AA:BB:CC:DD:EE:FF, default: <no conf change>
```
</details>
You can also execute `rucfg stats` to show fronthaul statistics including on-time/late packet' counters.
<details>
<summary>Statistics output will be similar to:</summary>
```
[INFO] Check if RU is available
[INFO] RU available
[INFO] Check SSH to RU available
[INFO] SSH to RU available
[INFO] Getting RU stats
[WARN] Received non-zero rc from ructl: 255
[INFO] ru stats =
{
Preaccumulator overflow detected
ORAN RX C-plane on time: 5442384
ORAN RX C-plane early: 0
ORAN RX C-plane late: 4
ORAN TX U-plane: 15908424
ORAN RX DL C-plane on time: 3349166
ORAN RX DL C-plane early: 0
ORAN RX DL C-plane late: 0
ORAN RX DL U-plane on time: 43538742
ORAN RX DL U-plane early: 0
ORAN RX DL U-plane late: 93
ORAN RX UL C-plane on time: 2093218
ORAN RX UL C-plane early: 0
ORAN RX UL C-plane late: 4
CLASSIFIER LLC: overflow protection: 0
CLASSIFIER LLC: frame err: 0
CLASSIFIER LLC: packet too long: 0
CLASSIFIER LLC: dropped: 0
CLASSIFIER LLC: passed: 49044133
CLASSIFIER ETH: dst MAC addr err: 0
CLASSIFIER ETH: VLAN LEGACY: 1524
CLASSIFIER ETH: VLAN Dot1Q: 49042609
CLASSIFIER ETH: VLAN Dot1AD: 0
CLASSIFIER ETH: VLAN Tag ID pass: 48981223
CLASSIFIER ETH: VLAN Tag ID error: 0
CLASSIFIER ETH: multicast: 60260
CLASSIFIER ETH: PTP: 56086
CLASSIFIER ETH: eCPRI: 48981223
CLASSIFIER ETH: to OS: 62910
CLASSIFIER ETH: dropped: 0
CLASSIFIER ETH: passed: 49044133
CLASSIFIER ECPRI: prot rev unsupported: 0
CLASSIFIER ECPRI: concat unsupported: 0
CLASSIFIER ECPRI: msg type unsupported: 0
CLASSIFIER ECPRI: pld size err: 0
CLASSIFIER ECPRI: U-Plane: 43538835
CLASSIFIER ECPRI: C-Plane: 5442388
CLASSIFIER ECPRI: delay: 0
CLASSIFIER ECPRI: dropped: 0
CLASSIFIER ECPRI: passed: 48981223
CPLANE PARSER: ul early: 0
CPLANE PARSER: ul late: 4
CPLANE PARSER: ul on time: 2093218
CPLANE PARSER: dl early: 0
CPLANE PARSER: dl late: 0
CPLANE PARSER: dl on time: 3349166
CPLANE PARSER: timing dropped: 4
CPLANE PARSER: timing passed: 5442384
CPLANE PARSER: bad du port id: 0
CPLANE PARSER: bad bandsector id: 0
CPLANE PARSER: bad cc id: 0
CPLANE PARSER: bad ru port id: 0
CPLANE PARSER: bad sequence id: 0
CPLANE PARSER: bad e bit: 0
CPLANE PARSER: bad subsequence id: 0
CPLANE PARSER: ecpri transport header dropped: 0
CPLANE PARSER: ecpri transport header passed: 5442384
CPLANE PARSER: bad payloadversion: 0
CPLANE PARSER: bad filterindex: 0
CPLANE PARSER: bad subframeid: 0
CPLANE PARSER: bad slotid: 0
CPLANE PARSER: bad startsymbolid: 0
CPLANE PARSER: bad numberofsections: 0
CPLANE PARSER: bad sectiontype: 0
CPLANE PARSER: bad timeoffset: 0
CPLANE PARSER: bad fftsize: 0
CPLANE PARSER: bad subcarrierspacing: 0
CPLANE PARSER: bad cplength: 0
CPLANE PARSER: bad udiqwidth: 0
CPLANE PARSER: radio aplication header dropped: 0
CPLANE PARSER: radio aplication header passed: 5442384
CPLANE PARSER: bad rb: 0
CPLANE PARSER: bad startprbc: 0
CPLANE PARSER: bad numprbc: 0
CPLANE PARSER: bad remask: 0
CPLANE PARSER: bad numsymbol: 0
CPLANE PARSER: bad ef: 0
CPLANE PARSER: bad freqoffset: 0
CPLANE PARSER: common section fields dropped: 0
CPLANE PARSER: common section fields passed: 5442384
CPLANE PARSER: bad section to symbol map: 0
CPLANE PARSER: dropped: 0
CPLANE PARSER: passed: 5442384
UPLANE PARSER: sequence ID err: 0
UPLANE PARSER: IQ pld size err: 0
UPLANE PARSER: C-Plane match err: 7138
UPLANE PARSER: eAxC ID unsupported: 0
UPLANE PARSER: any value unsupported: 0
UPLANE PARSER: dropped: 7231
UPLANE PARSER: passed: 43531604
UPLANE ENCAPSULATOR 0: false drop error: 0
UPLANE ENCAPSULATOR 0: passed: 6698292
UPLANE ENCAPSULATOR 1: false drop error: 0
UPLANE ENCAPSULATOR 1: passed: 6698292
UPLANE ENCAPSULATOR 2: false drop error: 0
UPLANE ENCAPSULATOR 2: passed: 0
UPLANE ENCAPSULATOR 3: false drop error: 0
UPLANE ENCAPSULATOR 3: passed: 0
COMMON COMMON: symbol counter: 111095058
}
```
</details>
#### VVDN LPRU
**Version 3.x**
@@ -843,6 +1246,42 @@ sudo ./ru_emulator -c <path-to/protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml>
Finally, start the OAI gNB.
#### WNC R1220
**Version 1.9.0**
The OAI configuration file [gnb.sa.band77.273prb.fhi72.4x4-wnc.conf](../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band77.273prb.fhi72.4x4-wnc.conf) corresponds to:
- TDD pattern `DDDDDDSUUU`, 5ms
- Bandwidth 100MHz
- 4TX4R
##### RU configuration
After switching on or rebooting the RU, you can check the RU PTP status with `show ptp clock` to make sure it's locked and run `show running-config` to check the current configuration.
To enable RU transmission, use the `radio enable` command. Then verify the configuration with `show running-config`, which should reflect the enabled state:
```
radio 1
no shutdown
```
The required parameters to configure are:
1. `center-frequency` → 3849990
2. `transmit-power` → 24
3. `lna-shutdown`**disabled**
4. `transport-interface` → configure the sub-interface (VLAN ID is defined during sub-interface creation)
5. `transport-peer-mac` → must match the DU MAC address
6. `phase-compensation`**enabled**
7. `bandwidth` → 100
8. `sub-carrier` → 30
9. `compress-oran-compliant`**enabled**
**Note**
* Ensure that the VLAN configuration and MAC addressing are consistent with the DU setup.
* The RU must be PTP synchronized before starting the gNB.
* After reboot, the RU loads its `startup-config`. To save the current configuration, use `copy running-config startup-config`.
## Configure Network Interfaces and DPDK VFs
The 7.2 fronthaul uses the xran library, which requires DPDK. In this step, we
@@ -1494,7 +1933,9 @@ sudo ldconfig
If you would like to install these libraries in the custom path, please replace `/usr/local` default path to e.g. `/opt/mplane-v2`.
## Benetel O-RU
Note: RAN550/650 v1.2.2 and v1.4.1 have been successfully tested.
Note: RAN550-1v2.1.0-M-0820797 has been successfully tested.
We added a [CI M-plane pipeline](https://jenkins-oai.eurecom.fr/job/RAN-SA-FHI72-MPLANE-CN5G/) showcasing the M-plane integration.
#### One time steps
Connect to the RU as user `root`, enable the mplane service, and reboot:
@@ -1636,6 +2077,8 @@ 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,6 +108,7 @@ 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

@@ -247,6 +247,242 @@ RRCReestablishmentRequest), the CU updates `du_assoc_id` and sets
`f1_ue_context_active` is false, the CU triggers UE Context Setup on the new
DU per TS 38.401 §8.7.
### PDU Session Management
#### PDU Session Modification
```mermaid
sequenceDiagram
participant UE
participant DU
participant CUCP as CU-CP
participant CUUP as CU-UP
participant AMF
AMF->>CUCP: PDUSessionResourceModifyRequest
Note over CUCP: ngap_gNB_handle_pdusession_modify_request
CUCP->>CUCP: decodePDUSessionResourceModify
CUCP->>CUCP: NGAP_PDUSESSION_MODIFY_REQ
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_MODIFY_REQ
opt UE not found or AMF_UE_ID mismatch
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP (Failed PDU Session)
Note over CUCP: stop further processing
end
loop nb_pdusessions_tomodify
CUCP->>CUCP: Update PDU session DRB/QoS configuration<br/>(add/modify/release)
end
alt !all_failed
Note over CUCP: Pre-RRC step: E1/F1 updates
Note over CUCP: Build E1AP DRB-To-Remove/To-Modify/To-Setup lists<br/>(populated during nr_rrc_update_pdusession)
CUCP->>CUUP: E1 BEARER CONTEXT MOD REQUEST (DRB-To-Remove/To-Modify/To-Setup)
Note over CUUP: e1_bearer_context_modif()<br/>Process DRB modifications, removals, and setups<br/>Update PDCP/SDAP entities, GTP tunnels
CUUP->>CUCP: E1 BEARER CONTEXT MOD RESPONSE
Note over CUCP: rrc_gNB_process_e1_bearer_context_modif_resp<br/>Save F1-U tunnel info for new DRBs<br/>Mark PDU sessions with new DRBs as PDU_SESSION_STATUS_NEW
Note over CUCP, DU: F1-U tunnel changes (new DRBs or DRB releases)
CUCP->>DU: F1 UE Context Modification Request
Note over DU: handle_ue_context_drbs_setup/release
DU->>CUCP: F1 UE Context Modification Response
Note over CUCP: rrc_CU_process_ue_context_modification_response
CUCP->>CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration
Note over CUCP: <br/>Attach DRB_ToReleaseList (if any)<br/>Set transaction ID to RRC_PDUSESSION_MODIFY
CUCP->>DU: rrc_deliver_dl_rrc_message
DU->>UE: RRCReconfiguration (DCCH)
UE->>DU: RRCReconfigurationComplete
DU->>CUCP: F1AP_UL_RRC_MESSAGE
Note over CUCP: rrc_gNB_decode_dcch
Note over CUCP: handle_rrcReconfigurationComplete<br/>Calls rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP<br/>(DRBs already removed earlier in nr_rrc_update_pdusession)
CUCP->>CUCP: rrc_gNB_send_NGAP_PDUSESSION_MODIFY_RESP
loop UE->pduSessions (matching transaction ID)
alt ESTABLISHED
Note over CUCP: Update status to ESTABLISHED<br/>Fill NGAP message (modified)<br/>Include QoS flow list
else PDU_SESSION_STATUS_FAILED
Note over CUCP: Fill NGAP message (failed to modify)<br/>Include cause
end
end
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP
else msg->nb_of_pdusessions_failed > 0
Note over CUCP: PDU Session failed to modify
CUCP->>AMF: NGAP_PDUSESSION_MODIFY_RESP
end
```
#### PDU Session Release
```mermaid
sequenceDiagram
participant AMF
participant CUCP
participant CUUP
participant DU
participant UE
AMF->>CUCP: NG PDU SESSION RESOURCE RELEASE COMMAND
Note over CUCP: ngap_gNB_handle_pdusession_release_command
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_RELEASE_COMMAND
Note over CUCP: set status PDU_SESSION_STATUS_TORELEASE
CUCP->>CUUP: E1 Bearer Context Modification Request
Note over CUUP: release_gtpu_tunnel (GTP tunnel, PDCP, SDAP)
CUUP->>CUCP: E1 Bearer Context Modification Response
Note over CUCP: rrc_send_f1_ue_context_modification_request
CUCP->>DU: F1 UE Context Modification Request
Note over DU: handle_ue_context_drbs_release (release in MAC/RLC)
DU->>CUCP: F1 UE Context Modification Response
Note over CUCP: rrc_CU_process_ue_context_modification_response
Note over CUCP: replace existing CellGroupConfig
Note over CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration
CUCP->>UE: RRCReconfiguration (DRB release list, NAS PDU)
UE->>CUCP: RRCReconfigurationComplete
Note over CUCP: handle_rrcReconfigurationComplete
Note over CUCP: rrc_gNB_send_NGAP_PDUSESSION_RELEASE_RESPONSE
CUCP->>AMF: NG PDU SESSION RESOURCE RELEASE RESPONSE
Note over CUCP: rm_drbs_by_pdusession (from stored RRC list)
Note over CUCP: rm_pduSession (from stored RRC list)
```
### QoS Flows Handling
This section describes the end-to-end handling of QoS flows in the OAI 5G SA implementation. QoS flows are
the finest granularity of QoS differentiation in 5G systems. According to 3GPP specs, each PDU session can
contain multiple QoS flows (maximum 64). Each QoS flow is mapped to exactly one Data Radio Bearer (DRB)
within the gNB, but multiple QoS flows can be mapped to the same DRB. The mapping is determined by the
CU-CP and configured in the CU-UP via E1AP, and in the UE via RRC signaling. Each PDU session is mapped
to a SDAP entity and can contain multiple DRBs, each of which may carry multiple QoS flows.
Key Standards:
- 3GPP TS 23.501: 5G System Architecture (6.2.5, Quality of service)
- 3GPP TS 37.324: SDAP Protocol (QoS flow to DRB mapping, 5.3)
- 3GPP TS 38.463: E1AP (Bearer Context Management with QoS flows)
- 3GPP TS 29.281: GTP-U (QFI marking)
- 3GPP TS 38.331: RRC (Radio Bearer Configuration)
Trigger: PDU Session Establishment or Modification from 5G Core (AMF/SMF)
The PDU session setup flow in OAI begins when the gNB receives an NGAP PDU Session Resource Setup Request
from the AMF, containing session parameters and QoS flow information. These flows are stored in the UE
context and passed to the RRC layer, which maps each QoS flow to a DRB and prepares the E1AP Bearer
Context Setup Request for the CU-UP. The CU-UP then creates the corresponding PDCP and SDAP bearers,
establishes F1-U and N3 GTP-U tunnels, and returns a Bearer Context Setup Response. The GTP-U layer stores
QFI-to-DRB mappings for tunnel management, while the SDAP layer maintains QFI-to-DRB tables for both
uplink and downlink packet routing.
During data transfer, uplink packets are demultiplexed using the QFI from the GTP-U header, and downlink
packets are marked with their QFI before being sent to the UPF, ensuring consistent QoS-based traffic
handling end-to-end.
#### CU-CP QoS-flow to DRB mapping
The QoS-flow-to-DRB algorithm runs in the CU-CP (RRC) when flows are added or updated (for example
`nr_rrc_add_bearers`, `nr_rrc_assign_drb_to_qos_flow`, and `nr_rrc_find_suitable_drb_for_qos` in
`openair2/RRC/NR/rrc_gNB_radio_bearers.c`). E1AP does not implement this policy: Bearer Context
Setup/Modification simply carries the resulting PDU sessions, DRB lists, and QoS flows per DRB that RRC
already chose.
The mapping is OAI-specific (3GPP defines that the gNB may map multiple QoS flows to one DRB but does
not mandate these numeric caps).
DRB assignment uses a QoS-aware multiplexing strategy from standardized 5QI where available:
- Dedicated DRBs (one QoS flow per DRB in practice for these classes):
- Delay-critical GBR (5QI 8290): each such flow triggers a new DRB, existing DRBs that already carry
DC-GBR are not reused for other flows.
- Other flows treated as isolated: 5QI 4, 6, 7, 8, 9, 10, 70, 80, and 71-73.
- Multiplexed DRBs (when the new flow is not in the dedicated set, and reuse is allowed on a PDU session):
- Non-delay-critical GBR flows: up to 2 QoS flows per DRB.
- Non-GBR flows: up to 5 QoS flows per DRB.
- Aggregate cap: at most 5 QoS flows per DRB in total (mixed GBR/non-GBR counts).
Per-DRB counting uses resource-type buckets (DC-GBR, GBR, non-GBR) derived from 3GPP TS 23.501
table 5.7.4-1 (standardized 5QI).
Dynamic 5QI without a numeric 5QI: OAI uses a conservative heuristic (packet delay budget, packet
error rate, and QoS priority thresholds) to decide whether the flow must use a new dedicated DRB or
may share an existing DRB. That path is independent of the static 5QI lists above.
#### Sequence Diagram
```mermaid
sequenceDiagram
participant UE
participant DU
participant CUCP as CU-CP
participant CUUP as CU-UP
participant AMF
participant UPF
AMF->>CUCP: NGAP PDU Session Resource Setup Request
Note over CUCP: QoS flows, QoS profile (e.g. 5QI, ARP, GFBR/MFBR)<br/>and N3 UP tunnel info
Note over CUCP: ngap_gNB_handle_pdusession_setup_request<br/>ITTI to RRC
CUCP->>CUCP: rrc_gNB_process_NGAP_PDUSESSION_SETUP_REQ
CUCP->>CUCP: nr_rrc_add_bearers
Note over CUCP: QoS-flow-to-DRB mapping
CUCP->>CUCP: trigger_bearer_setup
Note over CUCP: fill_e1_pdusession_to_setup / fill_e1_drb_to_setup<br/>per DRB (QoS flows, SDAP/PDCP)
Note over CUCP,CUUP: BEARER CONTEXT SETUP
CUCP->>CUUP: E1AP Bearer Context Setup Request
Note over CUUP: e1_bearer_context_setup()
loop Per PDU session
loop Each DRB to setup
CUUP->>CUUP: fill_e1_drb_setup
CUUP->>CUUP: fill_e1_qos_flows_setup
Note over CUUP: Copy QFIs from E1 request into E1 setup response
Note over CUUP: f1_drb_gtpu_create<br/>gtpv1u_create_ngu_tunnel<br/>no QFI in F1-U GTP, one tunnel per DRB
end
CUUP->>CUUP: e1_add_bearers
Note over CUUP: nr_sdap_addmod_entity, nr_pdcp_add_drb<br/>SDAP qfi2drb mapping
CUUP->>CUUP: n3_gtpu_create
Note over CUUP: gtpv1u_create_ngu_tunnel<br/>newGtpuCreateTunnel<br/>one N3 tunnel per PDU session
end
CUUP->>CUCP: E1AP Bearer Context Setup Response
Note over CUCP,CUUP: F1-U TEID/addr per DRB
Note over CUUP: N3 TEID (CU-UP) per PDU session
CUCP->>CUCP: rrc_gNB_process_e1_bearer_context_setup_resp
alt First F1 UE context (!f1_ue_context_active)
CUCP->>DU: F1 UE Context Setup Request
Note over CUCP: rrc_f1_ue_context_setup_from_e1_response
else F1 context already active
CUCP->>DU: F1 UE Context Modification Request
Note over CUCP: rrc_send_f1_ue_context_modification_request
end
Note over DU: handle_ue_context_drbs_setup<br/>QoS from F1 flows -> MAC logical channel config
DU->>CUCP: F1 UE Context Setup or Modification Response
CUCP->>CUCP: rrc_gNB_generate_dedicatedRRCReconfiguration
Note over CUCP: DRB-ToAddModList, SDAP-Config (QFI mapping)
CUCP->>DU: rrc_deliver_dl_rrc_message / DL RRC Message Transfer
DU->>UE: RRCReconfiguration (DCCH)
UE->>DU: RRCReconfigurationComplete
DU->>CUCP: UL RRC Message Transfer (F1AP)
Note over CUCP: rrc_gNB_decode_dcch, handle_rrcReconfigurationComplete
CUCP->>AMF: NGAP PDU Session Resource Setup Response
Note over UPF,UE: ===== Data Plane Active ===== (N3/F1-U)
Note over UPF,UE: DOWNLINK (N3 -> CU-UP)
UPF->>CUUP: GTP-U with PDU Session Container (QFI)
Note over CUUP: Gtpv1uHandleGpdu<br/>parse ext hdr -> QFI
Note over CUUP: sdap_data_req<br/>SDAP qfi2drb_table -> DRB
Note over CUUP: nr_pdcp_data_req_drb
CUUP->>DU: F1-U GTP-U (no PDU Session Container / QFI marking)
DU->>UE: DRB
Note over UE,UPF: UPLINK (CU-UP -> N3 UPF)
UE->>DU: DRB
DU->>CUUP: F1-U GTP-U (no QFI marking)
Note over CUUP: PDCP -> SDAP UL RX
Note over CUUP: QFI from SDAP UL header if configured<br/>else gtpv1uSendDirect (no QFI in GTP ext hdr)
Note over CUUP: nr_sdap / gtpv1uSendDirectWithQFI<br/>bearer_id = PDU session id (N3 tunnel key)
CUUP->>UPF: GTP-U PDU Session Container (UL PDU Session Info, QFI)
```
### Inter-DU Handover (F1)
The basic handover (HO) structure is as follows. In order to support various

View File

@@ -241,6 +241,12 @@ information on how the images are built.
~5G-NR ~nrUE
- 5G-NR SA test setup: OAI VNF + PNF/NVIDIA CUBB on gracehopper1-oai + WNC RU, OAIUE on jetson1-oai + B210, OAI CN5G
- OpenShift cluster for CN deployment and container images for gNB and UE deployment
- [RAN-SA-FHI72-MPLANE-CN5G](https://jenkins-oai.eurecom.fr/view/RAN/job/RAN-SA-FHI72-MPLANE-CN5G/)
~5G-NR
- cacofonix + FHI72 + Benetel550 (gNB), AmarisoftUE, OAI CN5G
- OpenShift cluster for CN deployment
- FHI 7.2 testing with 40 MHz, 4x4 MIMO configuration and 100 MHz, 2x2 MIMO configuration
- FHI 7.2 Configuration and Performance Management via NETCONF session of an O-RU
### RAN-CI-NSA-Trigger

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