Compare commits

...

159 Commits

Author SHA1 Message Date
Jaroslava Fiedlerova
70508ebaf5 Merge remote-tracking branch 'origin/integration_2026_w28' into develop
Integration: 2026.w28

- #235 RRC fixes
- #247 Enforce N_PRB=2^x3^y5^z in SC-FDMA uplink scheduler
- #274 Fix PSS correlation peak value and average value on the OAI UE logs
- #261 ci: Speedup fhi72 dockerfiles
- #273 Simplifications for xran builds
- #270 xran: fixes for FDD operation
- #281 Handle invalid input in nr_get_E
- #149 fix(zmq): speedup zmq radio
- #260 O-RU first fix pass
- #202 doc: add a Git guide (commit signing, branch management, rerere)
- #249 SDAP header handling fixes
- #199 UE: implement TRS processing in PHY
- #197 Bump SIMDE commit to the most up-to-date (1c68d9a)

Closes: #118
Closes: #127
Closes: #128
Closes: #148
Closes: #262
2026-07-10 19:05:22 +02:00
Jaroslava Fiedlerova
4a84b92d88 Merge remote-tracking branch 'luispereira106/bump-simde-commit' into integration_2026_w28
Bump SIMDE commit to the most up-to-date (1c68d9a) (#197)

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-10 14:21:15 +02:00
Jaroslava Fiedlerova
9c7b3d02e0 Merge remote-tracking branch 'sakthivelvelumani/ue-trs-support' into integration_2026_w28
UE: implement TRS processing in PHY (#199)

This PR implements PHY layer processing for CSI-RS tracking signal that
estimates the CFO and sends a frequency correction command to the radio
when CFO goes above a threshold of 500Hz.

A CI test is added with a GNU radio script between OCUDU gNB and the UE
that simulates a continuous frequency offset. The test passes if the UE
sends a frequency correction command.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Reviewed-by: Francesco Mani <email@francescomani.it>
2026-07-10 13:48:18 +02:00
Luis Pereira
3acb40cb85 Bump SIMDE commit to the most up-to-date (1c68d9a)
Signed-off-by: Luis Pereira <lpereira@allbesmart.pt>
2026-07-10 09:37:20 +01:00
Robert Schmidt
aff84037a7 Merge remote-tracking branch 'GuidoCasati/sdap-header-fix' into integration_2026_w28
SDAP header handling fixes (#249)

OAI SDAP treated SDAP header presence as a single PDU-session flag
(enable_sdap), so a valid asymmetric RRC config (UL header present, DL
header absent) caused the UE to parse downlink payload as an SDAP QFI
and drop traffic (e.g. IPv4 0x45 -> bogus QFI 5).

SDAP now follows RRC SDAP-Config per DRB and per direction from (TS
38.331 / TS 37.324). Header presence, QFI-to-DRB mapping, and
default-DRB fallback all derive from what RRC configured for each DRB.

In the new implementation, RRC (SDAP-Config IE) fills sdap_config_t per
DRB (role, defaultDRB, mapped QFIs), SDAP entity stores it in
qfi2drb_table + default_drb row to be used in the TX/RX data path.

Also, simplify mapping logic, improves E1 QoS-flow remap, and completes
UE reflective QoS mapping with the correct entity_role on new QFI rows.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 19:45:58 +02:00
Sakthivel Velumani
0f45f471b2 Add timing stats for TRS processing
Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-07-09 16:21:59 +00:00
Sakthivel Velumani
016ab2cfb3 fix: tracking CSI-RS measurement bitmap
measurement bitmap for CSI-RS is 0 means CSI-RS for tracking. So don't
return and proceed with processing the CSI-RS.

Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-07-09 16:21:59 +00:00
Sakthivel Velumani
7729bab5af feature: Estimate CFO from CSI-RS tracking signal
TRS resources if configured by gnb, is present in two consequtive slots
and two symbols per slot. LS estimates from the first symbol is saved
and in second symbol frequency offset and time offset is estimated. The
estimated FO is saved in UE global structure which is used in the last
TRS slot. If the FO is above a threshold, the radio's center frequency
is adjusted.

Time offset is estimated but not used because time is already tracked
using PBCH DMRS.

Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-07-09 16:21:51 +00:00
Robert Schmidt
fef2154525 Merge remote-tracking branch 'Thecave3/rerere-doc' into integration_2026_w28
doc: add a Git guide (commit signing, branch management, rerere) (#202)

Adds doc/git-guide.md, a single entry point for the practical Git
knowledge needed to contribute to OAI: commit signing setup (DCO +
verified commits), branch management (including fixup commits and
--autosquash), submodules, recovering from mistakes, and reusing
conflict resolutions with git rerere.

This started as a rerere-only guide. Review discussion (@luispereira106,
@rorsc, @sgarg00) converged on a broader scope: the Git how-tos are
scattered across the documentation (signing in CONTRIBUTING.md, branch
management in code-style-contrib.md), and configuring commit signing in
particular was reported as hard to get right. This PR consolidates the
how-tos in one guide while the policy documents stay authoritative for
the rules.

Single commit (review rounds squashed per the linear-history policy):

- New doc/git-guide.md with sections on commit signing (moved from
  CONTRIBUTING.md, led by a copy-pasteable SSH recipe), branch
  management (moved from code-style-contrib.md; git switch,
  fixup/--autosquash, --fixup=amend:), submodules (unintended
  pointer-update pitfall), recovering from mistakes
  (restore/reset/reflog), and git rerere.
- CONTRIBUTING.md keeps the normative DCO/Verified requirements and
  links to the guide; code-style-contrib.md keeps the workflow policy
  and links to the guide; doc/README.md lists the guide under Developer
  tools.
- Cross-references (not moved, since they are coupled to their own
  docs): clang-format.md pre-commit hook, GET_SOURCES.md branch/tag
  model, doc_best_practices.md docs: commit prefix.
- Small typo fix in the T tracer addconsoletrace.md documentation.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Francesco Mani <email@francescomani.it>
Reviewed-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-07-09 17:27:56 +02:00
Robert Schmidt
546fc0628b Merge remote-tracking branch 'bpodrygajlo/oru-fixes' into integration_2026_w28
O-RU first fix pass (#260)

Added several fixes requested in the original PR

- simplify code
- add example config files & test procedure

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-09 17:26:59 +02:00
Robert Schmidt
ddea885bc7 Merge remote-tracking branch 'bpodrygajlo/zmq-radio-speedup' into integration_2026_w28
fix(zmq): speedup zmq radio (#149)

- Utilize ZMQ "zero-copy" inteface on TX
- Skip one memcpy in RX
- Use AVX-512 optimized cf_t <-> c16_t conversion loops in tx and rx
- Parallelize TX and RX polling threads for faster execution

Closes: #118
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-09 17:26:00 +02:00
Robert Schmidt
be4d64a576 Merge remote-tracking branch 'origin/get_E_crash' into integration_2026_w28
Handle invalid input in nr_get_E (#281)

This PR prevents the function nr_get_E to crash with an assertion for
invalid input by handling the scenario with an error code

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-09 17:12:34 +02:00
Robert Schmidt
afcd77108d Merge remote-tracking branch 'karimboutiba/xran_fdd' into integration_2026_w28
xran: fixes for FDD operation (#270)

Two fixes in the xran fronthaul found while bringing up an FDD cell
(Band n1, 10 MHz).  Verified on a Band n1 FDD 10 MHz cell using Keysight
RUsim/UEsim, End-to-end UE attach and traffic work.

- Derive TTI from the configured numerology instead of a hardcoded 20
  slots per frame: xran_fh_tx_send_slot() assumed 30 kHz SCS. Compute
  slots_per_frame as 10 << mu from the fronthaul config (mu_number[0]
  for K release, frame_conf.nNumerology for F release), so the TTI is
  correct for any numerology. With the original code, PRACH was not
  working properly.
- Fix DL transmission in FDD mode: the TDD DL/guard slot check skipped
  every DL slot in FDD, where no TDD pattern exists and all slots carry
  DL. The check is now only applied when the frame duplex type is not
  FDD. Without this, no DL traffic was sent.

Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-09 17:10:29 +02:00
Robert Schmidt
dde7ecf994 Merge remote-tracking branch 'bpodrygajlo/xran-arm-build-fix' into integration_2026_w28
Simplifications for xran builds (#273)

- make xran_DOWNLOAD=ON default - skips an extra argument in cmake
  command
- Automatically detect aarch64 and select correct target for external
  xran build

Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-07-09 17:09:27 +02:00
Andrea Lacava
4bff655952 doc: add a Git guide and fix addconsoletrace typo
Add doc/git-guide.md, a single entry point for the practical Git
knowledge needed to contribute to OAI, consolidating the how-tos
that were scattered across the documentation:

- Setting up commit signing: moved from CONTRIBUTING.md, which keeps
  the normative DCO/Verified requirements and links to the guide; led
  by a compact SSH recipe, with the allowed_signers troubleshooting
  and a note on repository-level configuration for shared servers.
- Managing your own branch: moved from code-style-contrib.md, which
  keeps the workflow policy and links to the guide; mentions git
  switch and covers fixup commits with git rebase --autosquash,
  including the --fixup=amend:<commit> variant.
- Working with submodules: the update command and the unintended
  submodule-pointer-update pitfall seen in past PRs.
- Recovering from mistakes: git restore --staged, git restore,
  git reset --soft/--hard, and git reflog.
- Reusing conflict resolutions with git rerere: enabling, typical
  flow, inspecting, seeding from existing history, sharing the cache,
  and caveats, clarifying that contribution branches must keep a
  linear history while forks may carry merge commits.

The guide is referenced from doc/README.md (Developer tools),
code-style-contrib.md, and CONTRIBUTING.md. Guidance coupled to its
own document is cross-linked instead of moved: clang-format.md
(pre-commit hook), GET_SOURCES.md (branch and tag model), and
doc_best_practices.md (docs: commit prefix).

Also fix a typo in the T tracer addconsoletrace documentation.

Signed-off-by: Andrea Lacava <thecave003@gmail.com>
2026-07-09 10:43:17 -04:00
Guido Casati
14b5bcb600 doc: clarify enable_sdap as RRC SDAP header policy
Log, param help, and rrc-usage.md described enable_sdap as toggling the
SDAP layer, while it only sets sdap-HeaderUL/DL in RRC SDAP-Config.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 15:04:11 +02:00
Karim Boutiba
6e1bfb2c19 doc: document nr_scs_for_raster and sl_ahead in FHI 7.2 tutorial
Signed-off-by: Karim Boutiba <karim.boutiba@openairinterface.org>
2026-07-09 13:01:57 +02:00
Karim Boutiba
3921017988 xran: fix FDD operations and avoid skipping DL/UL slots.
Previously, is_tdd_ul_symbol() and is_tdd_dl_symbol() returned true
unconditionally in FDD. This is correct for UL/DL presence (in FDD,
every symbol carries both UL and DL), but it breaks logic that relies
on the TDD property that a UL symbol is not a DL symbol (and vice
versa), e.g. the computation of the last DL symbol in mixed slots. In
FDD, this assumption does not hold, leading to incorrect behavior.

Remove the FDD shortcut from the TDD symbol helpers so they strictly
answer the TDD question, and make the FDD case explicit at each
function call (nFrameDuplexType != XRAN_FDD && ...). This harmonizes
the TDD and FDD paths: in FDD, no slot/symbol is skipped on RX or TX,
and guard-slot handling only applies to TDD.

Signed-off-by: Karim Boutiba <karim.boutiba@openairinterface.org>
2026-07-09 12:46:15 +02:00
Karim Boutiba
e6dd656e52 xran: derive TTI from configured numerology instead of hardcoded 20 slots/frame
The TTI computation in xran_fh_tx_send_slot() assumed 20 slots per
frame (30 kHz SCS only). Compute slots_per_frame as 10 << mu from the
fronthaul configuration instead, so the TTI is correct for any numerology.

Signed-off-by: Karim Boutiba <karim.boutiba@openairinterface.org>
2026-07-09 12:45:42 +02:00
Guido Casati
223fd132e0 sdap: rename pdcp_entity to drb_id
In nr_sdap_rx_entity, the RX callback receives the DRB identity from PDCP
(drb-Identity). Rename pdcp_entity to drb_id to be aligned with TS 37.324 terminology.

Changes:
- Rename the RX callback argument through the full SDAP RX surface:
sdap_data_ind(), the rx_entity function pointer table, and nr_sdap_rx_entity().

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Guido Casati
7d029ed5b1 fix (sdap): copy entity_role on reflective QoS mapping
After new per-DRB SDAP roles changes, UE downlink reflective mapping
(TS 37.324 §5.3.2) still stored only drb_id on the new QFI row.
entity_role stayed NO_SDAP_HEADER, so later uplink TX and §5.3.1
end-marker logic read the wrong header config from that row.

When storing the reflective UL rule, copy the receiving DRB entity_role
(from nr_sdap_drb_lookup) together with drb_id, matching qfi2drb_map_add.

Changes:
- nr_sdap_rx_entity(): set qfi2drb_table[qfi].entity_role on RDI store

Refs:
- TS 37.324 §5.3.1, §5.3.2
- TS 38.331 SDAP-Config (sdap-HeaderUL / sdap-HeaderDL)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Guido Casati
991ede5c59 rrc: simplify E1 SDAP header present flags in fill_e1_drb_to_setup
In fill_e1_drb_to_setup(), derive E1 SDAP-Config header-present booleans
with logical negation instead of redundant ternary expressions.

Changes:
- NGAP: sDAP_Header_UL/DL assignment in fill_e1_drb_to_setup()

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Guido Casati
f34ecdd021 sdap: validate gNB UL RX QFI via qfi2drb_map and receiving DRB
Parent commits route QFI-to-DRB lookup through qfi2drb_map (including
default-DRB fallback per TS 37.324 clause 5.2.1), but gNB UL RX still
checked the raw mapping table and accepted any mapped QFI on whatever
DRB received the PDU.

On gNB UL SDAP PDUs with a header, nr_sdap_rx_entity resolves the QFI
through entity->qfi2drb_map, drops when no rule and no default DRB apply,
and drops when the resolved DRB does not match the receiving DRB before
GTP-U forwarding.

Changes:
- parse UL header with nr_sdap_ul_hdr_t
- call qfi2drb_map for QFI validation
- verify receiving-DRB match
- simplify gNB UL block

Refs:
- TS 37.324 clause 5.2.1 (Uplink), clause 6.2.2.3 (UL data PDU)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Guido Casati
96dc20010e sdap: pass sdap_config_t through E1 QoS-flow remap
E1 DRB-To-Modify already carries the new QFI list. The old
nr_sdap_entity_update_qos_flows() duplicated that as a bare qfi[] plus other params
while map_update expects a full sdap_config_t (add/release lists and per-DRB
role). Route the E1 path through one struct instead.

Changes:
- nr_sdap_entity_update_qos_flows(): take sdap_config_t *, caller fills the struct,
  function sets role and defaultDRB from qfi2drb_table, builds mappedQFIs2Release
  for QFIs dropped from the DRB, then calls qfi2drb_map_update()
- cucp_cuup_handler.c: build stack sdap_config_t from E1 qosFlows[] and call
  the new API

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Guido Casati
1edb0a09bb fix (sdap): select RX/TX SDAP headers on per-DRB RRC role
A DRB may have an uplink SDAP header but no downlink header (valid TS 38.331).
The old entity->enable_sdap flag was true if either direction had a header, so UE
downlink wrongly read byte 0 as QFI. IPv4 payloads start with 0x45 -> bogus QFI 5
and dropped traffic before the TUN (PR !158).

Header presence must follow TS 37.324 §5.2.1/§5.2.2 per DRB and direction, not a
single PDU-session-wide enable-sdap.

Also, store the default DRB as a `qfi2drb_t` mapping in the SDAP entity and use
it directly for fallback paths.

Changes:
- Add nr_sdap_drb_lookup() to return first qfi2drb_table row for a DRB
- nr_sdap_rx_entity(): parse QFI only when the receiving direction has a header,
  drop if the DRB has no table row
- nr_sdap_tx_entity(): UL/DL TX header bits from qfi2drb_map() (drb_id + entity_role),
  including default-DRB fallback
- nr_sdap_qfi2drb(): const qfi2drb_t *, remove SDAP_MAP_RULE_EMPTY
- nr_sdap_map_ctrl_pdu(): handle NULL from qfi2drb_map
- nr_sdap_qfi2drb_map_update(): sync entity_role on
  all QFIs for the DRB. At most one DRB when both headers absent.
- nr_sdap_entity_update_qos_flows(): keep per-DRB role from nr_sdap_drb_lookup(),
  skip update when DRB has no row
- Cleanup entity->enable_sdap
- nr_sdap.c: remove TUN-thread QFI drop, tx_entity owns unmappable-QFI handling
- replace default_drb with qfi2drb_t default_drb in SDAP entity and init
  default DRB id+role at SDAP entity creation

Refs:
- PR #158
- TS 37.324 §5.2.1, §5.2.2, §6.2.2.1
- TS 38.331 SDAP-Config (sdap-HeaderUL / sdap-HeaderDL)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-09 12:21:03 +02:00
Bartosz Podrygajlo
ff9b39145e doc(oru): Example config of OAI O-RU and OAI gNB with 7.2 interface
To run the test with OAI UE and vrtsim, use the following commands, assuming
the executables are in the build directory.

OAI O-RU:
  ./nr-oru -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/ru.band77.mu1.106rb.1x1.conf --device.name vrtsim --vrtsim.role server

OAI 7.2 gNB:
  ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.band77.mu1.106rb.fhi.1x1.conf

OAI NR UE:
  ./nr-uesoftmodem -C 4049760000 -r 106 --numerology 1 --ssb 516 --device.name vrtsim

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 11:48:11 +02:00
Bartosz Podrygajlo
ebdedfffcf feat(oru): load radio library according to configuration
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 11:34:00 +02:00
Bartosz Podrygajlo
486f4423d9 refactor(oru): improve readability of TDD config and prefix selection in fft_and_cp_insertion
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 11:33:52 +02:00
Bartosz Podrygajlo
bfeba0de48 refactor(oru): remove redundant extern/function declarations from main_nr_ru.c
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 11:33:45 +02:00
Bartosz Podrygajlo
80a8b81e96 perf(zmq): parallelize TX and RX polling threads
Replace the single polling thread that handled all channels
with separate, dedicated polling threads per TX and RX channel.
This avoids calling `zmq_poll` across all sockets simultaneously,
reduces lock contention, and enables concurrent socket operations
on multiple antennas.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:40:04 +02:00
Bartosz Podrygajlo
8bed33d44e test(zmq): add SIMD sample validation
Add `TxRxSamplesSIMD` to validate the correctness of the new ZMQ
device transmit and receive paths with int16 complex samples.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:39:49 +02:00
Bartosz Podrygajlo
1ec719bd44 perf(zmq): optimize TX path using ZMQ zero-copy message queue
Optimize the TX path by converting int16 complex samples to float
complex format directly into ZMQ message buffers using SIMD. Queue
these ZMQ messages (`zmq_msg_t`) directly, avoiding temporary internal
copying and enabling ZMQ's zero-copy interface during actual transmit
in the polling thread.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:37:57 +02:00
Bartosz Podrygajlo
9e94084e9d perf(zmq): optimize RX path using SIMD conversion and direct storage
Optimize the RX path by converting float complex samples received on ZMQ
to int16 complex format using SIMD right inside the polling thread. This
allows storing int16 complex (c16_t) samples directly inside the overflow_buffer,
skipping a temporary float buffer allocation and a memcpy in the receive function.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:37:47 +02:00
Bartosz Podrygajlo
5fad2465f0 refactor(zmq): templatize ring_buffer and overflow_buffer
Support float and int16 complex types

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:14:27 +02:00
Bartosz Podrygajlo
75545fa2c1 feat(zmq): add SIMD sample conversion functions
Add AVX-512 and AVX2 optimized conversion loops between float complex (cf_t)
and int16 complex (c16_t) sample formats in radio/zmq/zmq_simd.h.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:12:47 +02:00
Bartosz Podrygajlo
dc35ce95b9 test(zmq): addthroughput benchmark tests
Add a performance benchmark `BenchmarkThroughput` to measure the sample rate capacity

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-09 08:12:47 +02:00
Francesco Mani
48b329d115 prevent nr_get_E function to crash with an assertion for invalid input
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-07-09 07:57:51 +02:00
Bartosz Podrygajlo
6bdf1dde3d fix: Update xran version to 11.1.3
This update allows automatic discovery of target platform when building xran.

Supproted platforms: armv8 and x86_64 with avx512

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-08 15:34:05 +02:00
Bartosz Podrygajlo
3a3fd87633 fix: Make xran_DOWNLOAD=ON default
Make xran_DOWNLOAD ON by default - this simplifies work in the most common
usecase and still allows to overwrite xran directory with xran_LOCATION.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-08 13:10:40 +02:00
Robert Schmidt
c8e36d3968 Merge remote-tracking branch 'bpodrygajlo/ci-docker-speedup' into integration_2026_w28
ci: Speedup fhi72 dockerfiles (#261)

Move "COPY . ." down in 7.2 dockerfiles. This layer doesn't cache well
due to changes in docker context, moving it down allows docker builder
to cache other layers that are not affected by the context.

Reviewed-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-07-08 09:25:30 +02:00
Robert Schmidt
bf89f7997c Merge remote-tracking branch 'luispereira106/fix-pss-corr-metrics' into integration_2026_w28
Fix PSS correlation peak value and average value on the OAI UE logs (#274)

These metrics are very helpful to properly adjust the USRP gains on the OAI UE. There was a regression in the code, this commit just gets it showing properly again.

Current develop:

    [NR_PHY] Starting sync detection
    [PHY]    [UE thread Synch] Running Initial Synch
    [NR_PHY] Starting cell search with center freq: 3619200000, bandwidth: 106. Scanning for 1 number of GSCN.
    [NR_PHY] Scanning GSCN: 0, with SSB offset: 516, SSB Freq: 0.000000
    [PHY]    Initial sync: pbch decoded sucessfully, ssb index 0
    [PHY]    pbch rx ok. rsrp:51 dB/RE, adjust_rxgain:-1 dB
    [NR_PHY] Cell Detected with GSCN: 0, SSB SC offset: 516, SSB Ref: 0.000000, PSS Corr peak: 0 dB, PSS Corr Average: 0

This PR:

    [PHY]    SSB position provided
    [NR_PHY] Starting sync detection
    [PHY]    [UE thread Synch] Running Initial Synch
    [NR_PHY] Starting cell search with center freq: 3619200000, bandwidth: 106. Scanning for 1 number of GSCN.
    [NR_PHY] Scanning GSCN: 0, with SSB offset: 516, SSB Freq: 0.000000
    [PHY]    Initial sync: pbch decoded sucessfully, ssb index 0
    [PHY]    pbch rx ok. rsrp:51 dB/RE, adjust_rxgain:-1 dB
    [NR_PHY] Cell Detected with GSCN: 0, SSB SC offset: 516, SSB Ref: 0.000000, PSS Corr peak: 99 dB, PSS Corr Average: 61

Reviewed-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-07-08 09:24:43 +02:00
Robert Schmidt
d86bad7915 Merge remote-tracking branch 'PC-OC/scfdma-uplink-scheduler-rbsize' into integration_2026_w28
Enforce N_PRB=2^x3^y5^z in SC-FDMA uplink scheduler (#247)

With SC-FDMA, the scheduler in uplink needs to schedule N_PRB=2^x3^y5^z.
Not enforcing it may lead to unwanted UE behavior like retransmissions
and re-establishments.

TS38.211 - 6.3.1.4

Reviewed-by: Francesco Mani <email@francescomani.it>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-06 18:28:55 +02:00
Robert Schmidt
fcf078b18c Merge remote-tracking branch 'GuidoCasati/rrc-fixes' into integration_2026_w28
RRC fixes (#225)

- Hardens NR RRC re-establishment handling on the gNB and UE:
  re-establishment requests with invalid PCI/C-RNTI or incomplete UE
  context are rejected or fall back to RRCSetup per TS 38.33.
- Fixes an invalid post-release connected state after PDU session
  teardown
- Stops gNB process aborts when a UE sends spare RRC establishment-cause
  values.
- Fix RRCSetup fallback: the UE MAC layer is reset before bearer
  teardown on RRCSetup fallback to avoid scheduler use of released RLC
  entities.

Closes: #127
Closes: #128
Closes: #148
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Francesco Mani <email@francescomani.it>
2026-07-06 18:28:16 +02:00
Luis Pereira
fcea45b215 Fix PSS correlation peak value and average value on the OAI UE logs
These metrics are very helpful to properly adjust the USRP gains on the OAI UE.
There was a regression in the code, this commit just gets it showing properly again

Signed-off-by: Luis Pereira <lpereira@allbesmart.pt>
2026-07-06 17:17:05 +01:00
Calvin Peyron
fe56b68514 Remove trailing spaces
Signed-off-by: Calvin Peyron <calvin.peyron@open-cells.com>
2026-07-06 11:54:57 +02:00
Calvin Peyron
ad304b62d6 With SC-FDMA, scheduler in uplink needs to schedule N_PRB=2^x3^y5^z
Signed-off-by: Calvin Peyron <calvin.peyron@open-cells.com>
2026-07-06 11:54:50 +02:00
Robert Schmidt
0dab3bb6a1 Merge remote-tracking branch 'origin/integration_2026_w27' into develop
Integration: 2026.w27

- #227 Cleanup: NGAP reconnection debug log
- #233 fix: proper SA/NSA mode detection
- #239 Fix function init_context_sss_nr()
- #256 ci: Move test pcaps to github
- #244 fix(zmq): Fix clang compiler warnings in zmq radio
- #220 fix(NR_UE): gate half-frame PBCH mirror on caller-provided ssb_period
- #257 [FHI72] Update the xran K release tag due to the PR https://github.com/openairinterface/o-du-phy/pull/3
- #192 DLSIM test for type0 PDSCH frequency allocation
- #238 minorfix(imscope): moving alignas from struct type to atomic variable
- #255 ci: Speedup unit test dockerfile
- #217 NAS: refactor Identity Request handler and enforce spec-compliant security checks
- #243 Clean up openair0_device, remove eth_params usage in L2/L3
- #204 Implementation of the E3 Agent for dApps and E3AP (no service models)
- #258 NR UE: add assert for reserved bits per symbol not multiple of Qm
- #253 CI: Collect amariue logs, update eNB configs
- #203 CI: Add label check and signature verification in GitHub Actions

Closes: #219
Closes: #82
2026-07-04 17:24:07 +02:00
Robert Schmidt
ecb98e575f Merge remote-tracking branch 'origin/ci-duranta-fixes' into integration_2026_w27
CI: Add label check and signature verification in GitHub Actions (#203)

- Update contributing guidelines for signing of commits
- Check for unsigned commits in the github actions workflow
- Skip CI when no mandatory CI label present on the PR
- Always remove retrigger-ci label when present - Like post-always or
  post-cleanup

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Sagar Arora <sagar.arora@openairinterface.org>
Reviewed-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-07-03 15:56:17 +02:00
Shubhika Garg
f63c444bb7 ci: ensure maintainer approval job is evaluated correctly on push events
- The require-maintainer-approval job was being skipped on push event
    when upstream jobs (verify-signed-commits and check-labels) were skipped,
    even though detect-changes job successfully marked protected files as changed.
  - GitHub Actions may skip dependent jobs due to upstream skipped states before
    evaluating custom conditions.
  - Adding always() ensures the job condition is evaluated regardless of
    the status of upstream jobs, while still enforcing the rule that the job
    only runs when protected_files_changed == true.

Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-07-03 13:19:58 +02:00
Robert Schmidt
1cbfef20f4 Merge remote-tracking branch 'origin/ci-various-adjustments' into integration_2026_w27
CI: Collect amariue logs, update eNB configs (#253)

- collect amarisoft UE logs
- improve stability in LTE pipelines with prach_dtx_threshold

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-03 08:42:50 +02:00
Jaroslava Fiedlerova
2828e63dd8 CI: Remove OAI-CN5G-COTS-UE-Test from parent pipeline
The executor machine for this test is currently unavailable. The test
will be re-enabled once the replacement machine is configured.

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-07-03 08:32:51 +02:00
Jaroslava Fiedlerova
384186b0a6 CI: Configuration file adjustment for LTE-FDD-LTEBOX-Container
Try to reduce number of false RA attempts on eNB

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-07-03 08:32:51 +02:00
Jaroslava Fiedlerova
aa6f141498 CI: Configuration file adjustment for LTE-TDD-LTEBOX-Container
Try to reduce number of false RA attempts on eNB

Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-07-03 08:32:51 +02:00
Jaroslava Fiedlerova
32847ee143 CI: Enable UE log collection in RAN-SA-FHI72-MPLANE-CN5G
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-07-03 08:32:51 +02:00
Robert Schmidt
b1c7bdf99b Merge remote-tracking branch 'alexjiao2021/assert_uci_on_pusch' into integration_2026_w27
NR UE: add assert for reserved bits per symbol not multiple of Qm (#258)

map_overlapped_ack() divides num_reserved_bits_on_sym by Qm to get the
number of reserved REs. Add assertion to catch cases where the reserved
bit count is not a multiple of Qm, which would indicate a bug in the
upstream UCI-on-PUSCH mapping.

This is a follow-up of comment after PR #221

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 18:40:17 +02:00
Robert Schmidt
69a017f57b Merge remote-tracking branch 'Thecave3/e3-agent-framework' into integration_2026_w27
Implementation of the E3 Agent for dApps and E3AP (no service models) (#204)

These changes introduce an E3 Agent for dApps in the OAI codebase. dApps
are real-time microservices designed to operate within the O-RAN
ecosystem, enabling sub-10 ms Artificial Intelligence (AI) routines in
the Radio Access Network (RAN).

The E3 Agent enables spectrum sensing, frequency-domain signal logging,
and dynamic PRB (Physical Resource Block) policies through IPC/ZMQ
communication with dApps or xApps.

The dApps are designed for co-location with the CU/DU, where it can
interact directly with user-plane data to enable different network
management scenarios such as optimizing network slicing, scheduling, and
resource management.  A python library for the creation of the dApps is
available here [1].

Additional information about dApps:

- Paper on the dApp architecture [2]
- dApp framework presentation [3]
- Tutorial on dApp deployment on OAI [4]

As agreed in the original MR [5], this PR includes only the agent lifecycle,
configuration parsing, build plumbing, and the nr-softmodem init/destroy
hook.

It registers zero service models, so it builds and runs as an idle
agent. Concrete service models (e.g. spectrum sensing) and the PHY/MAC
hooks that drive them follow in later PRs.

Opt-in and dependencies

- Gated behind the E3_AGENT CMake option (OFF by default) / the
  --build-e3 build flag.  The default build is unaffected — e3ap is only
  compiled and linked into nr-softmodem when E3_AGENT=ON.

- When enabled, requires the external libe3 library [6] (vendor-neutral
  E3AP C++ lib), discovered via pkg_check_modules(CLIBE3 REQUIRED
  libe3). It is not vendored.

  Install libe3 (Debian/Ubuntu):

      git clone https://github.com/wineslab/libe3 && cd libe3
      ./build_libe3 -I      # installs deps: build-essential cmake pkg-config libzmq3-dev asn1c nlohmann-json3-dev libsctp-dev ...
      ./build_libe3 --install      # release build + installation

Build & test

Tested in RFsim: the gNB initializes the E3 agent, opens its setup
socket (/tmp/dapps/setup), registers zero service models, and runs
without crashing.  Configuration

The agent reads an optional E3Configuration section from the gNB config
file (parsed only when E3_AGENT=ON). If absent, it falls back to
built-in defaults (posix/ipc).

    E3Configuration : {
        link = "zmq";        # posix | zmq
        transport = "ipc";   # tcp | sctp | ipc
    };

The same parameters can be overridden on the command line (no
config-file edit required):

    sudo ./nr-softmodem -O <gnb.conf> ... --E3Configuration.link zmq --E3Configuration.transport ipc

Validated link/transport combinations: (zmq,ipc) (zmq,tcp) (posix,tcp)
(posix,sctp) (posix,ipc).

Notes

- I have added a new log component E3AP specific for the ops performed
  by the E3 Agent.
- This PR ships no gNB config file; the E3Configuration block / CLI
  override above is the documented way to enable the agent. Example
  configs follow in the next PRs.

[1] https://pypi.org/project/dapps/
[2] https://arxiv.org/pdf/2501.16502
[3] https://openrangym.com/o-ran-frameworks/dapps
[4] https://openrangym.com/tutorials/dapps-oai
[5] https://gitlab.eurecom.fr/oai/openairinterface5g/-/merge_requests/3361
[6] https://github.com/wineslab/libe3

Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org>
Reviewed-by: Sagar Arora <sagar.arora@openairinterface.org>
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 18:35:23 +02:00
Andrea Lacava
602d729d43 e3ap: add README with dApp overview, prerequisites, and startup guide
Signed-off-by: Andrea Lacava <thecave003@gmail.com>
2026-07-02 09:55:05 -04:00
Andrea Lacava
29c20ac232 e3ap: add E3 agent lifecycle and nr-softmodem init hook
Add the E3 agent lifecycle on top of libe3: configuration mapping,
agent creation/init/start, dApp report and status-change handlers,
and xApp control and subscription-query helpers. Wire e3_init() and
e3_destroy() into nr-softmodem under the E3_AGENT guard.

The agent registers no concrete service models: service models (such
as spectrum sensing) live in their own modules and register
themselves, keeping the framework decoupled from any specific use
case.

Signed-off-by: Andrea Lacava <thecave003@gmail.com>
2026-07-02 09:53:52 -04:00
Andrea Lacava
0a885dcf5a e3ap: add E3 agent configuration and CMake target
Add the E3AP CMake target, which links the external libe3 library
discovered via pkg-config, and the E3Configuration parser that reads
the link and transport selection from the gNB configuration file and
validates the supported link/transport combinations.

libe3 is kept external and is only required when E3_AGENT is enabled.

Signed-off-by: Andrea Lacava <thecave003@gmail.com>
2026-07-02 09:53:16 -04:00
Andrea Lacava
447ff67aba build: add opt-in E3_AGENT build option for the O-RAN E3 agent
Introduce the E3_AGENT CMake option (default OFF) and the matching
--build-e3 flag in build_oai so the E3 agent for O-RAN dApps can be
compiled opt-in. Gate the E3AP subdirectory and the nr-softmodem
linkage behind the option, and register an E3AP log component
together with its legacy T-tracer message IDs.

The E3 interface (nGRG) lets the gNB exchange real-time reports and
controls with external dApps. Keeping it behind a build flag leaves
the default CI build unchanged and avoids pulling in the external
libe3 dependency unless the agent is explicitly requested.

Signed-off-by: Andrea Lacava <thecave003@gmail.com>
2026-07-02 09:51:58 -04:00
Robert Schmidt
465b57d8c5 Merge remote-tracking branch 'rorsc/more-cleanup-split8-api' into integration_2026_w27
Clean up openair0_device, remove eth_params usage in L2/L3 (#243)

This PR has three goals

- Remove the usage of eth_params in L2/L3 code, and replace with
  tailored data structures for IP connectivity where necessary
- Remove many of the includes of common_lib.h where possible
- Slightly clean up openair0_device and in particular move fields to
  eth_state_t where appropriate

Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-07-02 14:55:07 +02:00
Robert Schmidt
35cf1c91bb Merge remote-tracking branch 'GuidoCasati/issue-963' into integration_2026_w27
NAS: refactor Identity Request handler and enforce spec-compliant security checks (#217)

Align UE Identity Request handling with TS 24.501 by checking the NAS
security header and rejecting unprotected non-SUCI requests.

Also, refactor Identity Response identity selection based on the
requested identity types, add to a helper and handle unavailable
identities in the UE context.

Closes: #82
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-By: Rakesh Mundlamuri <rakesh.mundlamuri@openairinterface.org
2026-07-02 14:54:04 +02:00
Robert Schmidt
b58631ce79 Merge remote-tracking branch 'bpodrygajlo/ut-speedup' into integration_2026_w27
ci: Speedup unit test dockerfile (#255)

Use docker cache, reduce the number of installed apt packages and reduce
compilation scope.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 14:53:15 +02:00
Guido Casati
860232782a fix (NGAP/RRC): map spare RRC establishment causes to NGAP notAvailable
A UE RRCSetupRequest with establishmentCause in the spare range (10–15)
could abort nr-softmodem: RRC asserted on cause >= NGAP_RRC_CAUSE_LAST,
and cause 11 failed NGAP ASN.1 encode when passed through by index.

NR RRC (TS 38.331) and NGAP RRCEstablishmentCause (TS 38.413 §9.3.1.111)
do not share one integer namespace: spare values must not be sent
as NGAP causes.

Remove the RRC AssertFatal on causes. When building
InitialUEMessage, map RRC indices 0–9 to the matching NGAP cause
and map anything else to notAvailable, as specified for unmapped UE
causes.

Changes:
- Drop establishment-cause range AssertFatal in rrc_gNB_send_NGAP_NAS_FIRST_REQ
- Add rrc2ngap_establishment_cause() and use it for InitialUEMessage RRCEstablishmentCause
- Remove redundant DevCheck on establishment_cause in ngap_gNB_handle_nas_first_req
- Adopt mapping logic in rrc_gNB_send_NGAP_NAS_FIRST_REQ

Closes: #127

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
e179a6df8f fix (UE RRC): queue MAC reset before RLC teardown on RRCSetup fallback
When the gNB answers RRCReestablishmentRequest with RRCSetup (TS 38.331
clause 5.3.3.4 fallback), the UE must release all bearers except SRB0 before
applying the new masterCellGroup. CONFIG_RESET was queued after RLC release,
so the MAC slot thread kept polling LCID 1 from a stale lc_ordered_list while
the RLC entity was already NULL.

Queue NR_MAC_RRC_CONFIG_RESET (RRC_SETUP_REESTAB_RESUME) first, then release
PDCP, RLC, and SDAP. On the MAC thread, clear lc_ordered_list except SRB0
before release_mac_configuration() so the UL scheduler stops looping released
logical channels until CONFIG_CG re-adds SRB1.

Changes:
- rrc_UE.c: move CONFIG_RESET to the start of nr_rrc_rrcsetup_fallback()
- config_ue.c: drop non-SRB0 lc_ordered_list entries on RRC_SETUP_REESTAB_RESUME

Closes: #128

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
1c659403b7 fix (RRC): log in nr_rrc_rrcsetup_fallback
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
1a1502ac5a fix (gNB RRC): release RRC connection after last DRB
TS 38.331 §5.3.1.1 forbids RRC_CONNECTED with SRB2 and zero DRBs.
Releasing all DRBs must release the RRC connection.

After PDU session release (3GPP TS 23.502 §4.3.4.2), OAI left
SRB2 up and COTS UEs could re-establish into an invalid configuration.

On NGAP PDU Session Release Response, if no DRBs remain and SRB2 is
active, send NGAP UE Context Release Request. AMF Command drives
RRCRelease on the existing CU-CP path.

Closes: #148

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
cb9dab74e3 fix (gNB RRC): skip CU-UP notify on re-estab without DRBs
Avoid E1 bearer modification when the UE has no DRBs left (e.g. after PDU
session release). (TS 38.331 clause 5.3.5.6.5 DRB with reestablishPDCP)

Changes:
- cuup_notify_reestablishment(): return early if drbs is empty

Fixes: #148

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
f79f5087e5 RRCReestablishmentRequest: add doxygen documentation
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
f5aaacd9e8 RRCReestablishmentRequest: replace random value magic number with constant
Replace the magic number 0x7fffffffff used for masking 39-bit random
values with a named constant NR_RRC_RANDOM_VALUE_39_BIT_MASK defined
in nr_rrc_defs.h.

Refs:
- TS 38.331 5.3.3.3: Random UE identity (39-bit values)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:30 +02:00
Guido Casati
cde7d98a61 RRCReestablishmentRequest: add C-RNTI constants and fix spec compliance
Replace hardcoded C-RNTI range values (0x1, 0xffef) with named constants
NR_C_RNTI_MIN and NR_C_RNTI_MAX. Correct maximum value to 0xfff2 per TS 38.321
Table 7.1-1. Change invalid C-RNTI handling to return early instead of
fallback to RRCSetup for spec compliance.

Fallback to RRCSetup only occurs when UE context cannot be retrieved,
not for validation errors, which for out-of-range values could
come from malicious UEs.

Major changes:
- Add NR_C_RNTI_MIN (0x0001) and NR_C_RNTI_MAX (0xfff2) constants to
  common/platform_constants.h
- Replace magic numbers in C-RNTI validation with named constants
- Change error path from fallback_rrc_setup to return for invalid C-RNTI
- Early validation before further processing

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:12:29 +02:00
Guido Casati
d568920466 RRCReestablishmentRequest: add input parameters validation
Add defensive parameter checks at the start of
rrc_handle_RRCReestablishmentRequest to prevent NULL pointer
dereferences and invalid assoc_id handling.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:03:24 +02:00
Guido Casati
2716a5a3dc RRCReestablishmentRequest: add PCI range validation
Add validation of physCellId against valid range (0-1007) per TS 38.331
specification. Invalid PCI values are now rejected early in the
reestablishment procedure.

This prevents processing of invalid reestablishment requests with
out-of-range PCI values, improving robustness and spec compliance.

Major changes:
- Add NR_PHYS_CELL_ID_MAX to common/platform_constants.h
- Add PCI range check in rrc_handle_RRCReestablishmentRequest() before
  cell lookup, returning early on invalid values

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:03:24 +02:00
Guido Casati
ede596cfc7 RRCReestablishmentRequest: note RRCSetup fallback when UE context is missing
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:03:24 +02:00
Guido Casati
33ff225739 fix (RRC): add SRB2 and DRB validation checks for reestablishment
Add validation checks per TS 38.331 5.3.7.1 to ensure SRB2 and at
least one DRB are active before processing RRC reestablishment
requests. Use more specific NGAP cause code for procedure failures.

Reestablishment requests without SRB2 or DRB setup now properly
fall back to RRC setup with appropriate NGAP cause indication

Major changes:
- Add SRB2 active check before processing reestablishment
- Add DRB active check (at least one DRB required)

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 14:03:24 +02:00
Robert Schmidt
11cf337b6a Merge remote-tracking branch 'turkeryilmaz/minorfix_imscope_psABInote' into integration_2026_w27
minorfix(imscope): moving alignas from struct type to atomic variable (#238)

I was receiving the note below when I built with "imscope_record", so
this is the simplest PR to avoid the psABI note while signing the new
individual CLA.

    [194/11156] Building CXX object openair1/PHY/TOOLS/imscope/CMakeFiles/imscope_record.dir/imscope_record.cpp.o
    In file included from /home/turker/Documents/OpenAirInterface/26w25/openair1/PHY/defs_nr_UE.h:12,
    from /home/turker/Documents/OpenAirInterface/26w25/openair1/PHY/TOOLS/imscope/imscope_internal.h:12,
    from /home/turker/Documents/OpenAirInterface/26w25/openair1/PHY/TOOLS/imscope/imscope_record.cpp:5:
    /usr/include/c++/13/atomic: In member function ‘_Tp std::atomic<_Tp>::exchange(_Tp, std::memory_order) [with _Tp = ImScopeDumpInstruction]’:
    /usr/include/c++/13/atomic:311:7: note: the ABI for passing parameters with 64-byte alignment has changed in GCC 4.6
    311 | exchange(_Tp __i, memory_order __m = memory_order_seq_cst) noexcept
    | ^~~~~~~~

Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-02 13:34:58 +02:00
Robert Schmidt
c5d1895540 Merge remote-tracking branch 'origin/dlsim_type0_test' into integration_2026_w27
DLSIM test for type0 PDSCH frequency allocation (#192)

Adding the test requires some changes in gNB L2 structures but it
doesn't implement the full support of type0 allocation in L2

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 13:34:58 +02:00
Robert Schmidt
58eebfe1d8 Merge remote-tracking branch 'origin/large-du-profile-k' into integration_2026_w27
[FHI72] Update the xran K release tag due to the PR openairinterface/o-du-phy#3 (#257)

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 13:34:42 +02:00
Turker Yilmaz
3dbb22cad8 minorfix(imscope): moving alignas from struct type to atomic variable to avoid psABI note
Signed-off-by: Turker Yilmaz <turker87@gmail.com>
2026-07-02 12:47:57 +02:00
Guido Casati
3226e5bb34 doc (NAS): update doc/5Gnas.md
Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 11:04:27 +02:00
Guido Casati
b61d41a440 tests: add --identity-guti to nr-ue-nas-simulator
Add an optional Identity Request test to the NAS/NGAP simulator.
With --identity-guti, the Initial UE Message carries a random 5G-GUTI
plus matching NGAP 5G-S-TMSI and GUAMI so the AMF requests SUCI per TS 23.502
registration step 6. The simulator then continues the usual attach, PDU
session, and deregistration flow.

Changes:
- nr-ue-nas-simulator: seed_unknown_guti(), identity_request BOOLPARAM,
  NGAP ue_identity fill in send_initial_ue_message()
- Update README

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-02 11:03:57 +02:00
Bartosz Podrygajlo
7b5baa5104 ci: Speedup fhi72 dockerfiles
Move COPY . . down in 7.2 dockerfiles. This layer doesn't cache well due to
changes in docker context, moving it down allows docker builder to cache other
layers that are not affected by the context.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-02 10:26:50 +02:00
Bartosz Podrygajlo
cea29f9c3f ci: Speedup unit test dockerfile
Use docker cache, reduce the number of installed apt packages, add docker ignore
entry for build directory

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-02 10:18:42 +02:00
Robert Schmidt
a65ddf5cd3 Merge remote-tracking branch 'ChrisAB/pbch_decoding_slot_five_before_phy_config' into integration_2026_w27
fix(NR_UE): gate half-frame PBCH mirror on caller-provided ssb_period (#220)

is_ssb_in_symbol() read cfg->ssb_table.ssb_period directly, but before
the FAPI PHY config request arrives (received_config_request == false)
that field is still 0, which it interprets as a 5ms period and enables
the half-frame PBCH mirror. get_ssb_index_in_symbol() already assumes
the 20ms default period for its frame-periodicity test, so the two
disagree and the UE attempts a spurious PBCH decode in the second
half-frame (slot 5 for mu=0) before being configured.

Closes: #219
Reviewed-by: Francesco Mani <email@francescomani.it>
2026-07-02 09:04:35 +02:00
Robert Schmidt
e47e28301f Merge remote-tracking branch 'bpodrygajlo/zmq-warning-cleanup' into integration_2026_w27
fix(zmq): Fix clang compiler warnings in zmq radio (#244)

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 09:00:33 +02:00
Robert Schmidt
8c4e48a670 Merge remote-tracking branch 'bpodrygajlo/pcap-to-github' into integration_2026_w27
ci: Move test pcaps to github (#256)

Move fronthaul capture artifact to my github fork to improve CI
stability.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 08:59:00 +02:00
Robert Schmidt
6d75ca8e9c Move fhstate into eth structure
fhstate is only used in the ETHERNET library, so move it to a smaller
scope. In AW2S it is write-only, so we can remove it.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-07-02 08:46:09 +02:00
Robert Schmidt
0d4a4a6d78 Merge remote-tracking branch 'rmagueta/fix_init_context_sss_nr' into integration_2026_w27
Fix function init_context_sss_nr() (#239)

Reduce of the size of the d_sss matrix.  This PR resolves a regression
found in the PR #152

Reviewed-by: Laurent THOMAS <laurent.thomas@open-cells.com>
2026-07-02 07:59:09 +02:00
Guido Casati
0abc46b708 NAS: refactor Identity Request handler and enforce spec-compliant security checks
Align UE Identity Request handling with TS 24.501 by checking the NAS
security header and rejecting unprotected non-SUCI requests.

Also, refactor Identity Response identity selection based on the
requested identity types, add to a helper and handle unavailable
identities in the UE context.

Closes #82

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-07-01 19:09:48 +02:00
Teodora Vladić
05af0556af Update the xran K release tag due to the PR https://github.com/openairinterface/o-du-phy/pull/3
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-07-01 13:29:10 +02:00
Alex Jiao
14ec3a930f NR UE: add assert for reserved bits per symbol not multiple of Qm
map_overlapped_ack() divides num_reserved_bits_on_sym by Qm to get the
number of reserved REs. Add assertion to catch cases where the reserved
bit count is not a multiple of Qm, which would indicate a bug in the
upstream UCI-on-PUSCH mapping.

Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
2026-07-01 19:23:41 +08:00
Bartosz Podrygajlo
cd376c904b ci: Move test pcaps to github
Move fronthaul capture artifact to my github fork to improve CI stability.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-01 11:37:43 +02:00
Bartosz Podrygajlo
2a3f0ffc02 fix: Address clang compiler warning in zmq_radio (non-c-typedef-for-linkage)
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-07-01 10:39:41 +02:00
Roberto Louro Magueta
e59473f8fe Fix function init_context_sss_nr() and reduction of the size of the d_sss matrix
Signed-off-by: Roberto Louro Magueta <rmagueta@allbesmart.pt>
2026-06-30 17:01:32 +01:00
Francesco Mani
ea3e1dbaf2 remove negative init for dlsim parameters
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-30 14:08:03 +02:00
Robert Schmidt
326f2a0cfb Merge remote-tracking branch 'aswanthkc/fix-check-sa/nsa' into integration_2026_w27
fix: proper SA/NSA mode detection (#233)

Use IS_SA_MODE(get_softmodem_params()) for proper SA/NSA check

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 12:30:58 +02:00
Robert Schmidt
fd794a85c1 Merge remote-tracking branch 'aswanthkc/issue-911-fix-3' into integration_2026_w27
Cleanup: NGAP reconnection debug log (#227)

I was using this print statement just for debugging Also this debug
statement should be shown only after reconnecting not at the first
connection.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 12:29:20 +02:00
Robert Schmidt
9c0577ad1e Remove unused is_init
This field is never used anywhere.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:09 +02:00
Robert Schmidt
102792c373 Move eCPRI fields to ethernet struct
These fields are only accessed from the ETHERNET library, which has a
dedicated ethernet struct (which is in device->priv). Move in there to
reduce visibility.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:09 +02:00
Robert Schmidt
61a1e8c0d8 Remove common_lib.h includes
After having removed eth_params_t from all L2/L3 files, we can reduce
the scope of common_lib.h, and remove the include from many files.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:09 +02:00
Robert Schmidt
c2dbfd79d5 Reformat ethernet_lib.c with clang-format
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
Robert Schmidt
bc32bf8875 Remove eth_params_s from nr_mac_gNB.h
We don't actually configure ethernet paramaters, only IP. Further, the
appropriate type f1ap_net_config_t already exists, so use this instead.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
Robert Schmidt
9f5ac5ac8e Remove eth_params_t from configure_nr_nfapi_vnf()
There are no ethernet parameters to be configured, and it can be
simplified.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
Robert Schmidt
ac738ae714 Remove RRC eth_params
In 4G, it's not used.
In 5G, there is no need, as we can pass it directly to the CU task.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
Robert Schmidt
6f7a26d681 Remove duplicate code
The same code is further up.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
Robert Schmidt
6d437cffcf Remove unused MAC.eth_params_s
This field is used as an intermediate "storage facility", but is never
really used. Besides, in the MAC, we store IP information, not Ethernet
information, so it's wrong anyway.

Also remove memory leaks through strdup(), the pointer lifetime is
smaller than that of MacRLC_ParamList.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-30 11:34:08 +02:00
aswanthkc
52c53c868f fix: proper SA/NSA mode detection
- Use IS_SA_MODE(get_softmodem_params()) for proper SA/NSA check

Signed-off-by: aswanthkc <a.kallangaattuvalasu@northeastern.edu>
2026-06-29 21:33:16 +00:00
aswanthkc
274d999526 Cleanup: NGAP reconnection debug log
This debug log should be shown only after
reconnecting not at the first connection.

Signed-off-by: aswanthkc <a.kallangaattuvalasu@northeastern.edu>
2026-06-29 21:25:31 +00:00
Robert Schmidt
c4125d1c74 Fix license in files from PR #211
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-29 16:27:21 +02:00
Francesco Mani
919b61f147 move default rbSize and rbStart selection in dlsim before call to ptrs config
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:39 +02:00
Francesco Mani
5b2a75f518 add dlsim test for type0 pdsch frequency allocation
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:39 +02:00
Francesco Mani
0b7c6bbb95 fixes in DLSCH RB extraction
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:39 +02:00
Francesco Mani
d81d30af28 remove unnecessary indentation
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:39 +02:00
Francesco Mani
7b2252d52d fix UE CSI-RS unavailable resources for type0 PDSCH
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:39 +02:00
Francesco Mani
fe211685ec setup DLSIM to test type0 DLSCH allocation
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:21:38 +02:00
Francesco Mani
29e75444e6 cleanup dlsim input parameters
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:07:18 +02:00
Francesco Mani
eba9f272ce include options for allocation type in sched_pdsch
(set to type1 in all scenarios)

Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-29 16:07:15 +02:00
Robert Schmidt
09c9bb998f Merge remote-tracking branch 'origin/integration_2026_w26' into develop
Integration: 2026.w26

- #221 Fix for issue 216
- #224 Bugfix PUCCH Format 2
- #210 Cleanup NR RU options
- #223 fix (f1ap): DRB-Information criticality and non-dynamic fiveQI handling in DU
- #218 fix (nas): parse UE Registration Reject and add NAS connection release
- remove OAI logo (image) due to possible trademark problems
- #194 NGAP Procedures for Xn Handover: Path Switch Request and Path Switch Request Acknowledge
- #235 fix(vrtsim): Reduce OS scheduler pressure caused by vrtsim timing thread
- #211 Add NR O-RU
- #241 fix: Make test_vrtsim stable

Closes: #216
Closes: #212
Closes: #174
2026-06-29 15:41:14 +02:00
Robert Schmidt
6d4573905a Merge remote-tracking branch 'bpodrygajlo/test_vrtsim_stability' into integration_2026_w26
fix: Make test_vrtsim stable (#241)

Ensure all resources used by test_vrtsim are unique so that there is no
overlap, which might cause an issue in CI.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-29 11:51:15 +02:00
Robert Schmidt
dc01a3540a Merge remote-tracking branch 'bpodrygajlo/nr-oru' into integration_2026_w26
Add NR O-RU (#211)

This is a PR that will add a functional vrtsim-based nr-oru to the build
system. This is only slightly different SW to the one being presented at
the OAI summer 2026 workshop.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-By: Merkebu Girmay <merkebu.girmay@openairinterface.org>
2026-06-29 11:50:01 +02:00
Robert Schmidt
c3f6af1008 Merge remote-tracking branch 'bpodrygajlo/vrtsim-perf-fix' into integration_2026_w26
fix(vrtsim): Reduce OS scheduler pressure caused by vrtsim timing thread (#235)

Increase the usleep to 20 uS to reduce the pressure on the OS scheduler.
This reduces the number of context switches and increases performance.
The value 20uS is approximately half a symbol in mu1 and shouldn't cause
too much extra delay.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-29 09:20:07 +02:00
Robert Schmidt
633b6de92a Merge remote-tracking branch 'venkatareddy453/ngap-pathswitch-and-ack-for-xnho' into integration_2026_w26
NGAP Procedures for Xn Handover: Path Switch Request Path Switch Request Acknowledge (#194)

This PR adds the following NGAP Path Switch procedures to support Xn
handover in accordance with 3GPP TS 38.413v16.2.0.

NGAP Path Switch Request: add encode and ITTI support

- Add a case in TASK_NGAP to support ITTI message from RRC to NGAP
- Add Path Switch Request message type definitions
- Implement encoder for Path Switch Request (3GPP TS 38.413v16.2.0
  §9.2.3.8)
  * RAN UE NGAP ID (M)
  * Source AMF UE NGAP ID (M)
  * User Location Information (M)
  * UE Security Capabilities (M)
  * PDU Session Resource to be Switched in Downlink List (M)
- Add required ASN.1 header includes for Path Switch Request IEs

NGAP Path Switch Request Acknowledge: add decode and handler

- Add handler at NGAP for Path Switch Request Acknowledge from AMF
- Add Path Switch Request Acknowledge message type definitions
- Implement decoder for Path Switch Request Acknowledge (3GPP TS
  38.413v16.2.0 §9.2.3.9)
  * AMF UE NGAP ID (M)
  * RAN UE NGAP ID (M)
  * Security Context (M)
  * PDU Session Resource Switched List (M)
  * Allowed NSSAI (M)
- Add required ASN.1 header includes for Path Switch Request Acknowledge
  IEs

Notes:

- Added a case NGAP_PATH_SWITCH_REQ in task_ngap to support ITTI message
  from RRC, and this will be allocated at
- RRC when the target gNB receives the RRC complete message from the UE
  during Xn handover.
- OAI CN5G does not support any of the NGAP path switch procedures
  (request, ack, and failure) as of today to test Xn Handover.
- We tested the procedures in an E2E setup at IISc with two gNBs in
  E1+F1+7.2x split containing the Xn handover code and connected to the
  same 5G core (used Open5GS & Aether-OnRamp separately), and also did
  not observe any Path Switch Failure.
- The implementation of Path Switch Failure at OAI RAN will be done
  along with the implementation of Path Switch procedures in OAI CN5G
  once the XNAP integration in OAI RAN is complete.

Acknowledgement:

> This work has been partially carried out as part of Xn Handover
development at the Indian Institute of Science (IISc), Bengaluru.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-By: Guido Casati <guido.casati@openairinterface.org>
2026-06-29 09:17:07 +02:00
Bartosz Podrygajlo
ad5e941bc9 fix: Make test_vrtsim stable
Ensure all resources used by test_vrtsim are unique so that there is no overlap,
which might cause an issue in CI.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-27 06:45:39 +02:00
Bartosz Podrygajlo
75d7fa275d fix(vrtsim): Reduce OS scheduler pressure caused by vrtsim timing thread
Increase the usleep to 20 uS to reduce the pressure on the OS scheduler. This
reduces the number of context switches and increases performance. The value 20uS
is approximately half a symbol in mu1 and shouldn't cause too much extra delay.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-26 14:17:18 +02:00
Shubhika Garg
328968cecf ci: ensure retrigger-ci label is cleaned up after every CI run
- Previously the label was only removed when it was the labeled event
    that triggered the workflow. If a PR was synchronized or reopened
    while the label was already present, it would not be removed by
    the workflow.
  - Created a dedicated cleanup job that depends on all other jobs
    with if: always(), ensuring it runs regardless of whether upstream
    jobs succeeded, failed, or were skipped.
  - Now the cleanup runs on every pull_request_target event and removes
    the label if present, ensuring it is always cleared after CI completes.

Co-authored-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-06-26 13:56:32 +02:00
Shubhika Garg
a5f60811c5 doc: improve commit signing guidelines in CONTRIBUTING.md
Signed-off-by: Shubhika Garg <shubhika.garg@openairinterface.org>
2026-06-26 13:56:31 +02:00
Shubhika Garg
840abd0048 ci: Add check for unsigned commits in the PR
- Add verify-signed-commits job that runs on PR
  - check-labels and all downstream jobs are skipped
    if signed commmit verifications fails
  - Fetch all commits in the PR via GitHub API
    and check verification status
  - Fail and print the unsigned commit SHAs if any
    unsigned commits are found.

Co-authored-by: Shubhika Garg <shubhika.garg@openairinterface.org>
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-06-26 13:56:30 +02:00
Venkatareddy Akumalla
829d6fe7cf NGAP Path Switch Request Acknowledge: add decode and handler
- Add handler at NGAP for Path Switch Request Acknowledge from AMF
- Add Path Switch Request Acknowledge message type definitions
- Implement decoder for Path Switch Request Acknowledge
  (Reference: 3GPP TS 38.413 §9.2.3.9)
    - AMF UE NGAP ID (M)
    - RAN UE NGAP ID (M)
    - Security Context (M)
    - PDU Session Resource Switched List (M)
    - Allowed NSSAI (M)
- Add required ASN.1 header includes for Path Switch Request Acknowledge IEs

Co-authored-by: Rakesh BB <rakesh.bb@fsid-iisc.in>
Signed-off-by: Venkatareddy Akumalla <venkatareddy.akumalla@openairinterface.org>
2026-06-26 10:09:29 +00:00
Venkatareddy Akumalla
db7dbda8e2 NGAP Path Switch Request: add encode and ITTI support
- Add a case in TASK_NGAP to support ITTI message from RRC to NGAP
- Add Path Switch Request message type definitions
- Implement encoder for Path Switch Request (3GPP TS 38.413v16.2.0 §9.2.3.8)
    - RAN UE NGAP ID (M)
    - Source AMF UE NGAP ID (M)
    - User Location Information (M)
    - UE Security Capabilities (M)
    - PDU Session Resource to be Switched in Downlink List (M)
- Add required ASN.1 header includes for Path Switch Request IEs

Co-authored-by: Rakesh BB <rakesh.bb@fsid-iisc.in>
Signed-off-by: Venkatareddy Akumalla <venkatareddy.akumalla@openairinterface.org>
2026-06-26 10:09:12 +00:00
Robert Schmidt
f448088c8d docs: remove OAI logo due to trademark issues
Duranta is a Linux Foundation networking project and does not have
permission to display the trademarked OAI logo
(https://openairinterface.org/logo/), which belongs to the
OpenAirInterface Software Alliance (legal entity "OPENAIRINTERFACE").

Remove logo to avoid any legal problems.

Remove some superfluous text with HTML code in some documentation.

Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-26 11:56:30 +02:00
Robert Schmidt
a3268a9c2f Merge remote-tracking branch 'GuidoCasati/issue-174' into integration_2026_w26
fix (nas): parse UE Registration Reject and add NAS connection release (#218)

The nrUE NAS task read the Registration Reject cause at a hardcoded
offset and called exit(1). This commits adds processing of
plain/security-protected 5GMM headers, decoding of the mandatory cause
per TS 24.501 §8.2.7, set 5GMM state.

Softmodem is stopped via termination_procedure and local AS detach
(send_nas_detach_req without wait_release -> 1) RRC IDLE 2)
NR_NAS_CONN_RELEASE_IND 3) itti_wait_tasks_unblock).

Changes:

- Add fgmm_registration_reject enc/dec
- Add handle_registration_reject()
- Replace inline FGS_REGISTRATION_REJECT case
- Fix unprotected_allowed() for plain Registration Reject
- Add nas_lib_test round-trip (Illegal_UE / 0x03)

Note: optional IEs (T3346, T3502, EAP) not decoded, §5.5.1.2.5 procedure TODO

Closes: #174
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-26 11:56:06 +02:00
Robert Schmidt
716c29fe1f Merge remote-tracking branch 'GuidoCasati/issue-212' into integration_2026_w26
fix (f1ap): DRB-Information criticality and non-dynamic fiveQI handling in DU (#223)

Aligns OAI F1AP encoding with TS 38.473 for DRB-Information criticality
on UE Context Modification, and clarifies how non-dynamic fiveQI is
handled across F1AP/E1AP decode and CU RRC.

Non-dynamic fiveQI may name standardized or pre-configured profiles on
the wire (0..255). F1/E1 libs now accept the full ASN range and reject
only out-of-range values. Pre-configured and other non-standardized
non-dynamic 5QI remain unsupported at RRC via the existing
is_5qi_standardized() skip.

DU is now gracefully handling unsupported 5QIs instead of crashing.

Closes: #212
Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
2026-06-26 11:55:34 +02:00
Guido Casati
3cbf777835 fix (nas): parse UE Registration Reject and stop UE cleanly
The nrUE NAS task mishandled FGS_REGISTRATION_REJECT: it read the 5GMM
cause at a hardcoded offset and called exit(1) from the NAS ITTI task,
which could abort the process while PHY threads were still running.

This commit adds processing of plain/security-protected 5GMM headers,
decoding of the mandatory cause per TS 24.501 §8.2.7, set 5GMM state.

Softmodem is stopped via termination_procedure and local AS detach
(send_nas_detach_req without wait_release -> 1) RRC IDLE 2)
NR_NAS_CONN_RELEASE_IND 3) itti_wait_tasks_unblock).

Ignore duplicate rejects once termination has started.

Changes:
- Add fgmm_registration_reject enc/dec
- Add handle_registration_reject()
- Replace inline FGS_REGISTRATION_REJECT case
- Fix unprotected_allowed() for plain Registration Reject
- Add nas_lib_test round-trip (Illegal_UE / 0x03)

Note: optional IEs (T3346, T3502, EAP) not decoded, stop-on-reject
replaces 21a7f99 exit(1), full §5.5.1.2.5 procedure remains TODO.

Closes #174

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-06-25 16:59:29 +02:00
Robert Schmidt
813a90bd21 Merge remote-tracking branch 'origin/cleanup_ru_options' into integration_2026_w26
Cleanup NR RU options (#210)

This PR is part of the effort to split LTE and NR code to avoid
cross-compilation as described in #110.

In nr-ru.c, up to now, we relied on if4_tools.c for some if4p5 functions
but what's in that file is strictly LTE using LTE frame parameters, so
it shouldn't work in NR. As a matter of fact, after some investigation
and feedback from @teodora-vladic about 7.2 split, none of those
functions are used in practice in NR and they can be removed. If this
works, we move one step closer to the full compilation split of LTE and
NR.

Reviewed-by: Robert Schmidt <robert.schmidt@openairinterface.org>
Reviewed-by: Teodora Vladić <teodora.vladic@openairinterface.org>
2026-06-25 13:20:02 +02:00
Robert Schmidt
0548191fac Merge remote-tracking branch 'sakthivelvelumani/bugfix-pucchf2' into integration_2026_w26
Bugfix PUCCH Format 2 (#224)

Fix RE offset computation in PUCCH format 2 generation. This fixes DL
retx with OCUDU in the CI.

Reviewed-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-25 13:18:22 +02:00
Robert Schmidt
145ab730c0 Merge remote-tracking branch 'alexjiao2021/fix-issue-216' into integration_2026_w26
Fix for issue 216 (#221)

When O_ACK <= 2, the reserved HARQ-ACK positions are selected from the
codeword using a d-factor stepping algorithm. The d-factor was computed
and applied at bit granularity, causing it to step through the flat
positions array mid-modulation-group when Qm > 1.

This scattered ACK/placeholder bits across more REs than intended,
mismatching the RE selection of the demultiplexer which operates at RE
granularity. As a result, placeholder positions were marked on REs that
the gNB treats as pure ULSCH, causing those positions to be transmitted
without the normal scrambling sequence XOR. This corrupts the ULSCH soft
bits at the gNB side, leading to PUSCH CRC failure.

Fix: compute the d-factor in RE units and step through the positions
array in strides of d_factor_re * Qm so that whole Qm-bit modulation
groups are always selected or skipped together.

Observed with O_ACK=1, MCS27 (256QAM), beta_offset_idx=11 where
Q_dash_ACK=6 (reserved) vs Q_dash_ACK_actual=3, giving d_factor=2. PUSCH
CRC was KO at third-party signal analyzers (MATLAB/WaveJudge) while the
OCUDU gNB could still decode due to soft LDPC margin.

Closes: #216
Reviewed-by: Francesco Mani <email@francescomani.it>
Reviewed-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-06-25 13:18:22 +02:00
Guido Casati
5f2108edb0 fix (mac): gracefully handle unsupported 5QIs on DRB setup
When a CU sends a DRB with a non-dynamic 5QI that is not in the
DU MAC lookup table, get_non_dynamic_priority() used to AssertFatal and
abort. Return -1 and log a warning instead, and skip flows with negative
priority when deriving the logical-channel priority so setup can continue.

A more spec-aligned fix would require F1AP UE Context Setup/Modification
Failure (not implemented) in case of a logical error in the DU:

> Where the logical error occurs in a request message of a class 1 procedure,
> and the procedure has a message to report this unsuccessful outcome,
> this message shall be sent with an appropriate cause value.

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-06-25 12:53:24 +02:00
Bartosz Podrygajlo
b7ab31854e feat(oru): PUSCH reception
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-24 13:48:23 +02:00
Sakthivel Velumani
f7d0ec0960 ue,phy,pucchf2: fix re offset calculation
Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-06-23 13:55:18 +00:00
Sakthivel Velumani
7032477784 log: fix typo in printing mac stats
Signed-off-by: Sakthivel Velumani <s.velumani@northeastern.edu>
2026-06-23 13:54:46 +00:00
Guido Casati
7e0c0fe18b fix (f1ap,e1ap,rrc): accept ASN non-dynamic fiveQI at lib decode
Non-dynamic fiveQI on F1AP/E1AP may name standardized or pre-configured
profiles (TS 38.473), the range is 0..255. Decode now accepts
MIN_FIVEQI..MAX_FIVEQI (0..255) and fails only when fiveQI is outside
that range. Pre-configured and other non-standardized non-dynamic values
remain unsupported: RRC still skips them via is_5qi_standardized().

Changes:
- F1AP/E1AP: range-check 0..255 only
- improve documentation

Refs:
- 3GPP TS 38.473 §9.3.1.49 (non-dynamic standardized or pre-configured 5QI)
- 3GPP TS 23.501 §5.7.2.1

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-06-23 12:55:42 +02:00
Guido Casati
5a478deac4 fix (f1ap): encode DRB-Information with ignore criticality in setup-mod
UE Context Modification DRB setup-mod encoding sent id_DRB_Information
with reject criticality. TS 38.473 defines that IE as ignore
(QoSInformation-ExtIEs, id-DRB-Information).

Changes:
- Set QoSInformation extension id_DRB_Information criticality to ignore
  in encode_drbs_to_setupmod()

Signed-off-by: Guido Casati <guido.casati@openairinterface.org>
2026-06-23 11:08:41 +02:00
Alex Jiao
40024d4caa fix(NR_UE): UCI on PUSCH ACK mapping d-factor uses RE granularity
When O_ACK <= 2, the reserved HARQ-ACK positions are selected from
the codeword using a d-factor stepping algorithm. The d-factor was
computed and applied at bit granularity, causing it to step through
the flat positions array mid-modulation-group when Qm > 1.

This scattered ACK/placeholder bits across more REs than intended,
mismatching the RE selection of the demultiplexer which operates at
RE granularity. As a result, placeholder positions were marked on
REs that the gNB treats as pure ULSCH, causing those positions to be
transmitted without the normal scrambling sequence XOR. This corrupts
the ULSCH soft bits at the gNB side, leading to PUSCH CRC failure.

Fix: compute the d-factor in RE units and step through the positions
array in strides of d_factor_re * Qm so that whole Qm-bit modulation
groups are always selected or skipped together.

Observed with O_ACK=1, MCS27 (256QAM), beta_offset_idx=11 where
Q_dash_ACK=6 (reserved) vs Q_dash_ACK_actual=3, giving d_factor=2.
PUSCH CRC was KO at third-party signal analyzers (MATLAB/WaveJudge)
while the OCUDU gNB could still decode due to soft LDPC margin.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>

Signed-off-by: Alex Jiao <alex.jiao@keysight.com>
2026-06-22 22:41:08 +08:00
ChrisAB
dcae0070a6 fix(NR_UE): gate half-frame PBCH mirror on caller-provided ssb_period
is_ssb_in_symbol() read cfg->ssb_table.ssb_period directly, but before the FAPI PHY config request arrives (received_config_request == false) that field is still 0, which it interprets as a 5ms period and enables the half-frame PBCH mirror. get_ssb_index_in_symbol() already assumes the 20ms default period for its frame-periodicity test, so the two disagree and the UE attempts a spurious PBCH decode in the second half-frame (slot 5 for mu=0) before being configured.

Closes: #219
Signed-off-by: ChrisAB <cristianbatrin@gmail.com>
2026-06-22 16:53:36 +03:00
Bartosz Podrygajlo
3fb74dc66e feat(oru): PRACH reception
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:23:49 +02:00
Bartosz Podrygajlo
82c3c04018 feat(phy): Allow per-repetition PRACH reception
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:22:56 +02:00
Bartosz Podrygajlo
df5a9facbe fix(oru): Fix timestamp generation in O-RU
Instead of guessing what is the absolute timestamp of each symbol returned
from fronthaul, add absolute hyper frame from GPS Epoch to the O-RU. The
O-RU can calculate the absolute sample index from hyper frame reliably
regardless of the frame/slot/symbol ordering.

Add config file for 1x1 O-DU that matches the edited O-RU config.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:22:30 +02:00
Bartosz Podrygajlo
71c692d727 fix(oru): Clean exit of the O-RU
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:21:02 +02:00
Bartosz Podrygajlo
159c219bdd feat(oru): DL FH processing
Implemented downlink fronthaul processing: symbol rotation, dft and cp insertion

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:20:51 +02:00
Bartosz Podrygajlo
ecfe769205 feat(phy): add a new tx_rf_symbols function to allow per symbol transmission
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:20:41 +02:00
Bartosz Podrygajlo
21329e1b70 feat(oru): Load vrtsim in O-RU and start reading samples
Added oru_south_read_thread that reads samples from vrtsim.
No UL FH processing is done as of this commit.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:20:25 +02:00
Bartosz Podrygajlo
0aa3f89c52 feat(radio): add a function which loads the specified radio library
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:20:15 +02:00
Bartosz Podrygajlo
cd79eec3ec feat(vrtsim): add get_timestamp to openair_device_t and vrtsim
This function can be used to convert a timespec struct to a
openair0_timestamp. It can be used to synchronize the realtime
clock to the device sample number.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:20:06 +02:00
Bartosz Podrygajlo
a1049e8dd1 feat(oru): Add north read thread
Start O-RU fronthaul library inside the O-RU application and start
reading DL IQ data.

Added configuration elements to the ORUs section to support configuring
the library.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:19:51 +02:00
Bartosz Podrygajlo
28786af8bf feat(oru): Setup frame params according to new configuration
Added initialization of frame params according to configuration added
in the new ORUs section. Only one ORU is supported right now.

Added example config that allows starting the ORU and verifying manually
that the config is read.

Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:16:24 +02:00
Bartosz Podrygajlo
eb1ae91051 ci: Build O-RU fronthaul in ubuntu fhi72 build image
Signed-off-by: Bartosz Podrygajlo <bartosz.podrygajlo@openairinterface.org>
2026-06-20 08:16:13 +02:00
Francesco Mani
0f50702483 remove dependence from LTE if4p5 functions
fh_if4p5_south_in and fh_if4p5_south_out are called in NR for 7.2 split but never actually executed
they are overwritten at runtime in ru_thread

Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-19 11:35:50 +02:00
Francesco Mani
7a0489842b cleanup nr-ru set_functions to leave only used options in NR
Signed-off-by: Francesco Mani <email@francescomani.it>
2026-06-19 09:46:38 +02:00
Shubhika Garg
910f8cef33 ci: skip ci when no mandatory ci label present on PR
- Add check-labels job that runs first on the PR events
  - Fail early if none of the mandatory CI labels are present
    (BUILD-ONLY, documentation, 4G-LTE, 5G-NR, nrUE, CI, retrigger-ci)
  - Skip detect-changes, require-maintainer-approval and trigger-jenkins
    when no mandatory CI label is found
  - Push events are unaffected by the label check
  - For labeled events, only retrigger-ci triggers the pipeline;
    any other label addition is skipped

Co-authored-by: Shubhika Garg <shubhika.garg@openairinterface.org>
Signed-off-by: Jaroslava Fiedlerova <jaroslava.fiedlerova@openairinterface.org>
2026-06-18 14:20:59 +02:00
196 changed files with 6080 additions and 2610 deletions

View File

@@ -5,3 +5,4 @@ common/utils/T/T_IDs.h
common/utils/T/T_messages.txt.h
common/utils/T/genids
common/utils/T/genids.o
build/

View File

@@ -17,12 +17,72 @@ permissions:
pull-requests: write
jobs:
verify-signed-commits:
runs-on: ubuntu-24.04
if: github.event_name == 'pull_request_target'
steps:
- name: Check all commits are signed
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
UNSIGNED=$(gh api repos/${{ github.repository }}/pulls/${{ github.event.pull_request.number }}/commits \
--paginate --jq '[.[] | select(.commit.verification.verified == false) | .sha[:7]] | join(", ")')
if [ -n "$UNSIGNED" ]; then
echo -e "$UNSIGNED commits are missing signature"
gh pr comment ${{ github.event.pull_request.number }} --repo ${{ github.repository }} --body "$(cat <<EOF
**Validation:**
The following commit(s) are missing signature:
$UNSIGNED
Please use 'git commit -S' to sign your commits.
For detailed instructions, refer to the [CONTRIBUTING.md](https://github.com/duranta-project/openairinterface5g/blob/develop/CONTRIBUTING.md) file at the root of this repository or [GitHub Docs](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)
EOF
)"
exit 1
fi
check-labels:
needs: verify-signed-commits
runs-on: ubuntu-24.04
if: github.event_name == 'pull_request_target'
steps:
- name: Check for mandatory CI label
run: |
PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels) }}' | jq -r '.[].name')
for label in \
"${{ vars.BUILD_ONLY_LABEL }}" \
"${{ vars.DOC_LABEL }}" \
"${{ vars.NR_LABEL }}" \
"${{ vars.NRUE_LABEL }}" \
"${{ vars.LTE_LABEL }}" \
"${{ vars.CI_LABEL }}" \
"${{ vars.RETRIGGER_CI_LABEL }}"; do
if echo "$PR_LABELS" | grep -qxF "$label"; then
exit 0
fi
done
exit 1
detect-changes:
needs: check-labels
runs-on: ubuntu-24.04
if: |
github.event.action != 'labeled' ||
github.event.label.name == vars.RETRIGGER_CI_LABEL
always() && (
github.event_name == 'push' ||
needs.check-labels.result == 'success'
) && (
github.event.action != 'labeled' ||
github.event.label.name == vars.RETRIGGER_CI_LABEL
)
outputs:
protected_files_changed: ${{ steps.filter.outputs.protected }}
@@ -49,8 +109,9 @@ jobs:
require-maintainer-approval:
needs: detect-changes
if: needs.detect-changes.outputs.protected_files_changed == 'true'
if: |
always() &&
needs.detect-changes.outputs.protected_files_changed == 'true'
runs-on: ubuntu-24.04
environment:
@@ -61,6 +122,7 @@ jobs:
trigger-jenkins:
needs:
- check-labels
- detect-changes
- require-maintainer-approval
@@ -105,8 +167,24 @@ jobs:
-H "X-Github-Hook-Installation-Target-Type: repository" \
--data @/tmp/filtered_payload.json
cleanup:
needs:
- verify-signed-commits
- check-labels
- detect-changes
- require-maintainer-approval
- trigger-jenkins
if: always() && github.event_name == 'pull_request_target'
runs-on: ubuntu-24.04
steps:
- name: Remove retrigger-ci label
if: always() && github.event.action == 'labeled' && github.event.label.name == vars.RETRIGGER_CI_LABEL
run: gh pr edit ${{ github.event.pull_request.number }} --remove-label "${{ vars.RETRIGGER_CI_LABEL }}"
run: |
PR_LABELS=$(echo '${{ toJson(github.event.pull_request.labels) }}' | jq -r '.[].name')
if echo "$PR_LABELS" | grep -qxF "${{ vars.RETRIGGER_CI_LABEL }}"; then
gh pr edit ${{ github.event.pull_request.number }} --remove-label "${{ vars.RETRIGGER_CI_LABEL }}" --repo ${{ github.repository }}
fi
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}

View File

@@ -383,6 +383,12 @@ endif()
add_boolean_option(ENABLE_IMSCOPE OFF "Enable phy scope based on imgui" OFF)
add_boolean_option(ENABLE_IMSCOPE_RECORD OFF "Enable recording IQ data for imscope" OFF)
#########################
##### E3 AGENT
#########################
set(E3_AGENT "OFF" CACHE STRING "O-RAN nGRG E3 Agent for dApps")
set_property(CACHE E3_AGENT PROPERTY STRINGS "ON" "OFF")
##################################################
# ASN.1 grammar C code generation & dependencies #
##################################################
@@ -855,7 +861,6 @@ set(NR_PHY_SRC_RU
${OPENAIR1_DIR}/PHY/MODULATION/nr_beamforming.c
${OPENAIR1_DIR}/PHY/MODULATION/slot_fep_nr.c
${OPENAIR1_DIR}/PHY/INIT/nr_init_ru.c
${OPENAIR1_DIR}/PHY/if4_tools.c
)
set(PHY_SRC_UE
@@ -1769,23 +1774,25 @@ target_link_libraries(lte-uesoftmodem PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs
# force the generation of ASN.1 so that we don't need to wait during the build
target_link_libraries(lte-uesoftmodem PRIVATE
asn1_lte_rrc asn1_s1ap asn1_m2ap asn1_m3ap asn1_x2ap)
# nr RRU
add_executable(nr-oru
${OPENAIR_DIR}/executables/nr-ru.c
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
${OPENAIR_DIR}/executables/main_nr_ru.c
)
target_link_libraries(nr-oru PRIVATE
UTIL NR_PHY_RU PHY_NR shlib_loader dl
radio_common softmodem_common nfapi_pnf_lib)
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
barrier actor nfapi_user_lib)
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
if (OAI_RU_FRONTHAUL)
add_executable(nr-oru
${OPENAIR_DIR}/executables/nr-ru.c
${OPENAIR_DIR}/openair1/PHY/INIT/nr_parms.c
${OPENAIR_DIR}/openair1/SCHED_NR/phy_frame_config_nr.c
${OPENAIR_DIR}/openair1/SCHED_NR/nr_prach_procedures.c
${OPENAIR_DIR}/openair1/SCHED_NR/nr_ru_procedures.c
${OPENAIR_DIR}/openair1/SCHED/phy_procedures_lte_common.c
${OPENAIR_DIR}/executables/main_nr_ru.c
${OPENAIR_DIR}/executables/nr-oru.c
)
target_link_libraries(nr-oru PRIVATE
UTIL NR_PHY_RU PHY_NR shlib_loader dl oru_fh MAC_NR_COMMON
radio_common softmodem_common nfapi_pnf_lib)
target_link_libraries(nr-oru PRIVATE pthread m CONFIG_LIB rt ${T_LIB} utils
barrier actor nfapi_user_lib)
target_link_libraries(nr-oru PRIVATE asn1_nr_rrc_hdrs asn1_lte_rrc_hdrs nr_phy_common time_management)
endif()
# nr-softmodem
###################################################
@@ -1830,6 +1837,11 @@ if(E2_AGENT)
target_compile_definitions(nr-softmodem PRIVATE ${E2AP_VERSION} ${KPM_VERSION} E2_AGENT)
endif()
if (E3_AGENT)
target_link_libraries(nr-softmodem PRIVATE e3ap)
target_compile_definitions(nr-softmodem PRIVATE E3_AGENT)
endif()
# force the generation of ASN.1 so that we don't need to wait during the build
target_link_libraries(nr-softmodem PRIVATE

View File

@@ -1,6 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# Contributing to OpenAirInterface
# Contributing to Duranta
We want to make contributing to this project as easy and transparent as possible.
@@ -18,41 +18,30 @@ We want to make contributing to this project as easy and transparent as possible
## Commit Guidelines
Every pull request must pass two CI checks before it can be merged:
1. **[Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)**:
Each commit must include a `Signed-off-by:` trailer in the commit message.
Use `git commit -s` (or `--signoff`).
2. **[Verified commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)**:
Each commit must be cryptographically signed using SSH or GPG keys to confirm
its origin.
### Signing Commits
To sign commits:
You can also get the verified label on your commits via using [SSH keys or GPG
keys](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-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 PR.
GitHub supports commit signing using either SSH keys or GPG keys. For the
step-by-step setup (key generation, Git configuration, registering the key on
GitHub, and verifying signatures locally), see the
[commit signing section of the Git guide](doc/git-guide.md#setting-up-commit-signing).
> **NOTE:** If your commits are not signed, the CI framework will not accept the PR.
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
By contributing to Duranta, you agree that your contributions will be
licensed under
1. [CSSL v1.0 license](LICENSES/preferred/CSSL-v1.0.txt): for RAN and UE

View File

@@ -1,7 +1,7 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
<h1 align="center">
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-OAI-Combined.png" alt="Duranta OAI" width="550"></a>
<a href="https://lfnetworking.org/projects/duranta/"><img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="Duranta OAI" width="550"></a>
</h1>
<p align="center">

View File

@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.

View File

@@ -14,7 +14,7 @@ description: A Helm subchart for 4G physims network function
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.

View File

@@ -14,7 +14,7 @@ description: A Helm chart for physical simulators network function
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.

View File

@@ -14,7 +14,7 @@ description: A Helm subchart for 5G physims network function
# pipeline. Library charts do not define any templates and therefore cannot be deployed.
type: application
icon: http://www.openairinterface.org/wp-content/uploads/2015/06/cropped-oai_final_logo.png
icon: https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png
# This is the chart version. This version number should be incremented each time you make changes
# to the chart and its templates, including the app version.

View File

@@ -48,7 +48,6 @@ def nsaTDDB200Status = ''
def saTDDB200Status = ''
def phytestLDPCoffloadStatus = ''
def saAW2SStatus = ''
def cn5gCOTSUESanityCheck = ''
def saAERIALStatus = ''
def saMultiAntennaStatus = ''
def saFHI72Status = ''
@@ -712,29 +711,6 @@ pipeline {
}
}
}
stage ("Sanity-Check OAI-CN5G") {
when { expression {do5Gtest} }
steps {
script {
triggerCN5GDownstreamJob ('OAI-CN5G-COTS-UE-Test')
}
}
post {
always {
script {
// Using a unique variable name for each test stage to avoid overwriting on a global variable
// due to parallel-time concurrency
cn5gCOTSUESanityCheck = finalizeDownstreamJob('OAI-CN5G-COTS-UE-Test')
}
}
failure {
script {
currentBuild.result = 'FAILURE'
failingStages += cn5gCOTSUESanityCheck
}
}
}
}
stage ("SA-AERIAL-CN5G") {
when { expression {do5Gtest} }
steps {

View File

@@ -7,7 +7,7 @@
#define N_ANTENNA_UL 1
#define TDD 1
log_options: "all.level=debug,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
log_options: "all.level=info,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
log_filename: "/tmp/ue.log",
/* Enable remote API and Web interface */

View File

@@ -7,7 +7,7 @@
#define N_ANTENNA_UL 1
#define TDD 1
log_options: "all.level=debug,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
log_options: "all.level=info,all.max_size=0,nas.level=debug,nas.max_size=1,rrc.level=debug,rrc.max_size=1",
log_filename: "/tmp/ue.log",
/* Enable remote API and Web interface */

View File

@@ -217,11 +217,19 @@ amarisoft_00105_40MHz:
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
Tracing:
Start: '' # nothing to be done
Stop: '' # nothing to be done
Collect: 'cp /tmp/ue.log %%log_dir%%'
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
Tracing:
Start: '' # nothing to be done
Stop: '' # nothing to be done
Collect: 'cp /tmp/ue.log %%log_dir%%'
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

@@ -75,12 +75,12 @@ class HTMLManagement():
self.htmlFile.write(' <tr style="border-collapse: collapse; border: none;">\n')
self.htmlFile.write(' <td style="border-collapse: collapse; border: none;">\n')
self.htmlFile.write(' <a href="http://www.openairinterface.org/">\n')
self.htmlFile.write(' <img src="http://www.openairinterface.org/wp-content/uploads/2016/03/cropped-oai_final_logo2.png" alt="" border="none" height=50 width=150>\n')
self.htmlFile.write(' <img src="https://raw.githubusercontent.com/duranta-project/governance/main/logos/Duranta-Logo-Color.png" alt="" border="none" style="margin-right: 2rem;" width=150>\n')
self.htmlFile.write(' </img>\n')
self.htmlFile.write(' </a>\n')
self.htmlFile.write(' </td>\n')
self.htmlFile.write(' <td style="border-collapse: collapse; border: none; vertical-align: center;">\n')
self.htmlFile.write(' <b><font size = "6">Job Summary -- Job: TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
self.htmlFile.write(' <b><font size = "6">TEMPLATE_JOB_NAME -- Build-ID: TEMPLATE_BUILD_ID</font></b>\n')
self.htmlFile.write(' </td>\n')
self.htmlFile.write(' </tr>\n')
self.htmlFile.write(' </table>\n')

View File

@@ -184,7 +184,8 @@ L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
}
prach_dtx_threshold = 200;
}
);
RUs = (

View File

@@ -184,7 +184,8 @@ L1s = (
{
num_cc = 1;
tr_n_preference = "local_mac";
}
prach_dtx_threshold = 200;
}
);
RUs = (

View File

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

View File

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

View File

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

View File

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

View File

@@ -10,8 +10,7 @@
FROM ran-base:develop AS ran-tests
RUN apt-get update && \
DEBIAN_FRONTEND=noninteractive apt-get upgrade --yes && \
DEBIAN_FRONTEND=noninteractive apt-get install --yes \
DEBIAN_FRONTEND=noninteractive apt-get install --yes --no-install-recommends \
libgtest-dev \
libyaml-cpp-dev \
libzmq3-dev \
@@ -26,7 +25,9 @@ WORKDIR /oai-ran
COPY . .
WORKDIR /oai-ran/build
RUN cmake .. -GNinja \
RUN --mount=type=cache,target=/root/.cache/ccache/ \
--mount=type=cache,target=/root/.cache/cpm/ \
cmake .. -GNinja \
-DENABLE_TESTS=ON -DOAI_ZMQ=ON -DCMAKE_BUILD_TYPE=Debug \
-DSANITIZE_ADDRESS=True -DOAI_VRTSIM_TAPS_CLIENT=ON -DOAI_RU_FRONTHAUL=ON \
&& \

View File

@@ -1,20 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
<table style="border-collapse: collapse; border: none;">
<tr style="border-collapse: collapse; border: none;">
<td style="border-collapse: collapse; border: none;">
<a href="http://www.openairinterface.org/">
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
</img>
</a>
</td>
<td style="border-collapse: collapse; border: none; vertical-align: center;">
<b><font size = "5">OAI Full Stack 4G-LTE RF simulation with containers</font></b>
</td>
</tr>
</table>
This page is only valid for an `Ubuntu 22` host.
OAI Full Stack 4G-LTE RF simulation with containers
**Table of Contents**

View File

@@ -1,22 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
<table style="border-collapse: collapse; border: none;">
<tr style="border-collapse: collapse; border: none;">
<td style="border-collapse: collapse; border: none;">
<a href="http://www.openairinterface.org/">
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
</img>
</a>
</td>
<td style="border-collapse: collapse; border: none; vertical-align: center;">
<b><font size = "5">OAI Full Stack 5G-NR RF simulation with containers</font></b>
</td>
</tr>
</table>
This page is only valid for an `Ubuntu 22` host.
**NOTE: this version (2023-01-27) has been updated for the `v1.5.0` version of the `OAI 5G CN`.**
OAI Full Stack 5G-NR RF simulation with containers
**Table of Contents**

View File

@@ -1,18 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
<table style="border-collapse: collapse; border: none;">
<tr style="border-collapse: collapse; border: none;">
<td style="border-collapse: collapse; border: none;">
<a href="http://www.openairinterface.org/">
<img src="../../../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
</img>
</a>
</td>
<td style="border-collapse: collapse; border: none; vertical-align: center;">
<b><font size = "5">OAI O-RAN 7.2 Front-haul Docker Compose</font></b>
</td>
</tr>
</table>
OAI O-RAN 7.2 Front-haul Docker Compose
![Docker deploy 7.2](../../../doc/images/docker-deploy-oai-7-2.png)

View File

@@ -43,6 +43,8 @@ Options:
Sets build directory (will be <oai-root>/cmake_targets/<build-dir>/build)
--build-e2
Enable the the E2 Agent
--build-e3
Enable the the E3 Agent
-I | --install-external-packages
Installs required packages such as LibXML, asn1.1 compiler, ...
This option will require root password
@@ -143,6 +145,10 @@ function main() {
CMAKE_CMD="$CMAKE_CMD -DE2_AGENT=ON"
shift
;;
--build-e3 )
CMAKE_CMD="$CMAKE_CMD -DE3_AGENT=ON"
shift
;;
-I | --install-external-packages)
INSTALL_EXTERNAL=1
echo_info "Will install external packages"
@@ -201,6 +207,7 @@ function main() {
shift;;
--nrRU)
NRRU=1
CMAKE_CMD="$CMAKE_CMD -DOAI_RU_FRONTHAUL=ON"
TARGET_LIST="$TARGET_LIST nr-oru"
echo_info "Will compile NR O-RU"
shift;;

View File

@@ -6,7 +6,7 @@
# Finds the xran library. Note that the library number is as follows:
# - oran_e_maintenance_release_v1.5 -> 5.1.6
# - oran_f_release_v1.3 -> 6.1.4
# - oran_k_release_v1.1 -> 11.1.1
# - oran_k_release_v1.3 -> 11.1.3
#
# Required options
# ^^^^^^^^^^^^^^^^

View File

@@ -494,8 +494,8 @@ install_simde_from_source(){
if [[ -v SIMDE_VERSION ]]; then
git checkout -f $SIMDE_VERSION
else
# At time of writing, last working commit for OAI: c7f26b7
git checkout c7f26b73ba8e874b95c2cec2b497826ad2188f68
# At time of writing, last working commit for OAI: 1c68d9a
git checkout 1c68d9ad60bf63f3fb527c4ee3b2319d828ffcc6
fi
# Showing which version is used
git log -n1

View File

@@ -39,8 +39,9 @@ typedef enum { NON_DYNAMIC, DYNAMIC } fiveQI_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 */
* - Pre-configured 5QI values: pre-configured in the AN (not in Table 5.7.4-1)
* - Dynamically assigned 5QI values: require signaling of QoS characteristics as part of QoS profile
* OAI implements standardized non-dynamic 5QI only. */
#define MIN_FIVEQI 0
#define MAX_STANDARDIZED_FIVEQI 90
#define MAX_FIVEQI 255

View File

@@ -45,6 +45,8 @@
#define NR_MAX_NB_PDU_SESSIONS (256)
#define NR_MAX_NB_ALLOWED_SNSSAI (8) /* Maximum number of allowed S-NSSAI in TS 38.413 */
#define MAX_DRBS_PER_UE (32) /* Maximum number of Data Radio Bearers per UE
* defined for NGAP in TS 38.413 - maxnoofDRBs */
#define MAX_PDUS_PER_UE (8) /* Maximum number of PDU Sessions per UE */
@@ -55,6 +57,8 @@
/** Maximum number of Paging Occasions per Paging Frame (TS 38.331 PCCH-Config) */
#define NR_PCCH_MAX_PO 4
#define NR_PHYS_CELL_ID_MAX 1007 /* Maximum Physical Cell ID (0..1007) */
#define NB_RB_MBMS_MAX (29 * 16) /* 29 = LTE_maxSessionPerPMCH + 16 = LTE_maxServiceCount from LTE_asn_constant.h */
#define NB_RAB_MAX 11 /* from LTE_maxDRB in LTE_asn_constant.h */

View File

@@ -11,7 +11,7 @@ LOG_D(<component>,<format>,<argument>,...)
LOG_T(<component>,<format>,<argument>,...)
)
```
these macros are used in place of the printf C function. The additionnal ***component*** parameter identifies the functionnal module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
these macros are used in place of the printf C function. The additional ***component*** parameter identifies the functional module which generates the message. At run time, the message will only be printed if the configured log level for the component is greater or equal than the macro level used in the code.
| macro | level letter | level value | level name |
|:---------|:---------------|:---------------|----------------:|

View File

@@ -155,6 +155,7 @@ static const char *const flag_name[] = {FOREACH_FLAG(FLAG_TEXT) ""};
COMP_DEF(NFAPI_PNF, log) \
COMP_DEF(ITTI, log) \
COMP_DEF(UTIL, log) \
COMP_DEF(E3AP, log) \
COMP_DEF(MAX_LOG_PREDEF_COMPONENTS, )
#define COMP_ENUM(comp, file_extension) comp,

View File

@@ -434,6 +434,27 @@ ID = LEGACY_MAC_TRACE
GROUP = ALL:LEGACY_MAC:LEGACY_GROUP_TRACE:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_INFO
DESC = E3AP legacy logs - info level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_INFO:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_ERROR
DESC = E3AP legacy logs - error level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_ERROR:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_WARNING
DESC = E3AP legacy logs - warning level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_WARNING:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_DEBUG
DESC = E3AP legacy logs - debug level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_DEBUG:LEGACY
FORMAT = string,log
ID = LEGACY_E3AP_TRACE
DESC = E3AP legacy logs - trace level
GROUP = ALL:LEGACY_E3AP:LEGACY_GROUP_TRACE:LEGACY
FORMAT = string,log
ID = LEGACY_NR_MAC_INFO
DESC = NR_MAC legacy logs - info level
GROUP = ALL:LEGACY_NR_MAC:LEGACY_GROUP_INFO:LEGACY

View File

@@ -31,7 +31,6 @@
#include <arpa/inet.h>
#include <common/utils/assertions.h>
#include <common/utils/LOG/log.h>
#include "common_lib.h"
#ifdef T
#undef T

View File

@@ -17,13 +17,19 @@ Key 5GS NAS messages:
## OAI Implementation Status
The following table lists implemented NAS messages and whether there is an encoder or decoder function, and if a corresponding unit test exists.
The following table lists NAS messages with dedicated `encode_*` / `decode_*` codecs under
`openair3/NAS/NR_UE/5GS/` (`5GMM/MSG`, `5GSM/MSG`). Unit test entries refer to
[`nas_lib_test.c`](../openair3/NAS/NR_UE/5GS/tests/nas_lib_test.c), most are encode/decode round-trips.
| Type | Message | Encoding | Decoding | Unit test |
|-------|-------------------------------------------|----------|----------|------------|
| 5GMM | Service Request | yes | yes | yes |
| 5GMM | Service Accept | yes | yes | yes |
| 5GMM | Service Reject | yes | yes | yes |
| 5GMM | Authentication Failure | yes | yes | yes |
| 5GMM | Authentication Reject | yes | yes | yes |
| 5GMM | Security Mode Reject | yes | yes | yes |
| 5GMM | Identity Request | no | yes | no |
| 5GMM | Authentication Response | yes | no | no |
| 5GMM | Identity Response | yes | no | no |
| 5GMM | Security Mode Complete | yes | no | no |
@@ -38,6 +44,23 @@ The following table lists implemented NAS messages and whether there is an encod
| 5GSM | PDU Session Establishment Request | yes | no | no |
| 5GSM | PDU Session Establishment Accept | no | yes | no |
### Runtime-handled messages
These network-originated messages are handled in [`nr_nas_msg.c`](../openair3/NAS/NR_UE/nr_nas_msg.c):
* Authentication Request
* Security Mode Command
* Downlink NAS Transport
* Deregistration Accept (UE originating)
* Registration Reject
* PDU Session Establishment Reject
## Integration testing
End-to-end attach can be tested with the [NR UE NAS simulator](../tests/nr-ue-nas-simulator/README.md),
which runs the OAI UE NAS stack and gNB NGAP against the AMF, forwarding NAS PDUs without PHY/MAC/RLC.
Use `nas_lib_test` for isolated codec round-trips.
### Code Structure
[openair3/NAS/NR_UE/nr_nas_msg.c](../openair3/NAS/NR_UE/nr_nas_msg.c):

View File

@@ -51,7 +51,7 @@ PROJECT_BRIEF = "Full experimental OpenSource LTE and NR implementation
# pixels and the maximum width should not exceed 200 pixels. Doxygen will copy
# the logo to the output directory.
PROJECT_LOGO = @CMAKE_SOURCE_DIR@/doc/images/oai_logo.png
PROJECT_LOGO =
# The OUTPUT_DIRECTORY tag is used to specify the (relative or absolute) path
# into which the generated documentation will be written. If a relative path is

View File

@@ -421,15 +421,14 @@ git apply ~/openairinterface5g/cmake_targets/tools/oran_fhi_integration_patches/
```bash
git clone https://github.com/openairinterface/o-du-phy.git ~/phy
cd ~/phy
git checkout 11.1.1 # the tag points to the `main` branch which has all patches applied that are relevant for OAI integration; the tag matches the value of cmake variable `K_VERSION`
git checkout 11.1.3 # the tag points to the `main` branch which has all patches applied that are relevant for OAI integration; the tag matches the value of cmake variable `K_VERSION`
```
or use `xran_DOWNLOAD` option when compiling OAI gNB.
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
`XRAN_DIR` to set the path to the fronthaul library. For building for a Arm
target, set as well the environment variable `TARGET=armv8`.
`XRAN_DIR` to set the path to the fronthaul library.
**Note**: you need at least gcc-11 and g++-11.
@@ -1259,7 +1258,7 @@ The OAI configuration file [`gnb.sa.band78.106prb.fhi72.1x1-proto-ru.conf`](../t
First, compile the RU as outlined in the [building ProtO-RU tutorial](https://github.com/NUS-CIR/ProtO-RU/tree/proto-ru?tab=readme-ov-file#building-proto-ru).
Then, ensure that both your DU and ProtO-RU host are PTP synchronized.
Next, use the RU config, [protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml](https://github.com/NUS-CIR/ProtO-RU/blob/proto-ru/proto-ru/conf-files/protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml), which corresponds to the above mentioned DU config file.
Next, use the RU config, [protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml](https://github.com/NUS-CIR/ProtO-RU/blob/proto-ru/proto-ru/conf-files/protoru-OAI-B210-TDD-n78-40MHz-1x1-30kHz.yml), which corresponds to the above mentioned DU config file. Please note that the RU delay profile might need to be adjusted according to the setup. The E2E test with xran K release required `T2a_max_cp_ul: 2985`.
In addition, please adapt the DU MAC address and VLAN tag to your needs.
ProtO-RU was successfully tested with USRP B210.
@@ -1557,7 +1556,10 @@ Edit the sample OAI gNB configuration file and check following parameters:
* `RUs` section
* Set an isolated core for RU thread `ru_thread_core`, in our environment we are using CPU 6
* If testing with a numerology different than 1 (e.g., FDD with numerology 0),
set `nr_scs_for_raster` to the used numerology, and adapt `sl_ahead`: it must be
strictly less than the number of slots in a frame (e.g., 5 for numerology 0).
* `fhi_72` (FrontHaul Interface) section: this config follows the structure
that is employed by the xRAN library (`xran_fh_init` and `xran_fh_config`
structs in the code):

View File

@@ -133,6 +133,9 @@ The other SDRs (AW2S, LimeSDR, ...) have no READMEs.
## Developer tools
- [code-style-contrib.md](./code-style-contrib.md): overall working practices, code style, and review process
- [git-guide.md](./git-guide.md): Git how-tos — commit signing setup, branch
management, submodules, recovering from mistakes, reusing conflict
resolutions (rerere)
- [cross-compile.md](./cross-compile.md): how to cross-compile OAI for ARM
- [clang-format.md](./clang-format.md): how to format the code. See also the
next entry for an error detection tool.

View File

@@ -90,9 +90,9 @@ the [MAC configuration](../MAC/mac-usage.md) as well for SIB configuration.
`0xffffff` is a reserved value and means "no SD"
Note that: SST=1, no SD is "eMBB"; SST=2, no SD is "URLLC"; SST=3, no SD
is "mMTC"
- `enable_sdap` (default: true): enable the use of the SDAP layer. If
deactivated, a transparent SDAP header is prepended to packets, but no
further processing is being done.
- `enable_sdap` (default: true): set `sdap-HeaderUL` and `sdap-HeaderDL` to
present in the RRC `SDAP-Config` IE for SA PDU sessions. If false, both
headers are absent (per DRB). SDAP entities are still created, SDAP layer always enabled.
- `cu_sibs` (default: `[]`) list of SIBs to give to the DU for transmission.
Currently supported:
- SIB2: serving-cell reselection parameters (configured in `sib2_config`)

View File

@@ -118,43 +118,12 @@ e.g., `v3.0`. We target to make releases bi-yearly.
### How to manage your own branch
Before starting to work, please make sure to branch off the latest `develop`
branch. Make commits as appropriate.
```bash
$ git fetch origin
$ git checkout develop
$ git checkout -b my-new-feature # name as appropriate
$ git add -p # add changes for change set 1, use `-p` to review what to include
$ git commit # in the editor, describe your changes
$ git add -p # add changes for change set 2
$ git commit # in the editor, describe your changes
```
Again, commit message should take multiple lines; after the initial title, a
blank line should follow. Read the `DISCUSSION` section in `man git commit` for
more information.
If your development takes longer, make sure to synchronize regularly with
`origin/develop` using `git rebase`:
```bash
$ git fetch origin
$ git rebase -i origin/develop
```
If you do logical changes, you should not have to resolve the same conflicts
over and over again. Note that if you jumped over multiple develop tags, you
can also rebase in intermediate steps, in case you fear the differences might
be too big.
```
$ git rebase -i 2023.w38
$ git rebase -i 2023.w41
$ git rebase -i develop
```
Once you rebased, push the changes to the remote
```
$ git push origin my-new-feature --force-with-lease # force with lease let's you only overwrite what you also have locally in origin/my-new-feature
```
Branch off the latest `develop` branch before starting to work, keep your
branch synchronized with `origin/develop` through regular rebases, and push
with `--force-with-lease` after rebasing. The step-by-step commands — including
how to rebase over multiple develop tags in intermediate steps and how to avoid
resolving the same conflicts repeatedly with `git rerere` — are in the
[branch management section of the Git guide](./git-guide.md#managing-your-own-branch).
### Use of git commit trailers

View File

@@ -61,7 +61,7 @@ Correct (Markdown):
**Bold text**
*Italic text*
![OAI Logo](./images/oai_final_logo.png) instead of <img src="./images/oai_final_logo.png" alt="OAI Logo">
![some image](./path/to/image.png)
| Feature | Description |
|---------------|---------------------------------|
@@ -75,7 +75,7 @@ Incorrect (HTML):
<b>Bold text</b>
<i>Italic text</i>
<img src="./images/oai_final_logo.png" alt="OAI Logo">
<img src="./path/to/image.png" alt="some image">
<table style="border-collapse: collapse; border: none;">
<tr>
@@ -114,7 +114,7 @@ Incorrect (HTML):
is](https://www.kernel.org/doc/html/latest/process/submitting-patches.html#separate-your-changes).
- See [OAI CN5G configuration files](#22-oai-cn5g-configuration-files) for
details.
- ![OAI Logo](./images/oai_final_logo.png)
- ![some image](./path/to/image.png)
```
## Inline code for technical elements

390
doc/git-guide.md Normal file
View File

@@ -0,0 +1,390 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
# Git guide
This guide collects the practical Git knowledge needed to contribute to OAI in
one place: how to set up commit signing, how to manage and synchronize a
feature branch, how to handle submodules, how to recover from common mistakes,
and how to avoid resolving the same merge conflicts over and over. It is a
how-to companion to the contribution *requirements*, which are
defined in [CONTRIBUTING.md](../CONTRIBUTING.md) (CLA, DCO, verified commits)
and [code-style-contrib.md](./code-style-contrib.md) (workflow, commit, and
review policy).
[[_TOC_]]
## Setting up commit signing
Every commit in a pull request must pass two independent CI checks, described
in [CONTRIBUTING.md](../CONTRIBUTING.md#commit-guidelines):
1. **[Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin)**:
the commit message carries a `Signed-off-by:` trailer.
2. **[Verified commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/about-commit-signature-verification)**:
the commit is cryptographically signed with an SSH or GPG key.
These are two different mechanisms: the sign-off is a line of text you add with
`git commit -s`, the signature is created automatically by Git once signing is
configured. You need both.
### Quick setup (SSH signing)
```bash
# 1. Generate a key pair (skip if you already have one)
ssh-keygen -t ed25519 -C "<your email>"
# 2. Configure Git to sign every commit with it
git config --global user.name "<Your Name>"
git config --global user.email "<your email>"
git config --global gpg.format ssh
git config --global user.signingkey ~/.ssh/id_ed25519.pub
git config --global commit.gpgsign true
```
> **NOTE:**
> `--global` writes to `~/.gitconfig` and applies to every repository on the
> machine. When working on a shared server (or with different identities in
> different clones), drop `--global` to store the same settings in the current
> repository's `.git/config` only.
Then print the public key with `cat ~/.ssh/id_ed25519.pub` and paste it into
your GitHub account under *Settings → SSH and GPG keys → New SSH key*, choosing
the key type **Signing Key**.
> **NOTE:**
> Adding an SSH key for repository access does not automatically enable commit
> signing. The key must also be added under GitHub's Signing Keys settings.
For commits to show as *Verified* on GitHub:
- your `git config user.email` must match an email of your GitHub account,
- that email must be [verified in your GitHub account](https://docs.github.com/en/account-and-profile/how-tos/email-preferences/verifying-your-email-address),
- and it must be the email address used for the CLA (see
[CONTRIBUTING.md](../CONTRIBUTING.md)).
If you prefer GPG over SSH, set `gpg.format` to `openpgp` and `user.signingkey`
to your GPG key ID instead; see the [GitHub documentation on signing
commits](https://docs.github.com/en/authentication/managing-commit-signature-verification/signing-commits)
for the full walkthrough of both methods.
### Signing off your commits (DCO)
The `Signed-off-by:` trailer is added with the `-s`/`--signoff` flag:
```bash
git commit -s # new commit
git commit --amend -s --no-edit # add the trailer to the last commit
```
It must read `Signed-off-by: Full Name <email-for-cla>`. See the
[commit trailers section](./code-style-contrib.md#use-of-git-commit-trailers)
of the contribution guidelines for this and other trailers.
### Verifying signed commits
You can verify that commits are properly signed locally using:
```bash
git log --show-signature
```
GitHub should also display a *Verified* badge next to signed commits once the
signing key has been correctly configured in your account.
For SSH commit signing, local Git verification may require an
`allowed_signers` file. This is only used for local verification in Git and is
not required by GitHub. If you see errors such as:
```text
No principal matched
Can't check signature
error: gpg.ssh.allowedSignersFile needs to be configured
```
create the file, add your signing identity, and enable it in your Git config:
```bash
mkdir -p ~/.config/git
echo "user@example.com ssh-ed25519 AAAACexamplekeystringhere" > ~/.config/git/allowed_signers
git config gpg.ssh.allowedSignersFile ~/.config/git/allowed_signers
```
> **NOTE:**
> This is only for local Git signature verification and does not affect GitHub,
> or remote repository behavior.
## Managing your own branch
The general development branch, and the target of every contribution, is
`develop`; see [GET_SOURCES.md](./GET_SOURCES.md) for the branch and tag model
(weekly `YYYY.wXX` tags, `vX.Y` releases). The rules for what a branch should
look like — linear history, small self-contained logical commits, commit
messages that explain *why* — are policy and live in
[code-style-contrib.md](./code-style-contrib.md#workflow).
Before starting to work, please make sure to branch off the latest `develop`
branch. Make commits as appropriate.
```bash
git fetch origin
git checkout develop
git checkout -b my-new-feature # name as appropriate
git add -p # add changes for change set 1, use `-p` to review what to include
git commit -s # in the editor, describe your changes
git add -p # add changes for change set 2
git commit -s # in the editor, describe your changes
```
Recent Git versions also offer `git switch` as a clearer alternative to
`git checkout` for branch operations: `git switch develop` changes branch,
`git switch -c my-new-feature` creates one.
Commit messages should take multiple lines; after the initial title, a blank
line should follow. Read the `DISCUSSION` section in `man git commit` for more
information. For documentation-only commits, prefix the title with `docs:`
(see [doc_best_practices.md](./doc_best_practices.md)).
Code must be formatted with clang-format; an optional pre-commit hook can
check this automatically at every commit — see
[clang-format.md](./clang-format.md) for its installation and how to combine
it with `git add -p`/`git stash -p`.
If your development takes longer, make sure to synchronize regularly with
`origin/develop` using `git rebase`:
```bash
git fetch origin
git rebase -i origin/develop
```
If you do logical changes, you should not have to resolve the same conflicts
over and over again. If the same conflicts do keep reappearing, e.g., when
maintaining a long-lived fork, consider enabling
[`git rerere`](#reusing-conflict-resolutions-with-git-rerere). Note that if
you jumped over multiple develop tags, you can also rebase in intermediate
steps, in case you fear the differences might be too big.
```bash
git rebase -i 2023.w38
git rebase -i 2023.w41
git rebase -i develop
```
Once you rebased, push the changes to the remote:
```bash
git push origin my-new-feature --force-with-lease # force with lease lets you only overwrite what you also have locally in origin/my-new-feature
```
### Fixing up earlier commits
The [workflow policy](./code-style-contrib.md#workflow) asks for a history
without "clean up" commits: when review or testing reveals a problem in an
earlier commit of your branch, fold the fix into that commit instead of
appending a `Fix bug` commit on top. Git automates this with fixup commits and
`--autosquash`:
```bash
git add -p # stage the fix
git commit --fixup=<commit> # creates a commit titled "fixup! <original title>"
git rebase -i --autosquash origin/develop # moves it after <commit> and squashes the two
```
During the `--autosquash` rebase, Git pre-arranges the todo list so each
`fixup!` commit is squashed into the commit it references; you normally just
accept it. The result is the same clean history as if the fix had been part of
the original commit.
A handy variant is `git commit --fixup=amend:<commit>`, which folds in the fix
and also rewrites the commit message: during the `--autosquash` rebase the
editor opens pre-filled with the original message, ready to be edited into the
new one.
## Working with submodules
Parts of the tree are Git submodules. After cloning, and after every branch
switch or pull, make sure they match the superproject:
```bash
git submodule update --init --recursive
```
A recurring review problem is the *unintended submodule pointer update*: a
submodule whose checked-out commit differs from what the superproject records
shows up in `git status` as `modified: <path> (new commits)`, and a broad
`git add .`, `git add -A`, or `git commit -a` silently records the new pointer
in your commit. To avoid it:
- review `git status` before committing and stage files explicitly (e.g. with
`git add -p`) rather than adding everything;
- if a pointer change was staged by accident, unstage it with
`git restore --staged <path>` and realign the submodule with
`git submodule update --init <path>`.
Only commit a submodule pointer change when updating that submodule is the
purpose of the commit, and say so in the commit message.
## Recovering from mistakes
To unstage a file that was added by accident (the changes stay in your working
tree), or to throw away local changes to a file:
```bash
git restore --staged <file> # unstage; keeps the modifications
git restore <file> # discard unstaged modifications - cannot be undone
```
`git reset` moves the current branch to another commit and differs in what it
does to your files:
```bash
git reset --soft HEAD~1 # undo the last commit, keep its changes staged (e.g. to re-split it)
git reset --hard <commit> # make branch, index and working tree identical to <commit>
```
> **Warning:** `git reset --hard` discards all uncommitted changes; there is no
> way to recover them.
Committed work is much harder to lose than it seems: `git reflog` records every
position of `HEAD` (commits, rebases, resets, checkouts) for a retention period
of at least 30 days, even for commits no branch points to anymore. If a rebase
or reset went wrong, find the last good state and reset back to it:
```bash
git reflog # e.g.: e75076172 HEAD@{5}: commit: doc: add git rerere guide
git reset --hard 'HEAD@{5}' # return the branch to that state
```
## Reusing conflict resolutions with git rerere
The `develop` branch is updated roughly once a week. Feature branches that live
for more than a few days therefore have to be re-synced with `develop`
repeatedly, and the same merge conflicts tend to reappear at every sync - often
in the same scheduler, PHY, or RRC files that several contributors touch at
once. Resolving the identical conflict by hand every week is error-prone and
wastes time.
Git ships a built-in feature for exactly this situation: `rerere`, short for
**reuse recorded resolution**. Once enabled, Git remembers how you resolved a
given conflict and replays that resolution automatically the next time the same
conflict appears.
This section explains how to enable and use it. It is a local developer
convenience: nothing about it changes the repository, the history you push, or
the contribution workflow.
### What it does
When a conflict occurs, `rerere` records the conflicted hunk (the *preimage*).
After you resolve it, `rerere` records your resolution (the *postimage*), keyed
by a hash of the preimage. The next time a conflict with the same preimage shows
up - in a later rebase, a later merge, or even another branch - Git reapplies
your recorded resolution instead of presenting the conflict again.
The data lives in `.git/rr-cache/` inside your local clone. It is never part of
any commit and is never pushed.
### Enabling it
Enable it once, globally, so it applies to every repository on your machine:
```bash
git config --global rerere.enabled true
git config --global rerere.autoupdate true
```
`rerere.autoupdate` stages a replayed resolution automatically. Without it, the
resolution is still written into your working tree, but you have to `git add`
the file yourself.
### Typical flow
The first time you hit a conflict after enabling `rerere`, resolve it exactly as
you always have:
```bash
# during a rebase or a merge that conflicts
git status # rerere reports which paths it is recording
# edit the conflicted files, remove the markers
git add <resolved-files>
git rebase --continue # or: git commit, for a merge
```
That resolution is now recorded. The next time the same conflict appears, Git
resolves it for you. With `autoupdate` on, the file is already staged and you can
go straight to:
```bash
git rebase --continue # or git commit
```
Always review the replayed result before continuing - see *Caveats* below.
### Inspecting and undoing recorded resolutions
```bash
git rerere status # paths with a recorded preimage in the current operation
git rerere diff # the resolution rerere is applying
git rerere forget <path> # discard a recorded resolution (e.g. a wrong one)
```
`git rerere forget` is the escape hatch when you recorded a bad resolution: it
drops the cached entry for that path so the next conflict is presented fresh.
### Seeding from existing history
If your branch already contains **merge commits** whose conflicts you resolved
before enabling `rerere`, you can backfill the cache so those resolutions are
available immediately. Git ships a helper for this in `contrib/`:
```bash
sh /path/to/git/contrib/rerere-train.sh origin/develop..HEAD
```
It replays the merge commits in the given range, reconstructs each conflict, and
records the resolution found in the merge commit.
> **Note:** this only works for resolutions captured in merge commits. A purely
> linear (rebased) history has no merge commits to learn from, so there is
> nothing to backfill - `rerere` will simply start recording from your next
> conflict onward.
### Sharing the cache (optional)
The cache is local. If you work across several machines, or want a team to share
resolutions for the same recurring conflicts, copy the directory:
```bash
rsync -a ~/work/oai-A/.git/rr-cache/ ~/work/oai-B/.git/rr-cache/
```
There is no built-in push/pull for the cache; treat it as an ordinary directory
to sync.
### Caveats and good practice
- `rerere` matches on the **exact** conflicting text. If `develop` changed the
lines surrounding your change, the preimage differs and the conflict is
presented as new. This is expected - the resolution is still recorded for the
next identical occurrence.
- A replayed resolution is only as correct as the original. When the code around
a conflict has evolved, an old resolution can apply cleanly yet be wrong.
**Review every replayed resolution and build/test before continuing.**
- `rerere` reduces repeated manual work; it does not change which branch
strategy you use. It helps both when rebasing onto `develop` and when merging
`develop` into a feature branch. Remember that branches intended for
contribution must have a linear history without merge commits (see the
[workflow policy](./code-style-contrib.md#workflow)); a fork can of course
carry merge commits if that is convenient for its development.
## See also
- [CONTRIBUTING.md](../CONTRIBUTING.md) - CLA, DCO, and licensing requirements.
- [code-style-contrib.md](./code-style-contrib.md) - workflow, commit, and
review policy, including commit trailers.
- [GET_SOURCES.md](./GET_SOURCES.md) - branches, tags, and how to obtain the
sources.
- [clang-format.md](./clang-format.md) - code formatting and its Git
integration (pre-commit hook).
- The [Git Book](https://git-scm.com/book/en/v2) and the
[`git rerere` manual](https://git-scm.com/docs/git-rerere)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 271 KiB

After

Width:  |  Height:  |  Size: 161 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 361 KiB

After

Width:  |  Height:  |  Size: 209 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 9.4 KiB

View File

@@ -9,7 +9,7 @@
FROM ran-base:latest AS ran-base
ARG xran_VERSION=11.1.1
ARG xran_VERSION=11.1.3
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00
ENV DEBIAN_FRONTEND=noninteractive
@@ -27,7 +27,6 @@ RUN apt-get update && \
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
## Download and build DPDK
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
@@ -42,7 +41,7 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy && \
git checkout $xran_VERSION &&\
cd /opt/phy/fhi_lib/lib && \
TARGET=armv8 WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
## Build Arm RAN Acceleration Library
RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \
@@ -54,6 +53,8 @@ RUN git clone https://git.gitlab.arm.com/networking/ral.git /opt/ral && \
ninja && \
ninja install
COPY . .
FROM ran-base AS ran-build-fhi72
## Build and install OAI
#run build_oai to build the target image

View File

@@ -12,9 +12,8 @@ ENV TZ=Europe
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
ARG xran_VERSION=11.1.1
ARG xran_VERSION=11.1.3
## Download and build DPDK
RUN wget --no-verbose http://fast.dpdk.org/rel/dpdk-24.11.4.tar.xz && \
@@ -31,6 +30,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
COPY . .
FROM ran-base AS ran-build-fhi72
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00

View File

@@ -9,7 +9,7 @@
FROM ran-base:latest AS ran-base
ARG xran_VERSION=11.1.1
ARG xran_VERSION=11.1.3
ARG DPDK_VERSION=22.11
ARG T2_PATCH=AMD-T2-SDFEC_25-03-1.patch
ENV DEBIAN_FRONTEND=noninteractive
@@ -27,7 +27,7 @@ RUN apt-get update && \
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
## Copy T2 patch
COPY ./$T2_PATCH /opt/.
## Download and build DPDK
@@ -46,6 +46,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-$DPDK_VERSION/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
COPY . .
FROM ran-base AS ran-build-fhi72-t2
## Build and install OAI
## Run build_oai to build the target image, if T2 patch present, build with T2 support

View File

@@ -9,7 +9,7 @@
FROM ran-base:latest AS ran-base
ARG xran_VERSION=11.1.1
ARG xran_VERSION=11.1.3
ARG E2AP_VERSION=E2AP_V3
ARG KPM_VERSION=KPM_V3_00
ENV DEBIAN_FRONTEND=noninteractive
@@ -32,7 +32,6 @@ RUN apt-get update && \
RUN rm -Rf /oai-ran
WORKDIR /oai-ran
COPY . .
RUN git clone https://github.com/CESNET/libyang.git && \
cd libyang && \
@@ -81,6 +80,8 @@ RUN git clone https://github.com/openairinterface/o-du-phy.git /opt/phy && \
cd /opt/phy/fhi_lib/lib && \
WIRELESS_SDK_TOOLCHAIN=gcc RTE_SDK=/oai-ran/dpdk-stable-24.11.4/ XRAN_DIR=/opt/phy/fhi_lib make XRAN_LIB_SO=1
COPY . .
FROM ran-base AS ran-build-fhi72
## Build and install OAI
#run build_oai to build the target image

View File

@@ -1,20 +1,6 @@
<!-- SPDX-License-Identifier: CC-BY-4.0 -->
<table style="border-collapse: collapse; border: none;">
<tr style="border-collapse: collapse; border: none;">
<td style="border-collapse: collapse; border: none;">
<a href="http://www.openairinterface.org/">
<img src="../doc/images/oai_final_logo.png" alt="" border=3 height=50 width=150>
</img>
</a>
</td>
<td style="border-collapse: collapse; border: none; vertical-align: center;">
<b><font size = "5">OAI Docker/Podman Build and Usage Procedures</font></b>
</td>
</tr>
</table>
---
OAI Docker/Podman Build and Usage Procedures
**Table of Contents**

View File

@@ -18,7 +18,6 @@
#include "SCHED/sched_eNB.h"
#include "PHY/LTE_TRANSPORT/transport_proto.h"
#include "nfapi/oai_integration/vendor_ext.h"
#include "radio/COMMON/common_lib.h"
#include "PHY/if4_tools.h"
#include "PHY/LTE_ESTIMATION/lte_estimation.h"

View File

@@ -33,7 +33,6 @@
#include "SCHED/sched_common.h"
#include "common/utils/LOG/log.h"
#include "common/utils/LOG/vcd_signal_dumper.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/ethernet_lib.h"
/* these variables have to be defined before including ENB_APP/enb_paramdef.h */

View File

@@ -18,7 +18,6 @@
#include "common/ran_context.h"
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include <openair1/PHY/phy_extern_ue.h>

View File

@@ -23,7 +23,6 @@
#include <sys/sysinfo.h>
#include <sys/types.h>
#include <unistd.h>
#include "radio/COMMON/common_lib.h"
#include "assertions.h"
#include "PHY/types.h"
#include "PHY/defs_eNB.h"

View File

@@ -19,7 +19,6 @@
#include "common/ran_context.h"
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include "PHY/phy_vars_ue.h"

View File

@@ -10,7 +10,6 @@
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "common/ran_context.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include "PHY/phy_vars.h"
#include "PHY/phy_extern.h"
@@ -22,6 +21,9 @@
#include <executables/softmodem-common.h>
#include <executables/thread-common.h>
#include "executables/nr-softmodem.h"
#include "nr-oru.h"
#include "openair1/PHY/INIT/nr_phy_init.h"
#include "openair1/SCHED_NR/sched_nr.h"
pthread_cond_t sync_cond;
pthread_mutex_t sync_mutex;
@@ -33,6 +35,9 @@ int sf_ahead = 4;
int emulate_rf = 0;
RAN_CONTEXT_t RC;
int64_t uplink_frequency_offset[MAX_NUM_CCs][4];
void nfapi_setmode(nfapi_mode_t nfapi_mode)
@@ -106,26 +111,15 @@ struct timespec timespec_sub(struct timespec, struct timespec)
struct timespec t = {0};
return t;
};
void perform_symbol_rotation(const int nsymb, const int numerology_index, double f0, c16_t *symbol_rotation)
void beam_index_allocation(uint16_t fapi_beam_index,
int ant,
int num_ports,
int symbols_per_slot,
int slot,
uint16_t bitmap_symbols,
int num_ant_max,
uint16_t **ant_beam_id_list)
{
return;
}
void init_timeshift_rotation(const int ofdm_symbol_size,
const int nb_prefix_samples,
const uint ofdm_offset_divisor,
c16_t *timeshift_symbol_rotation)
{
return;
};
int beam_index_allocation(bool das,
int fapi_beam_index,
NR_gNB_COMMON *common_vars,
int slot,
int symbols_per_slot,
int bitmap_symbols)
{
return 0;
}
uint16_t get_first_ant_idx(bool das, uint16_t num_ports_beams, uint16_t beam_id, uint16_t fapi_start_port)
{
@@ -135,6 +129,12 @@ void nr_fill_du(uint16_t N_ZC, const uint16_t *prach_root_sequence_map, uint16_t
{
return;
};
static void sig_handler(int sig_num)
{
oai_exit = 1;
}
uint16_t nr_du[838];
uint64_t downlink_frequency[MAX_NUM_CCs][4];
@@ -171,6 +171,7 @@ int main(int argc, char **argv)
printf("About to Init RU threads\n");
lock_memory_to_ram();
load_dftslib();
RC.nb_RU = 1;
RC.ru = malloc(sizeof(RC.ru));
@@ -178,24 +179,77 @@ int main(int argc, char **argv)
init_NR_RU(config_get_if(), NULL);
RU_t *ru = RC.ru[0];
ORU_t oru = {0};
oru.ru = ru;
int ret = get_oru_options(&oru);
AssertFatal(ret == 0, "Cannot configure oru, check your config file/cmdline");
ru->numerology = oru.numerology;
oru_init_frame_parms(&oru);
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nr_dump_frame_parms(fp);
init_symbol_rotation(fp);
init_timeshift_rotation(fp->ofdm_symbol_size, fp->nb_prefix_samples, fp->ofdm_offset_divisor, fp->timeshift_symbol_rotation);
ru->if_south = LOCAL_RF;
nr_phy_init_RU(oru.ru);
fill_rf_config(ru, ru->rf_config_file);
ru->N_TA_offset = set_default_nta_offset(fp->freq_range, fp->samples_per_subframe);
while (oai_exit == 0)
/* set PRACH configuration */
nfapi_nr_prach_config_t *prach_config = &ru->config.prach_config;
prach_config->prach_ConfigurationIndex.value = oru.prach_config_index;
prach_config->num_prach_fd_occasions_list[0].k1.value = oru.prach_msg1_freq;
prach_config->prach_sequence_length.value = 1;
prach_config->prach_sub_c_spacing.value = 1;
prach_config->num_prach_fd_occasions.value = 1;
reset_meas(&oru.rx_prach);
oru.prach_info = get_nr_prach_occasion_info_from_index(oru.prach_config_index, FR1, fp->frame_type);
LOG_A(PHY, "PRACH configuration index %d\n", oru.prach_config_index);
LOG_A(PHY,
"PRACH format %d start_symbol %d duration %d\n",
oru.prach_info.format,
oru.prach_info.start_symbol,
oru.prach_info.N_dur);
prepare_prach_item(&oru);
oru.fronthaul = oru_fh_init(&oru.fh_config);
AssertFatal(oru.fronthaul != NULL, "Cannot configure oru fronthaul, check your config file/cmdline");
LOG_I(PHY, "starting rfdevice\n");
ret = openair0_device_load(&ru->rfdevice, &ru->openair0_cfg);
AssertFatal(ret == 0, "RU %u: openair0_device_load() ret %d: cannot initialize rfdevice\n", ru->idx, ret);
ret = ru->rfdevice.trx_start_func(&ru->rfdevice);
AssertFatal(ret == 0, "RU %u: trx_start_func() ret %d: cannot start rfdevice\n", ru->idx, ret);
threadCreate(&oru.north_read_thread, oru_north_read_thread, (void *)&oru, "north_read_thread", -1, OAI_PRIORITY_RT_MAX);
threadCreate(&oru.south_read_thread, oru_south_read_thread, (void *)&oru, "south_read_thread", -1, OAI_PRIORITY_RT_MAX);
usleep(1000);
oru_fh_start(oru.fronthaul);
// Signal handler
signal(SIGINT, sig_handler);
while (oai_exit == 0) {
oru_fh_print_stats(oru.fronthaul);
sleep(1);
// stop threads
}
kill_NR_RU_proc(0);
pthread_join(oru.north_read_thread, NULL);
pthread_join(oru.south_read_thread, NULL);
end_configmodule(uniqCfg);
oru_fh_stop(oru.fronthaul);
if (ru->rfdevice.trx_stop_func) {
ru->rfdevice.trx_stop_func(&ru->rfdevice);
ru->rfdevice.trx_stop_func = NULL;
}
if (ru->rfdevice.trx_end_func) {
ru->rfdevice.trx_end_func(&ru->rfdevice);
ru->rfdevice.trx_end_func = NULL;
}
if (ru->ifdevice.trx_end_func) {
ru->ifdevice.trx_end_func(&ru->ifdevice);
ru->ifdevice.trx_end_func = NULL;
}
end_configmodule(uniqCfg);
logClean();
printf("Bye.\n");

View File

@@ -22,7 +22,6 @@
#include "common/utils/load_module_shlib.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include "PHY/phy_vars.h"

760
executables/nr-oru.c Normal file
View File

@@ -0,0 +1,760 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#include "common/config/config_userapi.h"
#include "common/utils/system.h"
#include "nr-oru.h"
#include "openair1/PHY/defs_nr_common.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
#include "oru_packet_processor.h"
#include <time.h>
#include "openair1/PHY/MODULATION/nr_modulation.h"
#include "openair1/SCHED_NR/sched_nr.h"
#include "openair1/PHY/MODULATION/modulation_common.h"
#include "openair2/LAYER2/NR_MAC_COMMON/nr_mac_common.h"
#define CONFIG_SECTION_ORU "ORUs.[0]"
#define CONFIG_STRING_ORU_TX_BW_LIST "tx_bw"
#define CONFIG_STRING_ORU_RX_BW_LIST "rx_bw"
#define CONFIG_STRING_ORU_CARRIER_TX_LIST "carrier_tx"
#define CONFIG_STRING_ORU_CARRIER_RX_LIST "carrier_rx"
#define CONFIG_STRING_ORU_FRAME_TYPE "frame_type"
#define CONFIG_STRING_ORU_PRACH_CONFIGID "prach_config_index"
#define CONFIG_STRING_ORU_PRACH_MSG1FREQ "prach_msg1_start"
#define CONFIG_STRING_ORU_NUMEROLOGY "mu"
#define CONFIG_STRING_ORU_TDD_PERIOD "tdd_period"
#define CONFIG_STRING_ORU_NUM_DL_SLOTS "num_dl_slots"
#define CONFIG_STRING_ORU_NUM_UL_SLOTS "num_ul_slots"
#define CONFIG_STRING_ORU_NUM_DL_SYMBOLS "num_dl_symbols"
#define CONFIG_STRING_ORU_NUM_UL_SYMBOLS "num_ul_symbols"
#define HLP_ORU_TX_BW "set the TX bandwidth list per component carrier"
#define HLP_ORU_RX_BW "set the RX bandwidth list per component carrier"
#define HLP_ORU_CARRIER_TX "set the TX carrier frequencies per component carrier"
#define HLP_ORU_CARRIER_RX "set the RX carrier frequencies per component carrier"
#define HLP_ORU_FRAMETYPE "set the Frame type TDD/FDD of all component carriers"
#define HLP_ORU_PRACH_CONFIGID "set the PRACH configuration id of all component carriers"
#define HLP_ORU_PRACH_MSG1FREQ "set the PRACH MSG1 frequency of all component carriers"
#define HLP_ORU_NUMEROLOGY "set the numerology of the RU"
#define HLP_ORU_TDD_PERIOD "set the 3GPP TDD periodificty 0-9"
#define HLP_ORU_NUM_DL_SLOTS "set the number of DL Slots in TDD"
#define HLP_ORU_NUM_UL_SLOTS "set the number of UL Slots in TDD"
#define HLP_ORU_NUM_DL_SYMBOLS "set the number of DL symbols in the mixed slot"
#define HLP_ORU_NUM_UL_SYMBOLS "set the number of UL symbols in the mixed slot"
// clang-format off
#define CMDLINE_PARAMS_DESC_ORU \
{ \
{CONFIG_STRING_ORU_TX_BW_LIST, HLP_ORU_TX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_RX_BW_LIST, HLP_ORU_RX_BW, 0, .iptr=NULL, .defintarrayval=DEFBW, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_CARRIER_TX_LIST, HLP_ORU_CARRIER_TX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_CARRIER_RX_LIST, HLP_ORU_CARRIER_RX, 0, .iptr=NULL, .defintarrayval=DEFCARRIER, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_ORU_FRAME_TYPE, HLP_ORU_FRAMETYPE, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_PRACH_CONFIGID, HLP_ORU_PRACH_CONFIGID, 0, .uptr=NULL, .defintval=152, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_PRACH_MSG1FREQ, HLP_ORU_PRACH_MSG1FREQ, 0, .uptr=NULL, .defintval=0, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUMEROLOGY, HLP_ORU_NUMEROLOGY, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_TDD_PERIOD, HLP_ORU_TDD_PERIOD, 0, .uptr=NULL, .defintval=5, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_DL_SLOTS, HLP_ORU_NUM_DL_SLOTS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_UL_SLOTS, HLP_ORU_NUM_UL_SLOTS, 0, .uptr=NULL, .defintval=1, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_DL_SYMBOLS, HLP_ORU_NUM_DL_SYMBOLS, 0, .uptr=NULL, .defintval=7, TYPE_UINT, 0}, \
{CONFIG_STRING_ORU_NUM_UL_SYMBOLS, HLP_ORU_NUM_UL_SYMBOLS, 0, .uptr=NULL, .defintval=3, TYPE_UINT, 0}, \
}
// clang-format on
#define CONFIG_SECTION_ORU_FH "ORUs.[0].fronthaul"
#define CONFIG_STRING_ORU_DPDK_DEVICES "dpdk_devices"
#define CONFIG_STRING_RX_CORE "rx_core"
#define CONFIG_STRING_EXTRA_EAL_ARGS "extra_eal_args"
#define CONFIG_STRING_DU_MAC_ADDRESSES "du_mac_addr"
#define CONFIG_STRING_MTU "mtu"
#define CONFIG_STRING_T2A_UP "T2a_up"
#define CONFIG_STRING_T2A_CP "T2a_cp"
#define CONFIG_STRING_PRACH_EAXC_OFFSET "prach_eaxc_offset"
#define HLP_DPDK_DEVICES "DPDK devices to use for the O-RU."
#define HLP_RX_CORE "The CPU core to be used to deploy dpdk RX worker for O-RU."
#define HLP_EXTRA_EAL_ARGS "Extra arguments passed to RTE_EAL_INIT."
#define HLP_DU_MAC_ADDRESSES "DU MAC addreses, used to prepare Ethernet headers."
#define HLP_MTU "MTU for RX and TX."
#define HLP_PRACH_EAXC_OFFSET "PRACH eAxC offset."
// clang-format off
#define CMDLINE_PARAMS_DESC_ORU_FH \
{ \
{CONFIG_STRING_ORU_DPDK_DEVICES, HLP_DPDK_DEVICES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
{CONFIG_STRING_RX_CORE, HLP_RX_CORE, PARAMFLAG_MANDATORY, .iptr=NULL, .defintval=-1, TYPE_INT, 0}, \
{CONFIG_STRING_EXTRA_EAL_ARGS, HLP_EXTRA_EAL_ARGS, 0, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
{CONFIG_STRING_DU_MAC_ADDRESSES, HLP_DU_MAC_ADDRESSES, PARAMFLAG_MANDATORY, .strptr=NULL, .defstrval=NULL, TYPE_STRINGLIST, 0}, \
{CONFIG_STRING_MTU, HLP_MTU, 0, .iptr=NULL, .defintval=9600, TYPE_INT, 0}, \
{CONFIG_STRING_T2A_UP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_T2A_CP, "", 0, .iptr=NULL, .defintarrayval=NULL, TYPE_INTARRAY, 0}, \
{CONFIG_STRING_PRACH_EAXC_OFFSET, HLP_PRACH_EAXC_OFFSET, 0, .iptr=NULL, .defintval=0, TYPE_INT, 0} \
}
// clang-format on
extern void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case);
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols);
void prepare_prach_item(ORU_t *oru)
{
AssertFatal(oru->ru != NULL, "ORU not configured\n");
AssertFatal(oru->ru->nr_frame_parms != NULL, "ORU not configured\n");
NR_DL_FRAME_PARMS *fp = oru->ru->nr_frame_parms;
RU_t *ru = oru->ru;
prach_item_t *prach_item = &oru->prach_item;
prach_item->num_slots = oru->prach_info.format < 4 ? get_long_prach_dur(oru->prach_info.format, fp->numerology_index) : 1;
prach_item->msg1_frequencystart = oru->prach_msg1_freq;
prach_item->mu = fp->numerology_index;
nfapi_nr_config_request_scf_t *cfg = &ru->config;
prach_item->prach_sequence_length = cfg->prach_config.prach_sequence_length.value;
prach_item->restricted_set = 0;
prach_item->numerology_index = fp->numerology_index;
prach_item->nb_rx = ru->nb_rx;
prach_item->rx_prach = &oru->rx_prach;
// Fill PRACH PDU
nfapi_nr_prach_pdu_t *prach_pdu = &prach_item->pdu;
prach_pdu->prach_start_symbol = oru->prach_info.start_symbol;
prach_pdu->num_prach_ocas = 1; // TODO: Hardcoded.
uint16_t format0 = oru->prach_info.format & 0xff;
uint16_t format1 = (oru->prach_info.format >> 8) & 0xff;
if (format1 != 0xff) {
switch (format0) {
case 0xa1:
prach_pdu->prach_format = 11;
break;
case 0xa2:
prach_pdu->prach_format = 12;
break;
case 0xa3:
prach_pdu->prach_format = 13;
break;
default:
AssertFatal(1 == 0, "Only formats A1/B1 A2/B2 A3/B3 are valid for dual format");
}
} else {
switch (format0) {
case 0:
prach_pdu->prach_format = 0;
break;
case 1:
prach_pdu->prach_format = 1;
break;
case 2:
prach_pdu->prach_format = 2;
break;
case 3:
prach_pdu->prach_format = 3;
break;
case 0xa1:
prach_pdu->prach_format = 4;
break;
case 0xa2:
prach_pdu->prach_format = 5;
break;
case 0xa3:
prach_pdu->prach_format = 6;
break;
case 0xb1:
prach_pdu->prach_format = 7;
break;
case 0xb4:
prach_pdu->prach_format = 8;
break;
case 0xc0:
prach_pdu->prach_format = 9;
break;
case 0xc2:
prach_pdu->prach_format = 10;
break;
default:
AssertFatal(1 == 0, "Invalid PRACH format");
}
}
}
int get_oru_options(ORU_t *oru)
{
int DEFBW[] = {273};
int DEFCARRIER[] = {3430560};
paramdef_t param[] = CMDLINE_PARAMS_DESC_ORU;
int nump = sizeofArray(param);
int ret = config_get(config_get_if(), param, nump, CONFIG_SECTION_ORU);
if (ret <= 0) {
LOG_E(NR_PHY, "problem reading section \"%s\"\n", CONFIG_SECTION_ORU);
return -1;
}
for (int i = 0; i < oru->ru->num_bands; i++) {
oru->bw_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_TX_BW_LIST)->iptr[i];
oru->bw_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_RX_BW_LIST)->iptr[i];
oru->carrier_freq_tx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_TX_LIST)->iptr[i];
oru->carrier_freq_rx[i] = gpd(param, nump, CONFIG_STRING_ORU_CARRIER_RX_LIST)->iptr[i];
}
oru->frame_type = *gpd(param, nump, CONFIG_STRING_ORU_FRAME_TYPE)->iptr;
oru->prach_config_index = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_CONFIGID)->iptr;
oru->prach_msg1_freq = *gpd(param, nump, CONFIG_STRING_ORU_PRACH_MSG1FREQ)->iptr;
oru->numerology = *gpd(param, nump, CONFIG_STRING_ORU_NUMEROLOGY)->iptr;
oru->tdd_period = *gpd(param, nump, CONFIG_STRING_ORU_TDD_PERIOD)->iptr;
oru->num_DL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SLOTS)->iptr;
oru->num_UL_slots = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SLOTS)->iptr;
oru->num_DL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_DL_SYMBOLS)->iptr;
oru->num_UL_symbols = *gpd(param, nump, CONFIG_STRING_ORU_NUM_UL_SYMBOLS)->iptr;
paramdef_t fh_param[] = CMDLINE_PARAMS_DESC_ORU_FH;
nump = sizeofArray(fh_param);
oru_fh_config_t *fh_cfg = &oru->fh_config;
ret = config_get(config_get_if(), fh_param, nump, CONFIG_SECTION_ORU_FH);
if (ret <= 0) {
printf("problem reading section \"%s\"\n", CONFIG_SECTION_ORU_FH);
return -1;
}
oru_fh_dpdk_config_t *dpdk_conf = &fh_cfg->dpdk_conf;
int num_dpdk_devices = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->numelt;
dpdk_conf->num_dpdk_devices = num_dpdk_devices;
AssertFatal(num_dpdk_devices > 0 && num_dpdk_devices <= 2,
"Invalid number of DPDK devices (%d). Configure 1 or 2 devices\n",
num_dpdk_devices);
for (int i = 0; i < num_dpdk_devices; i++) {
dpdk_conf->dpdk_devices[i] = gpd(fh_param, nump, CONFIG_STRING_ORU_DPDK_DEVICES)->strlistptr[i];
}
dpdk_conf->extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->strlistptr;
dpdk_conf->num_extra_eal_args = gpd(fh_param, nump, CONFIG_STRING_EXTRA_EAL_ARGS)->numelt;
fh_cfg->num_du_mac_addrs = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->numelt;
for (int i = 0; i < fh_cfg->num_du_mac_addrs; i++) {
fh_cfg->du_mac_addrs[i] = gpd(fh_param, nump, CONFIG_STRING_DU_MAC_ADDRESSES)->strlistptr[i];
AssertFatal(strlen(fh_cfg->du_mac_addrs[i]) == 17, "Invalid MAC address\n");
}
fh_cfg->enable_compression = false;
fh_cfg->rx_core = *gpd(fh_param, nump, CONFIG_STRING_RX_CORE)->iptr;
fh_cfg->mtu = *gpd(fh_param, nump, CONFIG_STRING_MTU)->iptr;
fh_cfg->num_prbs = oru->bw_tx[0];
fh_cfg->numerology = oru->numerology;
fh_cfg->prach_eaxc_offset = *gpd(fh_param, nump, CONFIG_STRING_PRACH_EAXC_OFFSET)->iptr;
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_UP);
fh_cfg->T2a_up_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[0];
fh_cfg->T2a_up_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_UP)->iptr[1];
AssertFatal(fh_cfg->T2a_up_min_uS <= fh_cfg->T2a_up_max_uS,
"T2a max (%d) has to be greater than T2a min (%d)\n",
fh_cfg->T2a_up_max_uS,
fh_cfg->T2a_up_min_uS);
AssertFatal(gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->numelt == 2, "Two parameters required for %s\n", CONFIG_STRING_T2A_CP);
fh_cfg->T2a_cp_min_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[0];
fh_cfg->T2a_cp_max_uS = gpd(fh_param, nump, CONFIG_STRING_T2A_CP)->iptr[1];
AssertFatal(fh_cfg->T2a_cp_min_uS <= fh_cfg->T2a_cp_max_uS,
"T2a max (%d) has to be greater than T2a min (%d)\n",
fh_cfg->T2a_cp_max_uS,
fh_cfg->T2a_cp_min_uS);
oru_fh_tdd_pattern_t *tdd_pattern = &fh_cfg->tdd_pattern;
tdd_pattern->num_dl_slots = oru->num_DL_slots;
tdd_pattern->num_ul_slots = oru->num_UL_slots;
tdd_pattern->num_dl_symbols = oru->num_DL_symbols;
tdd_pattern->num_ul_symbols = oru->num_UL_symbols;
int num_slots_frame = (1 << oru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
int num_period_frame = get_nb_periods_per_frame(oru->tdd_period);
int num_slots_period = num_slots_frame / num_period_frame;
tdd_pattern->tdd_pattern_length_slots = num_slots_period;
return 0;
}
void oru_init_frame_parms(ORU_t *oru)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
fp->frame_type = oru->frame_type;
ru->config.cell_config.frame_duplex_type.value = oru->frame_type;
ru->config.cell_config.frame_duplex_type.tl.tag = 0x100D;
fp->N_RB_DL = oru->bw_tx[0];
ru->config.ssb_config.scs_common.value = ru->numerology;
ru->config.carrier_config.dl_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_tx[0];
fp->N_RB_UL = oru->bw_rx[0];
ru->config.carrier_config.ul_grid_size[ru->config.ssb_config.scs_common.value].value = oru->bw_rx[0];
fp->numerology_index = ru->numerology;
LOG_I(NR_PHY,
"Set RU frame type to %s, N_RB_DL %d, N_RB_UL %d, mu %d\n",
oru->frame_type == TDD ? "TDD" : "FDD",
oru->bw_tx[0],
oru->bw_rx[0],
ru->numerology);
set_scs_parameters(fp, fp->numerology_index, oru->bw_tx[0], 0);
fp->slots_per_frame = 10 * fp->slots_per_subframe;
fp->nb_antennas_rx = ru->nb_rx;
fp->nb_antennas_tx = ru->nb_tx;
fp->symbols_per_slot = 14;
fp->samples_per_subframe_wCP = fp->ofdm_symbol_size * fp->symbols_per_slot * fp->slots_per_subframe;
fp->samples_per_frame_wCP = 10 * fp->samples_per_subframe_wCP;
fp->samples_per_slot_wCP = fp->symbols_per_slot * fp->ofdm_symbol_size;
fp->samples_per_slotN0 = (fp->nb_prefix_samples + fp->ofdm_symbol_size) * fp->symbols_per_slot;
fp->samples_per_slot0 =
fp->nb_prefix_samples0 + ((fp->symbols_per_slot - 1) * fp->nb_prefix_samples) + (fp->symbols_per_slot * fp->ofdm_symbol_size);
fp->samples_per_subframe = (fp->nb_prefix_samples0 + fp->ofdm_symbol_size) * 2
+ (fp->nb_prefix_samples + fp->ofdm_symbol_size) * (fp->symbols_per_slot * fp->slots_per_subframe - 2);
fp->samples_per_frame = 10 * fp->samples_per_subframe;
fp->freq_range = (oru->carrier_freq_tx[0] < 6e6) ? FR1 : FR2;
fp->dl_CarrierFreq = (double)oru->carrier_freq_tx[0] * 1000;
fp->ul_CarrierFreq = (double)oru->carrier_freq_rx[0] * 1000;
fp->Ncp = NORMAL;
fp->ofdm_offset_divisor = 8;
// Split 7.2 parameters
ru->config.prach_config.num_prach_fd_occasions.value = 1;
ru->config.prach_config.prach_ConfigurationIndex.value = oru->prach_config_index;
ru->config.prach_config.prach_ConfigurationIndex.tl.tag = 0x1029;
ru->config.prach_config.num_prach_fd_occasions_list = malloc(sizeof(*ru->config.prach_config.num_prach_fd_occasions_list));
ru->config.prach_config.num_prach_fd_occasions_list[0].k1.value = oru->prach_msg1_freq;
if (ru->config.cell_config.frame_duplex_type.value == 1 /* TDD */) {
ru->config.tdd_table.tdd_period.value = oru->tdd_period;
ru->config.tdd_table.tdd_period.tl.tag = 0x1026;
int numb_slots_frame = (1 << ru->numerology) * NR_NUMBER_OF_SUBFRAMES_PER_FRAME;
int numb_period_frame = get_nb_periods_per_frame(oru->tdd_period);
int numb_slots_period = numb_slots_frame / numb_period_frame;
ru->config.tdd_table.max_tdd_periodicity_list =
malloc(sizeof(*ru->config.tdd_table.max_tdd_periodicity_list) * (numb_slots_frame));
for (int n = 0; n < numb_slots_frame; n++) {
int s = 0;
int p = n % numb_slots_period;
nfapi_nr_max_tdd_periodicity_t *periodicity = &ru->config.tdd_table.max_tdd_periodicity_list[n];
periodicity->max_num_of_symbol_per_slot_list =
malloc(sizeof(*periodicity->max_num_of_symbol_per_slot_list) * NR_SYMBOLS_PER_SLOT);
nfapi_nr_max_num_of_symbol_per_slot_t *symbol_list = periodicity->max_num_of_symbol_per_slot_list;
if (p < oru->num_DL_slots) {
for (s = 0; s < 14; s++)
symbol_list[s].slot_config.value = 0;
} else if (p == oru->num_DL_slots) {
for (s = 0; s < oru->num_DL_symbols; s++)
symbol_list[s].slot_config.value = 0;
for (; s < NR_SYMBOLS_PER_SLOT - oru->num_UL_symbols; s++)
symbol_list[s].slot_config.value = 2;
for (; s < NR_SYMBOLS_PER_SLOT; s++)
symbol_list[s].slot_config.value = 1;
} else {
for (s = 0; s < NR_SYMBOLS_PER_SLOT; s++)
symbol_list[s].slot_config.value = 1;
}
}
}
}
void fft_and_cp_insertion(NR_DL_FRAME_PARMS *fp, c16_t *txdataF, c16_t *txdata, int slot, int symbol)
{
int nb_prefix_samples = fp->nb_prefix_samples;
if (fp->Ncp != 1) {
if (fp->numerology_index != 0) {
if (!(slot % (fp->slots_per_subframe / 2)) && (symbol == 0)) {
nb_prefix_samples = fp->nb_prefix_samples0;
}
} else {
if (symbol % 0x7 == 0) {
nb_prefix_samples = fp->nb_prefix_samples0;
}
}
}
PHY_ofdm_mod((int *)txdataF, (int *)txdata, fp->ofdm_symbol_size, 1, nb_prefix_samples, CYCLIC_PREFIX);
}
static void dl_symbol_process(RU_t *ru, int frame, int slot, int symbol, c16_t **txDataF, int64_t timestamp)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
uint32_t slot_offset = get_samples_slot_timestamp(fp, slot);
uint32_t symbol_offset = get_samples_symbol_timestamp(fp, slot, symbol);
__attribute__((aligned(64))) c16_t txdataF_shifted[fp->ofdm_symbol_size];
memset(txdataF_shifted, 0, sizeof(txdataF_shifted));
c16_t *rotation = fp->symbol_rotation[0] + (slot % fp->slots_per_subframe) * fp->symbols_per_slot + symbol;
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
// Phase compensation
rotate_cpx_vector(txDataF[aatx], *rotation, txDataF[aatx], fp->N_RB_DL * NR_NB_SC_PER_RB, 15);
// FFT Shift
const int num_samp_half = fp->N_RB_DL * NR_NB_SC_PER_RB / 2;
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
memcpy(txdataF_shifted + first_carrier_offset, txDataF[aatx], num_samp_half * sizeof(c16_t));
memcpy(txdataF_shifted, txDataF[aatx] + num_samp_half, num_samp_half * sizeof(c16_t));
fft_and_cp_insertion(ru->nr_frame_parms,
txdataF_shifted,
(c16_t *)&ru->common.txdata[aatx][slot_offset + symbol_offset],
slot,
symbol);
}
tx_rf_symbols(ru, frame, slot, timestamp, symbol, 1);
}
void *oru_north_read_thread(void *arg)
{
ORU_t *oru = (ORU_t *)arg;
RU_t *ru = (RU_t *)oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
__attribute__((aligned(64))) c16_t txDataF[ru->nb_tx][fp->N_RB_DL * NR_NB_SC_PER_RB];
memset(txDataF, 0, sizeof(txDataF));
c16_t *txDataF_ptr[ru->nb_tx];
for (int aatx = 0; aatx < ru->nb_tx; aatx++) {
txDataF_ptr[aatx] = txDataF[aatx];
}
uint32_t start_frame, start_slot;
uint64_t start_hyper_frame;
struct timespec utc_anchor_point;
oru_fh_get_utc_anchor_point(oru->fronthaul, &start_hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
// subtract the start_frame and start_slot from the timestamp simplify calculation below.
start_timestamp -= (start_frame * fp->samples_per_frame + get_samples_slot_timestamp(fp, start_slot));
// Now start_timestamp points to the start sample of the frame 0 slot 0 symbol 0 of hyperframe 0
LOG_A(PHY, "DL thread started: start_timestamp %ld, start_frame %d, start_slot %d\n", start_timestamp, start_frame, start_slot);
while (!oai_exit) {
int frame = -1, slot = -1, symbol = -1;
uint64_t hyper_frame;
int ret = oru_fh_tx_read_symbol(oru->fronthaul, (uint32_t **)txDataF_ptr, ru->nb_tx, &hyper_frame, &frame, &slot, &symbol);
if (ret != 0) {
LOG_E(PHY, "[RU_thread] read data error: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
continue;
}
if (start_hyper_frame > hyper_frame) {
continue;
}
uint64_t num_frames = (hyper_frame - start_hyper_frame) * 1024 + frame;
int64_t timestamp = start_timestamp + num_frames * fp->samples_per_frame + get_samples_slot_timestamp(fp, slot)
+ get_samples_symbol_timestamp(fp, slot, symbol);
if (timestamp < 0) {
continue;
}
dl_symbol_process(ru, frame, slot, symbol, txDataF_ptr, timestamp);
if (frame % 256 == 0 && slot == 0 && symbol == 0) {
LOG_I(PHY, "[RU_thread] read data: frame %d, slot %d, symbol %d\n", frame, slot, symbol);
}
}
return NULL;
}
// Returns PRACH symbol that was received in current frame, slot and symbol.
// If no PRACH symbol was received, returns -1
int get_prach_symbol(ORU_t *oru, int frame, int slot, int symbol, int numerology)
{
uint16_t RA_sfn_index;
AssertFatal(oru->ru->nr_frame_parms->frame_type == TDD, "Only supports TDD\n");
if (get_nr_prach_sched_from_info(oru->prach_info, oru->prach_config_index, frame, slot, numerology, FR1, &RA_sfn_index, true)) {
int format = oru->prach_item.pdu.prach_format;
int start_symbol = oru->prach_item.pdu.prach_start_symbol;
symbol -= start_symbol;
// TODO: Support more PRACH formats
AssertFatal(format == 8, "only support format B4\n");
// TODO: This is not exactly the case but it is correct
if (symbol >= 0 && symbol < 12) {
return symbol;
}
}
return -1;
}
void receive_prach(ORU_t *oru, int frame, int slot, int symbol, int prach_symbol)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
oru->prach_item.frame = frame;
oru->prach_item.slot = slot;
c16_t rxdataF[ru->nb_rx][NR_PRACH_SEQ_LEN_L];
memset(rxdataF, 0, sizeof(rxdataF));
rx_nr_prach_ru_rep(&oru->prach_item,
ru->common.rxdata,
fp,
ru->N_TA_offset,
prach_symbol,
0, // prachOccasion
rxdataF);
c16_t *rxdataF_ptr[ru->nb_rx];
for (int aarx = 0; aarx < ru->nb_rx; aarx++) {
rxdataF_ptr[aarx] = rxdataF[aarx];
}
oru_fh_rx_send_prach(oru->fronthaul, (uint32_t **)rxdataF_ptr, ru->nb_rx, frame, slot, symbol);
}
#define MAX_PENDING_UL_JOBS 64
typedef struct {
ul_job_t job;
bool active;
int symbols_sent;
} ul_pending_t;
static void receive_pusch(ORU_t *oru, int frame, int slot, int symbol, ul_job_t *job)
{
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
int aarx = job->antenna_id;
if (aarx < 0 || aarx >= fp->nb_antennas_rx) {
LOG_W(PHY, "[ORU south] receive_pusch: invalid antenna_id %d\n", aarx);
return;
}
// CP removal + FFT → full ofdm_symbol_size frequency-domain output
c16_t rxdataF_fft[fp->ofdm_symbol_size] __attribute__((aligned(32)));
nr_symbol_fep_ul(fp, (c16_t *)ru->common.rxdata[aarx], rxdataF_fft, symbol, slot, ru->N_TA_offset);
// Phase decompensation (conjugate rotation for UL)
apply_nr_rotation_symbol_RX(fp->symbols_per_slot,
fp->slots_per_subframe,
fp->timeshift_symbol_rotation,
fp->first_carrier_offset,
rxdataF_fft,
fp->symbol_rotation[link_type_ul],
fp->N_RB_UL,
slot,
symbol);
// Inverse FFT shift: split format → contiguous PRB format sent to DU.
// DL TX shift: contiguous[0..N/2-1] → FFT_input[first_carrier_offset..] (negative freqs)
// contiguous[N/2..N-1] → FFT_input[0..N/2-1] (positive freqs)
// UL RX inverse: FFT_out[first_carrier_offset..] → contiguous[0..N/2-1]
// FFT_out[0..N/2-1] → contiguous[N/2..N-1]
const int num_samp_half = fp->N_RB_UL * NR_NB_SC_PER_RB / 2;
const int first_carrier_offset = fp->ofdm_symbol_size - num_samp_half;
c16_t rxdataF[fp->N_RB_UL * NR_NB_SC_PER_RB];
memcpy(rxdataF, rxdataF_fft + first_carrier_offset, num_samp_half * sizeof(c16_t));
memcpy(rxdataF + num_samp_half, rxdataF_fft, num_samp_half * sizeof(c16_t));
oru_fh_rx_send_pusch(oru->fronthaul, (uint32_t *)rxdataF, symbol, job);
}
#define UL_WORK_QUEUE_DEPTH 128
typedef struct {
ORU_t *oru;
int frame;
int slot;
int symbol;
ul_job_t job;
} ul_work_item_t;
typedef struct {
ul_work_item_t ring[UL_WORK_QUEUE_DEPTH];
int head;
int tail;
int count;
pthread_mutex_t lock;
pthread_cond_t work_available;
pthread_cond_t space_available;
bool running;
} ul_work_queue_t;
static void *ul_worker_thread(void *arg)
{
ul_work_queue_t *q = arg;
while (1) {
pthread_mutex_lock(&q->lock);
while (q->count == 0 && q->running)
pthread_cond_wait(&q->work_available, &q->lock);
if (!q->running && q->count == 0) {
pthread_mutex_unlock(&q->lock);
break;
}
ul_work_item_t item = q->ring[q->head];
q->head = (q->head + 1) % UL_WORK_QUEUE_DEPTH;
q->count--;
pthread_cond_signal(&q->space_available);
pthread_mutex_unlock(&q->lock);
receive_pusch(item.oru, item.frame, item.slot, item.symbol, &item.job);
}
return NULL;
}
static void dispatch_ul_work(ul_work_queue_t *q, ORU_t *oru, int frame, int slot, int symbol, const ul_job_t *job)
{
pthread_mutex_lock(&q->lock);
while (q->count == UL_WORK_QUEUE_DEPTH)
pthread_cond_wait(&q->space_available, &q->lock);
q->ring[q->tail] = (ul_work_item_t){.oru = oru, .frame = frame, .slot = slot, .symbol = symbol, .job = *job};
q->tail = (q->tail + 1) % UL_WORK_QUEUE_DEPTH;
q->count++;
pthread_cond_signal(&q->work_available);
pthread_mutex_unlock(&q->lock);
}
void *oru_south_read_thread(void *arg)
{
ORU_t *oru = arg;
RU_t *ru = oru->ru;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
struct timespec utc_anchor_point;
AssertFatal(ru->rfdevice.get_timestamp != NULL, "rfdevice has no capability to translate UTC timestamp to sample index\n");
uint32_t start_frame, start_slot;
uint64_t hyper_frame;
oru_fh_get_utc_anchor_point(oru->fronthaul, &hyper_frame, &start_frame, &start_slot, &utc_anchor_point);
int64_t start_timestamp = ru->rfdevice.get_timestamp(&ru->rfdevice, &utc_anchor_point);
const int num_samples = 3000;
c16_t throwaway_samples[ru->nb_rx][num_samples];
void *rxp[ru->nb_rx];
for (int i = 0; i < ru->nb_rx; i++)
rxp[i] = throwaway_samples[i];
openair0_timestamp_t timestamp;
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, rxp, num_samples, ru->nb_rx);
AssertFatal(num_samples_read == num_samples, "Unexpected number of samples received\n");
openair0_timestamp_t next_timestamp = timestamp + num_samples_read;
while (next_timestamp > start_timestamp) {
start_timestamp += get_samples_slot_duration(fp, start_slot, 1);
start_slot++;
if (start_slot == fp->slots_per_frame) {
start_slot = 0;
start_frame++;
if (start_frame == 1024) {
start_frame = 0;
}
}
}
while (next_timestamp < start_timestamp) {
int num_samples_to_read = min(num_samples, (int)(start_timestamp - next_timestamp));
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, rxp, num_samples_to_read, ru->nb_rx);
AssertFatal(num_samples_read == num_samples_to_read, "Unexpected number of samples received\n");
next_timestamp += num_samples_read;
}
AssertFatal(next_timestamp == start_timestamp, "O-RU South thread could not sync to UTC anchor point\n");
int slot = start_slot;
int frame = start_frame;
// Worker pool: one thread per RX antenna so all antennas in a symbol process in parallel.
const int num_workers = ru->nb_rx;
pthread_t workers[num_workers];
ul_work_queue_t work_queue = {
.lock = PTHREAD_MUTEX_INITIALIZER,
.work_available = PTHREAD_COND_INITIALIZER,
.space_available = PTHREAD_COND_INITIALIZER,
.running = true,
};
for (int i = 0; i < num_workers; i++) {
char name[32];
snprintf(name, sizeof(name), "ul_worker_%d", i);
threadCreate(&workers[i], ul_worker_thread, &work_queue, name, -1, OAI_PRIORITY_RT_MAX);
}
ul_pending_t pending_ul[MAX_PENDING_UL_JOBS] = {0};
while (!oai_exit) {
int rx_slot_type = nr_slot_select(&ru->config, frame, slot);
for (int symbol = 0; symbol < 14; symbol++) {
int samples_to_read = get_samples_symbol_duration(fp, slot, symbol, 1);
size_t offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, symbol);
c16_t *rxp[fp->nb_antennas_rx];
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
rxp[aarx] = (c16_t *)&ru->common.rxdata[aarx][offset];
}
openair0_timestamp_t timestamp;
int num_samples_read = ru->rfdevice.trx_read_func(&ru->rfdevice, &timestamp, (void **)rxp, samples_to_read, ru->nb_rx);
AssertFatal(num_samples_read == samples_to_read, "Unexpected number of samples received\n");
LOG_D(PHY,
"[ORU south] read data: frame %d, slot %d, symbol %d, timestamp %ld num_symbols %d, samples %d\n",
frame,
slot,
symbol,
timestamp,
1,
num_samples_read);
// Drain the UL job ring
ul_job_t new_job;
while (oru_fh_poll_ul_job(oru->fronthaul, &new_job) == 0) {
bool added = false;
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
if (!pending_ul[i].active) {
pending_ul[i] = (ul_pending_t){.job = new_job, .active = true, .symbols_sent = 0};
added = true;
break;
}
}
if (!added)
LOG_W(PHY, "[ORU south] UL pending queue full, dropping job frame=%d slot=%d sym=%d\n",
new_job.frame, new_job.slot_in_frame, new_job.symbol);
}
if (rx_slot_type == NR_UPLINK_SLOT || rx_slot_type == NR_MIXED_SLOT) {
// Process pending jobs whose next symbol matches the current one
for (int i = 0; i < MAX_PENDING_UL_JOBS; i++) {
if (!pending_ul[i].active)
continue;
ul_job_t *j = &pending_ul[i].job;
// Skip jobs scheduled for a future slot or frame, and drop jobs in the past
int slots_per_frame = fp->slots_per_frame;
int total_slots = 1024 * slots_per_frame;
int diff_slots = (j->frame * slots_per_frame + j->slot_in_frame) - (frame * slots_per_frame + slot);
if (diff_slots < -total_slots / 2) {
diff_slots += total_slots;
} else if (diff_slots > total_slots / 2) {
diff_slots -= total_slots;
}
if (diff_slots > 0) {
// Future slot: skip
continue;
}
if (diff_slots < 0) {
// Past slot: drop
LOG_W(PHY, "[ORU south] missed UL slot %d.%d (now %d.%d), dropping job ant=%d\n",
j->frame, j->slot_in_frame, frame, slot, j->antenna_id);
pending_ul[i].active = false;
continue;
}
// Same slot: check symbol
int expected_symbol = j->symbol + pending_ul[i].symbols_sent;
if (expected_symbol < symbol) {
LOG_W(PHY, "[ORU south] missed UL symbol %d (now %d), dropping job ant=%d\n",
expected_symbol, symbol, j->antenna_id);
pending_ul[i].active = false;
} else if (expected_symbol == symbol) {
dispatch_ul_work(&work_queue, oru, frame, slot, symbol, j);
if (++pending_ul[i].symbols_sent == j->num_symbols)
pending_ul[i].active = false;
}
// expected_symbol > symbol: job spans multiple symbols, revisit next iteration
}
}
int prach_symbol = get_prach_symbol(oru, frame, slot, symbol, ru->numerology);
if (prach_symbol != -1)
receive_prach(oru, frame, slot, symbol, prach_symbol);
}
slot++;
if (slot == fp->slots_per_frame) {
slot = 0;
frame++;
if (frame == 1024) {
frame = 0;
}
}
}
pthread_mutex_lock(&work_queue.lock);
work_queue.running = false;
pthread_cond_broadcast(&work_queue.work_available);
pthread_mutex_unlock(&work_queue.lock);
for (int i = 0; i < num_workers; i++)
pthread_join(workers[i], NULL);
pthread_cond_destroy(&work_queue.work_available);
pthread_cond_destroy(&work_queue.space_available);
pthread_mutex_destroy(&work_queue.lock);
return NULL;
}

59
executables/nr-oru.h Normal file
View File

@@ -0,0 +1,59 @@
/*
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#ifndef __NR_ORU_H__
#define __NR_ORU_H__
#include "openair1/PHY/defs_RU.h"
#include <pthread.h>
#include "oru_fh.h"
#include "openair2/LAYER2/NR_MAC_COMMON/nr_prach_config.h"
#include "openair1/PHY/defs_gNB.h"
typedef struct {
RU_t *ru;
/// tx carrier
uint64_t carrier_freq_tx[MAX_BANDS_PER_RRU];
/// rx carrier
uint64_t carrier_freq_rx[MAX_BANDS_PER_RRU];
/// tx BW in PRBs
int bw_tx[MAX_BANDS_PER_RRU];
/// rx BW in PRBs
int bw_rx[MAX_BANDS_PER_RRU];
/// 3GPP FRAME Type FDD/TDD
int frame_type;
/// 3GPP PRACH configuration index
int prach_config_index;
/// 3GPP MSG1 Start frequency
int prach_msg1_freq;
/// 3GPP TDD periodicity (0.5 ms, 1 0.625ms, 2 1ms, 3 1.25ms, 4 2ms,5 2.5ms, 6 5ms, 7 10ms, 8 3ms, 9 4ms
int tdd_period;
/// number of DL slots
int num_DL_slots;
/// number of UL slots
int num_UL_slots;
/// number of DL symbols
int num_DL_symbols;
/// number of UL symbols
int num_UL_symbols;
int numerology;
pthread_t north_read_thread;
pthread_t south_read_thread;
oru_fh_config_t fh_config;
void *fronthaul;
// PRACH related
nr_prach_info_t prach_info;
time_stats_t rx_prach;
time_stats_t rx;
prach_item_t prach_item;
} ORU_t;
int get_oru_options(ORU_t *oru);
void oru_init_frame_parms(ORU_t *oru);
void *oru_north_read_thread(void *arg);
void *oru_south_read_thread(void *arg);
void prepare_prach_item(ORU_t *oru);
#endif

View File

@@ -18,11 +18,8 @@
#include "common/utils/fsn.h"
#include "common/ran_context.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/ethernet_lib.h"
#include "PHY/if4_tools.h"
#include "PHY/defs_nr_common.h"
#include "PHY/phy_extern.h"
#include "PHY/NR_TRANSPORT/nr_transport_proto.h"
@@ -76,15 +73,6 @@ void fh_if5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
ru->ifdevice.trx_write_func2(&ru->ifdevice, timestamp, buffs, 0, get_samples_per_slot(slot, ru->nr_frame_parms), 0, ru->nb_tx);
}
// southbound IF4p5 fronthaul
void fh_if4p5_south_out(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
LOG_D(PHY,"Sending IF4p5 for frame %d subframe %d\n",ru->proc.frame_tx,ru->proc.tti_tx);
if ((nr_slot_select(&ru->config, ru->proc.frame_tx, ru->proc.tti_tx) & NR_DOWNLINK_SLOT) > 0)
send_IF4p5(ru,frame, slot, IF4p5_PDLFFT);
}
/*************************************************************/
/* Input Fronthaul from south RCC/RAU */
@@ -148,196 +136,6 @@ void fh_if5_south_in(RU_t *ru, int *frame, int *tti)
rxmeas.tv_nsec);
}
// Synchronous if4p5 from south
void fh_if4p5_south_in(RU_t *ru,
int *frame,
int *slot) {
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
RU_proc_t *proc = &ru->proc;
int f,sl;
uint16_t packet_type;
uint32_t symbol_number=0;
uint32_t symbol_mask_full=0;
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
recv_IF4p5(ru, &f, &sl, &packet_type, &symbol_number);
if (packet_type == IF4p5_PULFFT) proc->symbol_mask[sl] = proc->symbol_mask[sl] | (1<<symbol_number);
else if (packet_type == IF4p5_PULTICK) {
if ((proc->first_rx == 0) && (f != *frame))
LOG_E(PHY, "rx_fh_if4p5: PULTICK received frame %d != expected %d\n", f, *frame);
if ((proc->first_rx == 0) && (sl != *slot))
LOG_E(PHY, "rx_fh_if4p5: PULTICK received subframe %d != expected %d (first_rx %d)\n", sl, *slot, proc->first_rx);
break;
} else if (packet_type == IF4p5_PRACH) {
// nothing in RU for RAU
}
LOG_D(PHY,"rx_fh_if4p5: subframe %d symbol mask %x\n",*slot,proc->symbol_mask[sl]);
} while(proc->symbol_mask[sl] != symbol_mask_full);
//caculate timestamp_rx, timestamp_tx based on frame and subframe
proc->tti_rx = sl;
proc->frame_rx = f;
proc->timestamp_rx = (proc->frame_rx * fp->samples_per_subframe * 10) + get_samples_slot_timestamp(fp, proc->tti_rx);
// proc->timestamp_tx = proc->timestamp_rx + (4*fp->samples_per_subframe);
proc->tti_tx = (sl+ru->sl_ahead)%fp->slots_per_frame;
proc->frame_tx = (sl > (fp->slots_per_frame - 1 - (ru->sl_ahead))) ? (f + 1) & 1023 : f;
if (proc->first_rx == 0) {
if (proc->tti_rx != *slot) {
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->tti_rx %d, subframe %d)\n",proc->tti_rx,*slot);
exit_fun("Exiting");
}
if (proc->frame_rx != *frame) {
LOG_E(PHY,"Received Timestamp (IF4p5) doesn't correspond to the time we think it is (proc->frame_rx %d frame %d)\n",proc->frame_rx,*frame);
exit_fun("Exiting");
}
} else {
proc->first_rx = 0;
*frame = proc->frame_rx;
*slot = proc->tti_rx;
}
proc->symbol_mask[proc->tti_rx] = 0;
LOG_D(PHY,"RU %d: fh_if4p5_south_in sleeping ...\n",ru->idx);
}
// asynchronous inbound if4p5 fronthaul from south
void fh_if4p5_south_asynch_in(RU_t *ru,int *frame,int *slot) {
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
RU_proc_t *proc = &ru->proc;
uint16_t packet_type;
uint32_t symbol_number = 0;
uint32_t symbol_mask = (1 << fp->symbols_per_slot) - 1;
uint32_t prach_rx = 0;
do { // Blocking, we need a timeout on this !!!!!!!!!!!!!!!!!!!!!!!
recv_IF4p5(ru, &proc->frame_rx, &proc->tti_rx, &packet_type, &symbol_number);
if (proc->first_rx != 0) {
*frame = proc->frame_rx;
*slot = proc->tti_rx;
proc->first_rx = 0;
} else {
if (proc->frame_rx != *frame) {
LOG_E(PHY,"frame_rx %d is not what we expect %d\n",proc->frame_rx,*frame);
exit_fun("Exiting");
}
if (proc->tti_rx != *slot) {
LOG_E(PHY,"tti_rx %d is not what we expect %d\n",proc->tti_rx,*slot);
exit_fun("Exiting");
}
}
if (packet_type == IF4p5_PULFFT)
symbol_mask &= ~(1 << symbol_number);
else if (packet_type == IF4p5_PRACH)
prach_rx &= ~0x1;
} while (symbol_mask > 0 || prach_rx > 0); // haven't received all PUSCH symbols and PRACH information
}
/*************************************************************/
/* Input Fronthaul from North RRU */
// RRU IF4p5 TX fronthaul receiver. Assumes an if_device on input and if or rf device on output
// receives one subframe's worth of IF4p5 OFDM symbols and OFDM modulates
void fh_if4p5_north_in(RU_t *ru,int *frame,int *slot)
{
uint32_t symbol_number=0;
uint32_t symbol_mask, symbol_mask_full;
uint16_t packet_type;
/// **** incoming IF4p5 from remote RCC/RAU **** ///
symbol_number = 0;
symbol_mask = 0;
symbol_mask_full = (1<<(ru->nr_frame_parms->symbols_per_slot))-1;
do {
recv_IF4p5(ru, frame, slot, &packet_type, &symbol_number);
symbol_mask = symbol_mask | (1<<symbol_number);
} while (symbol_mask != symbol_mask_full);
}
void fh_if5_north_asynch_in(RU_t *ru, int *frame, int *slot)
{
AssertFatal(1 == 0, "Shouldn't get here\n");
}
void fh_if4p5_north_asynch_in(RU_t *ru,int *frame,int *slot) {
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nfapi_nr_config_request_scf_t *cfg = &ru->config;
RU_proc_t *proc = &ru->proc;
uint16_t packet_type;
uint32_t symbol_mask_full = 0;
int slot_tx,frame_tx;
LOG_D(PHY, "%s(ru:%p frame, subframe)\n", __FUNCTION__, ru);
uint32_t symbol_number = 0;
uint32_t symbol_mask = 0;
// symbol_mask_full = ((subframe_select(fp,*slot) == SF_S) ? (1<<fp->dl_symbols_in_S_subframe) : (1<<fp->symbols_per_slot))-1;
do {
recv_IF4p5(ru, &frame_tx, &slot_tx, &packet_type, &symbol_number);
if (((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0) && (symbol_number == 0))
start_meas(&ru->rx_fhaul);
LOG_D(PHY,"slot %d (%d): frame %d, slot %d, symbol %d\n",
*slot,nr_slot_select(cfg,frame_tx,*slot),frame_tx,slot_tx,symbol_number);
if (proc->first_tx != 0) {
*frame = frame_tx;
*slot = slot_tx;
proc->first_tx = 0;
} else {
AssertFatal(frame_tx == *frame,
"frame_tx %d is not what we expect %d\n",frame_tx,*frame);
AssertFatal(slot_tx == *slot,
"slot_tx %d is not what we expect %d\n",slot_tx,*slot);
}
if (packet_type == IF4p5_PDLFFT) {
symbol_mask = symbol_mask | (1<<symbol_number);
} else
AssertFatal(false, "Illegal IF4p5 packet type (should only be IF4p5_PDLFFT%d\n", packet_type);
} while (symbol_mask != symbol_mask_full);
if ((nr_slot_select(cfg, frame_tx, slot_tx) & NR_DOWNLINK_SLOT) > 0)
stop_meas(&ru->rx_fhaul);
proc->tti_tx = slot_tx;
proc->frame_tx = frame_tx;
if (frame_tx == 0 && slot_tx == 0)
proc->frame_tx_unwrap += 1024;
proc->timestamp_tx =
((uint64_t)frame_tx + proc->frame_tx_unwrap) * fp->samples_per_subframe * 10 + get_samples_slot_timestamp(fp, slot_tx);
LOG_D(PHY, "RU %d/%d TST %lu, frame %d, subframe %d\n", ru->idx, 0, proc->timestamp_tx, frame_tx, slot_tx);
if (ru->feptx_ofdm)
ru->feptx_ofdm(ru, frame_tx, slot_tx);
if (ru->fh_south_out)
ru->fh_south_out(ru, frame_tx, slot_tx, proc->timestamp_tx);
}
void fh_if5_north_out(RU_t *ru)
{
/// **** send_IF5 of rxdata to BBU **** ///
AssertFatal(1 == 0, "Shouldn't get here\n");
}
// RRU IF4p5 northbound interface (RX)
void fh_if4p5_north_out(RU_t *ru)
{
RU_proc_t *proc=&ru->proc;
start_meas(&ru->tx_fhaul);
send_IF4p5(ru, proc->frame_rx, proc->tti_rx, IF4p5_PULFFT);
stop_meas(&ru->tx_fhaul);
}
static void rx_rf(RU_t *ru, int *frame, int *slot)
{
RU_proc_t *proc = &ru->proc;
@@ -494,7 +292,7 @@ static radio_tx_gpio_flag_t get_gpio_flags(RU_t *ru, int slot)
return flags_gpio;
}
void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
void tx_rf_symbols(RU_t *ru, int frame, int slot, uint64_t timestamp, int start_symbol, int num_symbols)
{
RU_proc_t *proc = &ru->proc;
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
@@ -521,22 +319,23 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
txsymb++;
}
AssertFatal(txsymb>0,"illegal txsymb %d\n",txsymb);
AssertFatal(txsymb > 0, "illegal txsymb %d\n", txsymb);
if (fp->slots_per_subframe == 1) {
if (txsymb <= 7)
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = 2 * (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 2) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
} else {
if(slot%(fp->slots_per_subframe/2))
siglen = txsymb * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
else
siglen = (fp->ofdm_symbol_size + fp->nb_prefix_samples0) + (txsymb - 1) * (fp->ofdm_symbol_size + fp->nb_prefix_samples);
if (txsymb < start_symbol) {
// No DL symbols in this transmission
return;
}
//+ ru->end_of_burst_delay;
flags_burst = TX_BURST_END;
int end_symbol = start_symbol + num_symbols - 1;
if (end_symbol >= txsymb) {
flags_burst = TX_BURST_END;
} else {
flags_burst = TX_BURST_MIDDLE;
}
int num_symbols_this_transmission = min(txsymb, end_symbol) - start_symbol + 1;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols_this_transmission);
} else if (slot_type == NR_DOWNLINK_SLOT) {
int prevslot_type = nr_slot_select(cfg,frame,(slot+(fp->slots_per_frame-1))%fp->slots_per_frame);
int nextslot_type = nr_slot_select(cfg,frame,(slot+1)%fp->slots_per_frame);
@@ -548,9 +347,11 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
} else {
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
}
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
} else { // FDD
flags_burst = proc->first_tx == 1 ? TX_BURST_START : TX_BURST_MIDDLE;
siglen = get_samples_symbol_duration(fp, slot, start_symbol, num_symbols);
}
if (ru->openair0_cfg.gpio_controller != RU_GPIO_CONTROL_NONE)
@@ -561,8 +362,9 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
int nt = ru->nb_tx;
void *txp[nt];
uint32_t time_offset = get_samples_slot_timestamp(fp, slot) + get_samples_symbol_timestamp(fp, slot, start_symbol);
for (int i = 0; i < nt; i++)
txp[i] = (void *)&ru->common.txdata[i][get_samples_slot_timestamp(fp, slot)] - sf_extension * sizeof(int32_t);
txp[i] = (void *)&ru->common.txdata[i][time_offset] - sf_extension * sizeof(int32_t);
// prepare tx buffer pointers
uint32_t txs = ru->rfdevice.trx_write_func(&ru->rfdevice,
@@ -586,7 +388,12 @@ void tx_rf(RU_t *ru, int frame,int slot, uint64_t timestamp)
10 * log10((double)signal_energy(txp[0], siglen + sf_extension)));
}
static void fill_rf_config(RU_t *ru, char *rf_config_file)
void tx_rf(RU_t *ru, int frame, int slot, uint64_t timestamp)
{
tx_rf_symbols(ru, frame, slot, timestamp, 0, 14);
}
void fill_rf_config(RU_t *ru, char *rf_config_file)
{
NR_DL_FRAME_PARMS *fp = ru->nr_frame_parms;
nfapi_nr_config_request_scf_t *config = &ru->config; //tmp index
@@ -649,7 +456,7 @@ static void fill_rf_config(RU_t *ru, char *rf_config_file)
}
}
static void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
void fill_split7_2_config(split7_config_t *split7, const nfapi_nr_config_request_scf_t *config, const NR_DL_FRAME_PARMS *fp)
{
const nfapi_nr_prach_config_t *prach_config = &config->prach_config;
const nfapi_nr_tdd_table_t *tdd_table = &config->tdd_table;
@@ -833,7 +640,7 @@ void *ru_thread(void *param)
ret = openair0_transport_load(&ru->ifdevice, &ru->openair0_cfg, &ru->eth_params);
AssertFatal(ret == 0, "RU %u: openair0_transport_init() ret %d: cannot initialize transport protocol\n", ru->idx, ret);
if (ru->ifdevice.get_internal_parameter != NULL) {
if (ru->ifdevice.get_internal_parameter) {
/* it seems the device can "overwrite" (request?) to set the callbacks
* for fh_south_in()/fh_south_out() differently */
void *t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_in");
@@ -842,8 +649,6 @@ void *ru_thread(void *param)
t = ru->ifdevice.get_internal_parameter("fh_if4p5_south_out");
if (t != NULL)
ru->fh_south_out = t;
} else {
malloc_IF4p5_buffer(ru);
}
int cpu = sched_getcpu();
@@ -1088,94 +893,59 @@ void set_function_spec_param(RU_t *ru)
switch (ru->if_south) {
case LOCAL_RF: // this is an RU with integrated RF (RRU, gNB)
reset_meas(&ru->rx_fhaul);
if (ru->function == NGFI_RRU_IF5) { // IF5 RRU
ru->do_prach = 0; // no prach processing in RU
ru->fh_north_in = NULL; // no shynchronous incoming fronthaul from north
ru->fh_north_out = fh_if5_north_out; // need only to do send_IF5 reception
ru->fh_south_out = tx_rf; // send output to RF
ru->fh_north_asynch_in = fh_if5_north_asynch_in; // TX packets come asynchronously
ru->feprx = NULL; // nothing (this is a time-domain signal)
ru->feptx_ofdm = NULL; // nothing (this is a time-domain signal)
ru->feptx_prec = NULL; // nothing (this is a time-domain signal)
ru->nr_start_if = nr_start_if; // need to start the if interface for if5
ru->ifdevice.host_type = RRU_HOST;
ru->rfdevice.host_type = RRU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
reset_meas(&ru->rx_fhaul);
reset_meas(&ru->tx_fhaul);
reset_meas(&ru->compression);
reset_meas(&ru->transport);
} else if (ru->function == NGFI_RRU_IF4p5) {
ru->do_prach = 1; // do part of prach processing in RU
ru->fh_north_in = NULL; // no synchronous incoming fronthaul from north
ru->fh_north_out = fh_if4p5_north_out; // send_IF4p5 on reception
ru->fh_south_out = tx_rf; // send output to RF
ru->fh_north_asynch_in = fh_if4p5_north_asynch_in; // TX packets come asynchronously
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft only (no precoding in RRU)
ru->feptx_prec = NULL;
ru->nr_start_if = nr_start_if; // need to start the if interface for if4p5
ru->ifdevice.host_type = RRU_HOST;
ru->rfdevice.host_type = RRU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
reset_meas(&ru->tx_fhaul);
reset_meas(&ru->compression);
reset_meas(&ru->transport);
} else if (ru->function == gNodeB_3GPP) {
ru->do_prach = 0; // no prach processing in RU
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
ru->feptx_prec = NULL;
ru->fh_north_in = NULL; // no incoming fronthaul from north
ru->fh_north_out = NULL; // no outgoing fronthaul to north
ru->nr_start_if = NULL; // no if interface
ru->rfdevice.host_type = RAU_HOST;
ru->fh_south_in = rx_rf; // local synchronous RF RX
ru->fh_south_out = tx_rf; // local synchronous RF TX
ru->start_rf = start_rf; // need to start the local RF interface
ru->stop_rf = stop_rf;
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
}
AssertFatal(ru->function == gNodeB_3GPP, "ru->function %d not supported for LOCAL_RF\n", ru->function);
ru->do_prach = 0; // no prach processing in RU
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_ofdm = nr_feptx_tp; // this is fep with idft and precoding
ru->feptx_prec = NULL;
ru->fh_north_in = NULL; // no incoming fronthaul from north
ru->fh_north_out = NULL; // no outgoing fronthaul to north
ru->nr_start_if = NULL; // no if interface
ru->rfdevice.host_type = RAU_HOST;
ru->fh_south_in = rx_rf; // local synchronous RF RX
ru->fh_south_out = tx_rf; // local synchronous RF TX
ru->start_rf = start_rf; // need to start the local RF interface
ru->stop_rf = stop_rf;
ru->start_write_thread = start_write_thread; // starting RF TX in different thread
break;
case REMOTE_IF5: // the remote unit is IF5 RRU
ru->do_prach = 0;
ru->txfh_in_fep = 0;
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_prec = NULL; // need to do transmit Precoding + IDFTs
ru->feptx_ofdm = nr_feptx_tp; // need to do transmit Precoding + IDFTs
ru->fh_south_in = fh_if5_south_in; // synchronous IF5 reception
ru->fh_south_out = (ru->txfh_in_fep>0) ? NULL : fh_if5_south_out; // synchronous IF5 transmission
ru->fh_south_asynch_in = NULL; // no asynchronous UL
ru->start_rf = ru->eth_params.transp_preference == ETH_UDP_IF5_ECPRI_MODE ? start_streaming : NULL;
ru->stop_rf = NULL;
ru->start_write_thread = NULL;
ru->nr_start_if = nr_start_if; // need to start if interface for IF5
ru->ifdevice.host_type = RAU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
ru->do_prach = 0;
ru->txfh_in_fep = 0;
ru->feprx = nr_fep_tp; // this is frequency-shift + DFTs
ru->feptx_prec = NULL; // need to do transmit Precoding + IDFTs
ru->feptx_ofdm = nr_feptx_tp; // need to do transmit Precoding + IDFTs
ru->fh_south_in = fh_if5_south_in; // synchronous IF5 reception
ru->fh_south_out = (ru->txfh_in_fep > 0) ? NULL : fh_if5_south_out; // synchronous IF5 transmission
ru->fh_south_asynch_in = NULL; // no asynchronous UL
ru->start_rf = ru->eth_params.transp_preference == ETH_UDP_IF5_ECPRI_MODE ? start_streaming : NULL;
ru->stop_rf = NULL;
ru->start_write_thread = NULL;
ru->nr_start_if = nr_start_if; // need to start if interface for IF5
ru->ifdevice.host_type = RAU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
break;
case REMOTE_IF4p5:
ru->do_prach = 0;
ru->feprx = NULL; // DFTs
ru->feptx_prec = nr_feptx_prec; // Precoding operation
ru->feptx_ofdm = NULL; // no OFDM mod
ru->fh_south_in = fh_if4p5_south_in; // synchronous IF4p5 reception
ru->fh_south_out = fh_if4p5_south_out; // synchronous IF4p5 transmission
ru->fh_south_asynch_in = (ru->if_timing == synch_to_other) ? fh_if4p5_south_in : NULL; // asynchronous UL if synch_to_other
ru->fh_north_out = NULL;
ru->fh_north_asynch_in = NULL;
ru->start_rf = NULL; // no local RF
ru->stop_rf = NULL;
ru->start_write_thread = NULL;
ru->nr_start_if = nr_start_if; // need to start if interface for IF4p5
ru->ifdevice.host_type = RAU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
ru->do_prach = 0;
ru->feprx = NULL; // DFTs
ru->feptx_prec = nr_feptx_prec; // Precoding operation
ru->feptx_ofdm = NULL; // no OFDM mod
ru->fh_south_in = NULL;
ru->fh_south_out = NULL;
ru->fh_south_asynch_in = NULL;
ru->fh_north_out = NULL;
ru->fh_north_asynch_in = NULL;
ru->start_rf = NULL; // no local RF
ru->stop_rf = NULL;
ru->start_write_thread = NULL;
ru->nr_start_if = nr_start_if; // need to start if interface for IF4p5
ru->ifdevice.host_type = RAU_HOST;
ru->ifdevice.eth_params = &ru->eth_params;
break;
default:
LOG_E(PHY,"RU with invalid or unknown southbound interface type %d\n",ru->if_south);
LOG_E(PHY, "RU with invalid or unknown southbound interface type %d\n", ru->if_south);
break;
} // switch on interface type
}
@@ -1369,47 +1139,14 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
ru->openair0_cfg.tune_offset = get_softmodem_params()->tune_offset;
if (strcmp(*param[RU_LOCAL_RF_IDX].strptr, "yes") == 0) {
if (!config_isparamset(param, RU_LOCAL_IF_NAME_IDX)) {
ru->if_south = LOCAL_RF;
ru->function = gNodeB_3GPP;
LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j);
} else {
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
ru->eth_params.my_addr = strdup(*param[RU_LOCAL_ADDRESS_IDX].strptr);
ru->eth_params.remote_addr = strdup(*param[RU_REMOTE_ADDRESS_IDX].strptr);
ru->eth_params.my_portc = *param[RU_LOCAL_PORTC_IDX].uptr;
ru->eth_params.remote_portc = *param[RU_REMOTE_PORTC_IDX].uptr;
ru->eth_params.my_portd = *param[RU_LOCAL_PORTD_IDX].uptr;
ru->eth_params.remote_portd = *param[RU_REMOTE_PORTD_IDX].uptr;
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
if (strcmp(str, "udp") == 0) {
ru->if_south = LOCAL_RF;
ru->function = NGFI_RRU_IF5;
ru->eth_params.transp_preference = ETH_UDP_MODE;
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (udp)\n", j);
} else if (strcmp(str, "raw") == 0) {
ru->if_south = LOCAL_RF;
ru->function = NGFI_RRU_IF5;
ru->eth_params.transp_preference = ETH_RAW_MODE;
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF5 (raw)\n", j);
} else if (strcmp(str, "udp_if4p5") == 0) {
ru->if_south = LOCAL_RF;
ru->function = NGFI_RRU_IF4p5;
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (udp)\n", j);
} else if (strcmp(str, "raw_if4p5") == 0) {
ru->if_south = LOCAL_RF;
ru->function = NGFI_RRU_IF4p5;
ru->eth_params.transp_preference = ETH_RAW_IF4p5_MODE;
LOG_D(PHY, "Setting function for RU %d to NGFI_RRU_IF4p5 (raw)\n", j);
}
}
AssertFatal(!config_isparamset(param, RU_LOCAL_IF_NAME_IDX), "RU_TRANSPORT_PREFERENCE not supported for local RF\n");
ru->if_south = LOCAL_RF;
ru->function = gNodeB_3GPP;
LOG_D(PHY, "Setting function for RU %d to gNodeB_3GPP\n", j);
ru->max_pdschReferenceSignalPower = *param[RU_MAX_RS_EPRE_IDX].uptr;
ru->max_rxgain = *param[RU_MAX_RXGAIN_IDX].uptr;
ru->sf_extension = *param[RU_SF_EXTENSION_IDX].uptr;
} // strcmp(local_rf, "yes") == 0
else {
} else { // strcmp(local_rf, "yes") == 0
char *str = *param[RU_TRANSPORT_PREFERENCE_IDX].strptr;
LOG_D(PHY, "RU %d: Transport %s\n", j, str);
ru->eth_params.local_if_name = strdup(*param[RU_LOCAL_IF_NAME_IDX].strptr);
@@ -1432,10 +1169,6 @@ static void NRRCconfig_RU(configmodule_interface_t *cfg)
ru->if_south = REMOTE_IF5;
ru->function = NGFI_RAU_IF5;
ru->eth_params.transp_preference = ETH_RAW_MODE;
} else if (strcmp(str, "udp_if4p5") == 0) {
ru->if_south = REMOTE_IF4p5;
ru->function = NGFI_RAU_IF4p5;
ru->eth_params.transp_preference = ETH_UDP_IF4p5_MODE;
} else if (strcmp(str, "raw_if4p5") == 0) {
ru->if_south = REMOTE_IF4p5;
ru->function = NGFI_RAU_IF4p5;

View File

@@ -28,7 +28,6 @@
#include <unistd.h>
#include <sys/sysinfo.h>
#include "radio/COMMON/common_lib.h"
#include "assertions.h"
/* help strings definition for command line options, used in CMDLINE_XXX_DESC macros and printed when -h option is used */

View File

@@ -19,6 +19,11 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "openair2/E2AP/flexric/src/agent/e2_agent_api.h"
#include "openair2/E2AP/RAN_FUNCTION/init_ran_func.h"
#endif
#ifdef E3_AGENT
#include "openair2/E3AP/e3_agent.h"
#endif
#include "nr-softmodem.h"
#include <common/utils/assertions.h>
#include <openair2/GNB_APP/gnb_app.h>
@@ -60,7 +65,6 @@ unsigned short config_frames[4] = {2,9,11,13};
#include "nr-softmodem-common.h"
#include "openair2/E1AP/e1ap_common.h"
#include "pdcp.h"
#include "radio/COMMON/common_lib.h"
#include "s1ap_eNB.h"
#include "sctp_eNB_task.h"
#include "system.h"
@@ -521,6 +525,13 @@ int main( int argc, char **argv ) {
softmodem_verify_mode(get_softmodem_params());
//////////////////////////////////
//// Init the E3 Agent
#ifdef E3_AGENT
printf("Init E3 Agent\n");
e3_init();
#endif // E3_AGENT
#if T_TRACER
T_Config_Init();
#endif
@@ -727,6 +738,11 @@ int main( int argc, char **argv ) {
time_manager_finish();
#ifdef E3_AGENT
printf("Destroy E3 Agent\n");
e3_destroy();
#endif // E3_AGENT
free(pckg);
logClean();
printf("Bye.\n");

View File

@@ -64,4 +64,6 @@ void init_eNB_afterRU(void);
void init_pdcp(void);
void fill_rf_config(RU_t *ru, char *rf_config_file);
#endif

View File

@@ -215,9 +215,8 @@ static void UE_synch(void *arg) {
((ret.rx_offset << 1) / fp->samples_per_subframe * fp->slots_per_subframe)
+ round((float)((ret.rx_offset << 1) % fp->samples_per_subframe) / fp->samples_per_slot0);
if (get_nrUE_params()->cont_fo_comp) {
UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
} else {
UE->freq_offset = freq_offset - UE->dl_Doppler_shift;
if (!get_nrUE_params()->cont_fo_comp) {
// rerun with new cell parameters and frequency-offset
nrue_ru_set_freq(UE, ul_carrier, dl_carrier, freq_offset);
}
@@ -711,6 +710,18 @@ static inline int get_readBlockSize(uint16_t slot, const NR_DL_FRAME_PARMS *fp)
return rem_samples + next_slot_first_symbol;
}
void trs_freq_correction(PHY_VARS_NR_UE *ue, int cfo)
{
if (abs(cfo) > TRS_CFO_THRESH) {
LOG_A(PHY, "CFO estimated (%d) from TRS exceeded threshold (%d). Adjusting radio CF\n", cfo, TRS_CFO_THRESH);
ue->freq_offset += cfo;
uint64_t dl_carrier;
uint64_t ul_carrier;
nr_get_carrier_frequencies(ue, &dl_carrier, &ul_carrier);
nrue_ru_set_freq(ue, ul_carrier, dl_carrier, ue->freq_offset);
}
}
void *UE_thread(void *arg)
{
//this thread should be over the processing thread to keep in real time

View File

@@ -18,7 +18,6 @@
#include "common/config/config_userapi.h"
#include "common/utils/load_module_shlib.h"
#include "common/utils/nr/nr_common.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/if_defs.h"
#include "openair1/PHY/MODULATION/nr_modulation.h"
#include "PHY/CODING/nrLDPC_coding/nrLDPC_coding_interface.h"

View File

@@ -24,7 +24,6 @@
#include "PHY/types.h"
#include "PHY/defs_common.h"
#include "radio/COMMON/common_lib.h"
#include "radio/ETHERNET/ethernet_lib.h"
#include "PHY/if4_tools.h"
@@ -562,7 +561,8 @@ void* ru_thread_control( void* param )
//if (ru->is_slave == 1) lte_sync_time_init(&ru->frame_parms);
if (ru->rfdevice.is_init != 1) openair0_device_load(&ru->rfdevice,&ru->openair0_cfg);
int ret = openair0_device_load(&ru->rfdevice, &ru->openair0_cfg);
AssertFatal(ret == 0, "could not load device library\n");
if (ru->rfdevice.trx_config_func) AssertFatal((ru->rfdevice.trx_config_func(&ru->rfdevice,&ru->openair0_cfg)==0),
"Failed to configure RF device for RU %d\n",ru->idx);

View File

@@ -223,12 +223,12 @@ void oru_fh_cleanup(void *handle)
free(fh);
}
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
{
if (!handle)
return -1;
oru_fh_t *fh = (oru_fh_t *)handle;
read_dl_iq(fh->packet_processor, txdataF, nb_tx, frame, slot, symbol);
read_dl_iq(fh->packet_processor, txdataF, nb_tx, hyper_frame, frame, slot, symbol);
return 0;
}
@@ -240,7 +240,7 @@ int oru_fh_get_ready_jobs(void *handle)
return get_ready_job_count(fh->packet_processor);
}
int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, struct timespec *ts)
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t *frame, uint32_t *slot, struct timespec *ts)
{
if (!handle || !frame || !slot || !ts)
return -1;
@@ -249,6 +249,7 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t *frame, uint32_t *slot, s
absolute_gps_symbol -= absolute_gps_symbol % NR_SYMBOLS_PER_SLOT; // Round down to start of current slot
uint64_t absolute_slot = absolute_gps_symbol / NR_SYMBOLS_PER_SLOT;
uint32_t slots_per_frame = 10 << fh->cfg.numerology;
*hyper_frame = (absolute_slot / slots_per_frame) / 1024;
*frame = (absolute_slot / slots_per_frame) % 1024;
*slot = absolute_slot % slots_per_frame;
@@ -267,11 +268,17 @@ void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame,
write_prach_iq(fh->packet_processor, prachF, nb_rx, frame, slot, symbol);
}
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol)
int oru_fh_poll_ul_job(void *handle, ul_job_t *job) {
oru_fh_t *fh = (oru_fh_t *)handle;
AssertFatal(fh, "Invalid handle\n");
return poll_ul_job(fh->packet_processor, job);
}
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol_index, const ul_job_t *job)
{
oru_fh_t *fh = (oru_fh_t *)handle;
AssertFatal(fh, "Invalid handle\n");
write_ul_iq(fh->packet_processor, puschF, nb_rx, frame, slot, symbol);
write_ul_iq(fh->packet_processor, puschF, symbol_index, job);
}
int oru_fh_start(void *handle)

View File

@@ -7,6 +7,7 @@
#include "oru_io.h"
#include <stdint.h>
#include "oru_packet_processor.h"
typedef struct {
char *dpdk_devices[MAX_RU_PORTS];
@@ -74,23 +75,25 @@ int oru_fh_get_ready_jobs(void *handle);
* @param handle Pointer to the fronthaul handle.
* @param txdataF Array of pointers to buffers to store the received frequency-domain IQ samples (per TX antenna).
* @param nb_tx Number of TX antennas.
* @param hyper_frame Absolute GPS hyper-frame number
* @param frame Pointer to store the frame number of the read symbol.
* @param slot Pointer to store the slot number of the read symbol.
* @param symbol Pointer to store the symbol number.
* @return 0 on success or -1 on failure
*/
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
int oru_fh_tx_read_symbol(void *handle, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
/**
* @brief Get the UTC anchor point mapping between 5G time and system time.
*
* @param handle Pointer to the fronthaul handle.
* @param frame Pointer to store the reference frame number.
* @param hyper_frame Pointer to store the reference hyperframe number (1024 frames each)
* @param slot Pointer to store the reference slot number.
* @param ts Pointer to a timespec structure to store the corresponding system time.
* @return 0 on success, negative on error.
*/
int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, struct timespec *ts);
int oru_fh_get_utc_anchor_point(void *handle, uint64_t *hyper_frame, uint32_t* frame, uint32_t* slot, struct timespec *ts);
/**
* @brief Send PRACH symbol data (U-Plane) over the Fronthaul interface.
@@ -104,17 +107,25 @@ int oru_fh_get_utc_anchor_point(void *handle, uint32_t* frame, uint32_t* slot, s
*/
void oru_fh_rx_send_prach(void *handle, uint32_t **prachF, int nb_rx, int frame, int slot, int symbol);
/**
* @brief Poll for pending UL jobs
*
* @param handle Pointer to the fronthaul handle.
* @param job Job descriptor
*
* @returns 0 if successful, -1 otherwise
*/
int oru_fh_poll_ul_job(void *handle, ul_job_t *job);
/**
* @brief Send PUSCH symbol data (U-Plane) over the Fronthaul interface.
*
* @param handle Pointer to the fronthaul handle.
* @param puschF Array of pointers to buffers containing the frequency-domain PUSCH IQ samples.
* @param nb_rx Number of RX antennas.
* @param frame Target frame number.
* @param slot Target slot number.
* @param symbol Target symbol number.
* @param symbol Absolute symbol index within slot (013); must be within the job's symbol range
* @param job Job descriptor
*/
void oru_fh_rx_send_pusch(void *handle, uint32_t **puschF, int nb_rx, int frame, int slot, int symbol);
void oru_fh_rx_send_pusch(void *handle, uint32_t *puschF, int symbol, const ul_job_t *job);
/**
* @brief Start the O-RU Fronthaul processing threads and loops.

View File

@@ -9,6 +9,7 @@
#include <stdint.h>
#include <stdlib.h>
#include <stdbool.h>
#include <time.h>
#include "assertions.h"
#include "log.h"
#include <rte_ring.h>
@@ -16,8 +17,21 @@
#include <sys/types.h>
#include <sys/types.h>
#include <stdatomic.h>
#define PRACH_ERR_LOG_RATELIMIT 10000
#define RATELIMIT(n, block) \
do { \
static _Atomic unsigned long counter = 0; \
unsigned long current = atomic_fetch_add_explicit(&counter, 1, memory_order_relaxed); \
if (current % (n) == 0) { \
block \
} \
} while (0)
#define DL_JOB_RING_SIZE 128
#define UL_JOB_RING_SIZE 128
#define MAX_CONCURRENT_DL_JOBS (DL_JOB_RING_SIZE - 1)
#define NUM_CONCURRENT_DL_SYMBOL_WINDOWS MAX_CONCURRENT_DL_JOBS
#define NUM_CONCURRENT_UL_SYMBOL_WINDOWS 128
@@ -28,13 +42,7 @@
#define SYMBOL_BITMASK_SIZE ((NR_SYMBOLS_PER_SLOT * MAX_TDD_PATTERN_LENGTH_MS * MAX_SLOTS_PER_MS + 7) / 8)
#define MAX_RX_FRAGMENTS 4
#define MAX_MBUFS_PER_SYMBOL 64
typedef enum {
SYM_UL_IDLE,
SYM_UL_ACTIVE,
SYM_UL_READY,
SYM_UL_EMPTY,
} ul_symbol_job_state_t;
#define MAX_SLOTS_PER_FRAME 160
typedef struct {
struct {
@@ -54,43 +62,43 @@ typedef struct {
} dl_symbol_job_t;
typedef struct {
struct {
bool cplane_received;
int section_id;
int num_prb;
int start_prb;
} per_antenna[MAX_ANTENNAS];
uint64_t absolute_symbol;
ul_symbol_job_state_t state;
} ul_symbol_job_t;
typedef struct {
struct {
bool cplane_received;
int section_id;
int num_prb;
int start_prb;
int filter_id;
} per_antenna[MAX_ANTENNAS];
uint64_t absolute_symbol;
} prach_symbol_job_t;
bool active;
uint64_t start_absolute_symbol;
uint32_t num_symbols;
int section_id;
int num_prb;
int start_prb;
int filter_id;
} prach_job_t;
typedef struct {
_Atomic(uint64_t) dl_tdd_mismatch;
_Atomic(uint64_t) ul_tdd_mismatch;
_Atomic(uint64_t) ul_cplane_missing;
_Atomic(uint64_t) prach_cplane_missing;
_Atomic(uint64_t) prach_cplane_missing_ant;
_Atomic(uint64_t) prach_cplane_missing_inactive;
_Atomic(uint64_t) prach_cplane_missing_stale;
_Atomic(uint64_t) prach_cplane_missing_early;
_Atomic(uint64_t) prach_out_of_mbufs;
_Atomic(uint64_t) prach_jobs_pool_exhausted;
_Atomic(uint64_t) out_of_mbufs;
_Atomic(uint64_t) total_uplane_sent;
_Atomic(int64_t) ul_uplane_ota_delay_sum;
_Atomic(uint64_t) ul_uplane_ota_delay_count;
} thread_safe_stats_t;
typedef struct {
dl_symbol_job_t dl_symbol_jobs[MAX_CONCURRENT_DL_JOBS];
dl_symbol_job_t *dl_symbol_rx_window[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
bool was_dl_symbol_completed[NUM_CONCURRENT_DL_SYMBOL_WINDOWS];
ul_symbol_job_t ul_symbol_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
prach_symbol_job_t prach_jobs[NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
prach_job_t prach_jobs[MAX_SLOTS_PER_FRAME][MAX_ANTENNAS];
uint64_t current_absolute_symbol;
uint64_t last_pushed_symbol;
struct rte_ring *dl_free_jobs;
struct rte_ring *dl_ready_jobs;
struct rte_ring *ul_free_jobs;
struct rte_ring *ul_ready_jobs;
ul_job_t ul_jobs_pool[UL_JOB_RING_SIZE];
uint32_t T2a_min_cp_sym_diff;
uint32_t T2a_max_cp_sym_diff;
uint32_t T2a_min_up_dl_sym_diff;
@@ -169,6 +177,13 @@ void *init_packet_processor(int numerology,
for (int i = 0; i < MAX_CONCURRENT_DL_JOBS; i++) {
rte_ring_enqueue(ctx->dl_free_jobs, (void *)&ctx->dl_symbol_jobs[i]);
}
ctx->ul_ready_jobs = rte_ring_create("ul_ready_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
AssertFatal(ctx->ul_ready_jobs != NULL, "Failed to create ring ul_ready_jobs\n");
ctx->ul_free_jobs = rte_ring_create("ul_free_jobs", UL_JOB_RING_SIZE, rte_socket_id(), 0);
AssertFatal(ctx->ul_free_jobs != NULL, "Failed to create ring ul_free_jobs\n");
for (int i = 0; i < UL_JOB_RING_SIZE - 1; i++) {
rte_ring_enqueue(ctx->ul_free_jobs, (void *)&ctx->ul_jobs_pool[i]);
}
ctx->eaxcid_config = (struct xran_eaxcid_config){.mask_cuPortId = 0xF000,
.mask_bandSectorId = 0x0F00,
@@ -201,6 +216,12 @@ void cleanup_packet_processor(void *context)
if (ctx->dl_free_jobs) {
rte_ring_free(ctx->dl_free_jobs);
}
if (ctx->ul_ready_jobs) {
rte_ring_free(ctx->ul_ready_jobs);
}
if (ctx->ul_free_jobs) {
rte_ring_free(ctx->ul_free_jobs);
}
free(ctx);
}
}
@@ -465,6 +486,7 @@ static void handle_dl_cplane_packet(oru_packet_processor_context_t *ctx,
}
if (job->per_antenna[ant_id].cplane_received) {
ctx->stats.cplane_err_dup++;
ctx->stats.cplane_err_dup_dl++;
return;
}
}
@@ -507,30 +529,25 @@ static void handle_ul_cplane_packet(oru_packet_processor_context_t *ctx,
ctx->stats.ul_tdd_mismatch++;
return;
}
for (int i = 0; i < num_symbols; i++) {
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[job_index];
if (job->state == SYM_UL_IDLE) {
job->absolute_symbol = target_absolute_symbol + i;
job->state = SYM_UL_ACTIVE;
for (int j = 0; j < MAX_ANTENNAS; j++) {
job->per_antenna[j].cplane_received = false;
}
} else if (job->state == SYM_UL_READY) {
ctx->stats.application_too_slow++;
return;
}
if (job->per_antenna[ant_id].cplane_received) {
ctx->stats.cplane_err_dup++;
return;
}
job->absolute_symbol = target_absolute_symbol + i;
if (!job->per_antenna[ant_id].cplane_received) {
job->per_antenna[ant_id].cplane_received = true;
job->per_antenna[ant_id].section_id = section->hdr.u1.common.sectionId;
job->per_antenna[ant_id].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
job->per_antenna[ant_id].start_prb = section->hdr.u1.common.startPrbc;
}
ul_job_t *ul_job = NULL;
if (rte_ring_dequeue(ctx->ul_free_jobs, (void **)&ul_job) == 0) {
memset(ul_job, 0, sizeof(*ul_job));
ul_job->response_payload.section_id = section->hdr.u1.common.sectionId;
ul_job->response_payload.comp_method = hdr->udComp.udCompMeth;
ul_job->response_payload.iq_width = hdr->udComp.udIqWidth == 0 ? 16 : hdr->udComp.udIqWidth;
uint64_t absolute_gps_symbol = target_absolute_symbol;
ul_job->hyper_frame = absolute_gps_symbol / (1024 * (10 * (1 << ctx->numerology) * 14));
ul_job->frame = (absolute_gps_symbol / (10 * (1 << ctx->numerology) * 14)) % 1024;
ul_job->slot_in_frame = (absolute_gps_symbol % (10 * (1 << ctx->numerology) * 14)) / 14;
ul_job->symbol = absolute_gps_symbol % 14;
ul_job->num_symbols = num_symbols;
ul_job->antenna_id = ant_id;
ul_job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
ul_job->start_prb = section->hdr.u1.common.startPrbc;
int ret = rte_ring_enqueue(ctx->ul_ready_jobs, (void *)ul_job);
AssertFatal(ret == 0, "Failed to enqueue ul_job to ul_ready_jobs ring\n");
} else {
ctx->stats.application_too_slow++;
}
}
@@ -541,6 +558,8 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
{
if (hdr->cmnhdr.numOfSections != 1) {
ctx->stats.cplane_err_hdr++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
{ LOG_W(HW, "PRACH CP: Invalid numOfSections %d (expected 1)\n", hdr->cmnhdr.numOfSections); });
return;
}
@@ -550,11 +569,14 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
struct xran_cp_radioapp_section3 *section = (void *)rte_pktmbuf_adj(pkt, sizeof(struct xran_cp_radioapp_section3_header));
if (section == NULL) {
ctx->stats.cplane_err_hdr++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Failed to adjust mbuf for section3 header\n"); });
return;
}
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
int aarx = ant_id - ctx->prach_eaxc_offset;
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
{ LOG_W(HW, "PRACH CP: Invalid aarx %d (ant_id %d, eaxc_offset %d)\n", aarx, ant_id, ctx->prach_eaxc_offset); });
return;
}
@@ -574,26 +596,34 @@ void handle_prach_cplane_packet(oru_packet_processor_context_t *ctx,
txrx_window_histogram_count(&ctx->stats.prach_cplane_hist, diff);
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
for (int i = 0; i < num_symbols; i++) {
uint32_t job_index = (target_absolute_symbol + i) % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
if (job->absolute_symbol != target_absolute_symbol + i) {
job->absolute_symbol = target_absolute_symbol + i;
for (int j = 0; j < MAX_ANTENNAS; j++) {
job->per_antenna[j].cplane_received = false;
}
}
if (job->per_antenna[aarx].cplane_received) {
continue;
}
job->per_antenna[aarx].cplane_received = true;
job->per_antenna[aarx].section_id = section->hdr.u1.common.sectionId;
job->per_antenna[aarx].num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
job->per_antenna[aarx].start_prb = section->hdr.u1.common.startPrbc;
job->per_antenna[aarx].filter_id = hdr->cmnhdr.field.filterIndex;
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH CP: Invalid slot_in_frame %d\n", slot_in_frame); });
return;
}
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
if (job->active && job->start_absolute_symbol == target_absolute_symbol) {
ctx->stats.cplane_err_dup++;
ctx->stats.cplane_err_dup_prach++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
LOG_W(HW, "PRACH CP: Duplicate packet for slot %d, aarx %d, start_symbol %lu\n", slot_in_frame, aarx, target_absolute_symbol);
});
return;
}
job->active = true;
job->start_absolute_symbol = target_absolute_symbol;
job->num_symbols = num_symbols;
job->section_id = section->hdr.u1.common.sectionId;
job->num_prb = section->hdr.u1.common.numPrbc == 0 ? ctx->num_prb : section->hdr.u1.common.numPrbc;
job->start_prb = section->hdr.u1.common.startPrbc;
job->filter_id = hdr->cmnhdr.field.filterIndex;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
LOG_A(HW,
"PRACH JOB added slot_in_frame %d, aarx %d target_absolute_symbol %lu\n",
slot_in_frame,
aarx,
target_absolute_symbol);
});
}
void handle_cplane_packet(void *context, void *pkt)
@@ -633,20 +663,24 @@ void handle_cplane_packet(void *context, void *pkt)
}
*((uint64_t *)section) = rte_be_to_cpu_64(*((uint64_t *)section));
if (hdr->cmnhdr.field.dataDirection == XRAN_DIR_DL) {
ctx->stats.cplane_received_dl++;
handle_dl_cplane_packet(ctx, pkt, hdr, section, ant_id);
} else {
ctx->stats.cplane_received_ul++;
handle_ul_cplane_packet(ctx, pkt, hdr, section, ant_id);
}
rte_pktmbuf_free(pkt);
return;
}
case XRAN_CP_SECTIONTYPE_3: {
ctx->stats.cplane_received_prach++;
struct xran_cp_radioapp_section3_header *hdr = (struct xran_cp_radioapp_section3_header *)apphdr;
handle_prach_cplane_packet(ctx, pkt, hdr, ant_id);
rte_pktmbuf_free(pkt);
return;
}
default:
ctx->stats.cplane_received_other++;
rte_pktmbuf_free(pkt);
return;
}
@@ -656,19 +690,38 @@ static void print_histogram(const char *name, txrx_histogram_t *hist, uint32_t w
{
if (hist->count == 0)
return;
printf(" %s (mean: %.2f symbols) window start: %u, end %u:\n", name, (double)hist->sum / hist->count, window_start, window_end);
char buf[4096];
int len = snprintf(buf,
sizeof(buf),
" %s (mean: %.2f symbols) window [%u, %u]:",
name,
(double)hist->sum / hist->count,
window_start,
window_end);
bool first = true;
for (int i = 0; i < HIST_SIZE; i++) {
if (hist->hist[i] > 0) {
int bucket = i - HIST_SIZE / 2;
char bin_str[64];
int bin_len = 0;
if (i == 0) {
printf(" <= %+d : %lu\n", bucket, hist->hist[i]);
bin_len = snprintf(bin_str, sizeof(bin_str), "%s<=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
} else if (i == HIST_SIZE - 1) {
printf(" >= %+d : %lu\n", bucket, hist->hist[i]);
bin_len = snprintf(bin_str, sizeof(bin_str), "%s>=%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
} else {
printf(" %+d : %lu\n", bucket, hist->hist[i]);
bin_len = snprintf(bin_str, sizeof(bin_str), "%s%+d:%lu", first ? " " : ", ", bucket, hist->hist[i]);
}
first = false;
if (len + bin_len < sizeof(buf)) {
strcpy(buf + len, bin_str);
len += bin_len;
} else {
break; // buffer full
}
}
}
LOG_I(HW, "%s\n", buf);
memset(hist, 0, sizeof(*hist));
}
void print_packet_processor_stats(void *context)
@@ -677,39 +730,73 @@ void print_packet_processor_stats(void *context)
if (ctx == NULL)
return;
printf("ORU Packet Processor Stats:\n");
printf(" Total C-Plane Packets received: %lu\n", ctx->stats.total_cplane);
printf(" Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
printf(" Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
LOG_I(HW, "ORU Packet Processor Stats:\n");
LOG_I(HW,
" Total C-Plane Packets received: %lu (DL: %lu, UL: %lu, PRACH: %lu, Other: %lu)\n",
ctx->stats.total_cplane,
ctx->stats.cplane_received_dl,
ctx->stats.cplane_received_ul,
ctx->stats.cplane_received_prach,
ctx->stats.cplane_received_other);
LOG_I(HW, " Total U-Plane Packets received: %lu\n", ctx->stats.total_uplane_received);
LOG_I(HW, " Total U-Plane Packets sent: %lu\n", ctx->thread_safe_stats.total_uplane_sent);
if (ctx->thread_safe_stats.ul_uplane_ota_delay_count > 0)
LOG_I(HW,
" UL U-Plane OTA delay (mean symbols): %.2f (%lu packets)\n",
(double)(int64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_sum
/ (double)ctx->thread_safe_stats.ul_uplane_ota_delay_count,
(uint64_t)ctx->thread_safe_stats.ul_uplane_ota_delay_count);
if (ctx->stats.cplane_err_hdr > 0)
printf(" C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
LOG_I(HW, " C-Plane Header Errors: %lu\n", ctx->stats.cplane_err_hdr);
if (ctx->stats.cplane_err_ver > 0)
printf(" C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
LOG_I(HW, " C-Plane Protocol Version Errors: %lu\n", ctx->stats.cplane_err_ver);
if (ctx->stats.cplane_err_early > 0)
printf(" C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
LOG_I(HW, " C-Plane Timing Early Errors: %lu\n", ctx->stats.cplane_err_early);
if (ctx->stats.cplane_err_late > 0)
printf(" C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
LOG_I(HW, " C-Plane Timing Late Errors: %lu\n", ctx->stats.cplane_err_late);
if (ctx->stats.cplane_err_dup > 0)
printf(" C-Plane Duplicate Packet Errors: %lu\n", ctx->stats.cplane_err_dup);
LOG_I(HW,
" C-Plane Duplicate Packet Errors: %lu (DL: %lu, UL: %lu, PRACH: %lu)\n",
ctx->stats.cplane_err_dup,
ctx->stats.cplane_err_dup_dl,
ctx->stats.cplane_err_dup_ul,
ctx->stats.cplane_err_dup_prach);
if (ctx->stats.uplane_err_early > 0)
printf(" U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
LOG_I(HW, " U-Plane Timing Early Errors: %lu\n", ctx->stats.uplane_err_early);
if (ctx->stats.uplane_err_late > 0)
printf(" U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
LOG_I(HW, " U-Plane Timing Late Errors: %lu\n", ctx->stats.uplane_err_late);
if (ctx->stats.uplane_err_dup > 0)
printf(" U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
LOG_I(HW, " U-Plane Duplicate Packet Errors: %lu\n", ctx->stats.uplane_err_dup);
if (ctx->stats.uplane_missing_cplane > 0)
printf(" U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
LOG_I(HW, " U-Plane Missing C-Plane Errors: %lu\n", ctx->stats.uplane_missing_cplane);
if (ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch > 0)
printf(" DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
LOG_I(HW, " DL TDD Mismatch Errors: %lu\n", ctx->stats.dl_tdd_mismatch + ctx->thread_safe_stats.dl_tdd_mismatch);
if (ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch > 0)
printf(" UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
LOG_I(HW, " UL TDD Mismatch Errors: %lu\n", ctx->stats.ul_tdd_mismatch + ctx->thread_safe_stats.ul_tdd_mismatch);
if (ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing > 0)
printf(" UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
LOG_I(HW, " UL C-Plane Missing Errors: %lu\n", ctx->stats.ul_cplane_missing + ctx->thread_safe_stats.ul_cplane_missing);
if (ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing > 0)
LOG_I(HW,
" PRACH C-Plane Missing Errors: %lu (Never Received: %lu, Stale: %lu, Early: %lu)\n",
ctx->stats.prach_cplane_missing + ctx->thread_safe_stats.prach_cplane_missing,
ctx->stats.prach_cplane_missing_inactive + ctx->thread_safe_stats.prach_cplane_missing_inactive,
ctx->stats.prach_cplane_missing_stale + ctx->thread_safe_stats.prach_cplane_missing_stale,
ctx->stats.prach_cplane_missing_early + ctx->thread_safe_stats.prach_cplane_missing_early);
if (ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant > 0)
LOG_I(HW,
" PRACH Ant C-Plane Missing Errors: %lu\n",
ctx->stats.prach_cplane_missing_ant + ctx->thread_safe_stats.prach_cplane_missing_ant);
if (ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs > 0)
LOG_I(HW, " PRACH Out Of Mbufs Errors: %lu\n", ctx->stats.prach_out_of_mbufs + ctx->thread_safe_stats.prach_out_of_mbufs);
if (ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted > 0)
LOG_I(HW,
" PRACH Jobs Pool Exhausted Errors: %lu\n",
ctx->stats.prach_jobs_pool_exhausted + ctx->thread_safe_stats.prach_jobs_pool_exhausted);
if (ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs > 0)
printf(" Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
LOG_I(HW, " Out Of Mbufs Errors: %lu\n", ctx->stats.out_of_mbufs + ctx->thread_safe_stats.out_of_mbufs);
if (ctx->stats.application_too_slow > 0)
printf(" Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
LOG_I(HW, " Application Too Slow Errors: %lu\n", ctx->stats.application_too_slow);
print_histogram("DL C-Plane", &ctx->stats.dl_cplane_hist, ctx->T2a_max_cp_sym_diff, ctx->T2a_min_cp_sym_diff);
print_histogram("DL U-Plane", &ctx->stats.dl_uplane_hist, ctx->T2a_max_up_dl_sym_diff, ctx->T2a_min_up_dl_sym_diff);
@@ -725,8 +812,17 @@ void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out
out_stats->dl_tdd_mismatch += ctx->thread_safe_stats.dl_tdd_mismatch;
out_stats->ul_tdd_mismatch += ctx->thread_safe_stats.ul_tdd_mismatch;
out_stats->ul_cplane_missing += ctx->thread_safe_stats.ul_cplane_missing;
out_stats->prach_cplane_missing += ctx->thread_safe_stats.prach_cplane_missing;
out_stats->prach_cplane_missing_ant += ctx->thread_safe_stats.prach_cplane_missing_ant;
out_stats->prach_cplane_missing_inactive += ctx->thread_safe_stats.prach_cplane_missing_inactive;
out_stats->prach_cplane_missing_stale += ctx->thread_safe_stats.prach_cplane_missing_stale;
out_stats->prach_cplane_missing_early += ctx->thread_safe_stats.prach_cplane_missing_early;
out_stats->prach_out_of_mbufs += ctx->thread_safe_stats.prach_out_of_mbufs;
out_stats->prach_jobs_pool_exhausted += ctx->thread_safe_stats.prach_jobs_pool_exhausted;
out_stats->out_of_mbufs += ctx->thread_safe_stats.out_of_mbufs;
out_stats->total_uplane_sent = ctx->thread_safe_stats.total_uplane_sent;
out_stats->ul_uplane_ota_delay_sum += ctx->thread_safe_stats.ul_uplane_ota_delay_sum;
out_stats->ul_uplane_ota_delay_count += ctx->thread_safe_stats.ul_uplane_ota_delay_count;
}
}
@@ -739,7 +835,7 @@ static void unpack_iq(c16_t *txdataF, void *iqdata, int start_prb, int num_prb)
}
}
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol)
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol)
{
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
if (ctx == NULL)
@@ -754,6 +850,7 @@ void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *s
uint64_t absolute_gps_symbol = job->absolute_symbol;
int numerology = ctx->numerology;
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
*hyper_frame = (absolute_gps_symbol / num_symbols_per_frame) / 1024;
*frame = (absolute_gps_symbol / num_symbols_per_frame) % 1024;
*slot = (absolute_gps_symbol % num_symbols_per_frame) / NR_SYMBOLS_PER_SLOT;
*symbol = absolute_gps_symbol % NR_SYMBOLS_PER_SLOT;
@@ -834,11 +931,142 @@ void fill_data_section_header(struct data_section_hdr *data_section_hdr, int num
data_section_hdr->fields.all_bits = rte_cpu_to_be_32(data_section_hdr->fields.all_bits);
}
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
int poll_ul_job(void *context, ul_job_t *job)
{
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
if (ctx == NULL || job == NULL) {
return -1;
}
ul_job_t *dequeued_job = NULL;
if (rte_ring_dequeue(ctx->ul_ready_jobs, (void **)&dequeued_job) == 0) {
*job = *dequeued_job;
rte_ring_enqueue(ctx->ul_free_jobs, (void *)dequeued_job);
return 0;
}
return -1;
}
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job)
{
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
if (ctx == NULL || job == NULL)
return;
AssertFatal(symbol >= job->symbol && symbol < job->symbol + job->num_symbols && symbol < NR_SYMBOLS_PER_SLOT,
"Symbol %d outside of job range [%d, %d)\n",
symbol,
job->symbol,
job->symbol + job->num_symbols);
// Delay from OTA symbol to packet send: current timer symbol minus the absolute symbol of this UL symbol.
const int slots_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << ctx->numerology);
const uint64_t ota_absolute_symbol = (uint64_t)job->hyper_frame * 1024ULL * slots_per_frame * NR_SYMBOLS_PER_SLOT
+ (uint64_t)job->frame * slots_per_frame * NR_SYMBOLS_PER_SLOT
+ (uint64_t)job->slot_in_frame * NR_SYMBOLS_PER_SLOT
+ (uint64_t)symbol;
int64_t delay = (int64_t)ctx->current_absolute_symbol - (int64_t)ota_absolute_symbol;
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_sum, delay, memory_order_relaxed);
atomic_fetch_add_explicit(&ctx->thread_safe_stats.ul_uplane_ota_delay_count, 1, memory_order_relaxed);
int aarx = job->antenna_id;
if (aarx < 0 || aarx >= MAX_ANTENNAS) {
LOG_W(HW, "ORU: Invalid antenna index %d\n", aarx);
return;
}
const bool use_comp = (job->response_payload.comp_method != 0);
int section_id = job->response_payload.section_id;
int total_ul_rbs = job->num_prb;
int start_prb_base = job->start_prb;
int frame = job->frame;
int slot_in_frame = job->slot_in_frame;
int mu = ctx->numerology;
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
uint32_t num_mbufs = 0;
int rbs_sent = 0;
size_t overhead = sizeof(struct rte_ether_hdr) + sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
+ sizeof(struct data_section_hdr);
if (use_comp) {
overhead += sizeof(struct data_section_compression_hdr);
}
int max_prb_per_packet = (int)((ctx->mtu - overhead) / (NR_NB_SC_PER_RB * sizeof(int32_t)));
while (rbs_sent < total_ul_rbs) {
int num_ul_rbs = total_ul_rbs - rbs_sent;
if (num_ul_rbs > max_prb_per_packet) {
num_ul_rbs = max_prb_per_packet;
}
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
if (pkt == NULL) {
ctx->thread_safe_stats.out_of_mbufs++;
break;
}
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr);
if (use_comp) {
header_length += sizeof(struct data_section_compression_hdr);
}
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
size_t data_len = sizeof(int32_t) * num_sc;
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
if (buf == NULL) {
LOG_W(HW, "ORU: Failed to append data to mbuf (insufficient space)\n");
rte_pktmbuf_free(pkt);
break;
}
if (num_mbufs == (MAX_MBUFS_PER_SYMBOL - 1)) {
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
num_mbufs = 0;
}
mbufs[num_mbufs++] = pkt;
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)buf;
uint16_t ecpri_payload_size = (uint16_t)(header_length - 4 + data_len);
fill_ecpri_header(ecpri_header, &ctx->eaxcid_config, ECPRI_IQ_DATA, ecpri_payload_size, 0, aarx, ctx->pusch_seq_id[aarx]++, 0);
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
fill_data_section_header(data_section_header, num_ul_rbs, start_prb_base + rbs_sent, section_id);
void *iq_data_start;
if (use_comp) {
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
compression_header->ud_comp_hdr.ud_comp_meth = job->response_payload.comp_method;
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(job->response_payload.iq_width);
compression_header->rsrvd = 0;
iq_data_start = (void *)(compression_header + 1);
} else {
iq_data_start = (void *)(data_section_header + 1);
}
uint16_t *src = (uint16_t *)&rxdataF[(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
uint16_t *dst = (uint16_t *)iq_data_start;
for (int i = 0; i < num_sc * 2; i++) {
*dst++ = rte_cpu_to_be_16(*src++);
}
rbs_sent += num_ul_rbs;
}
if (num_mbufs > 0) {
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
}
}
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
{
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
if (ctx == NULL)
return;
int numerology = ctx->numerology;
int num_symbols_per_frame = NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << numerology) * NR_SYMBOLS_PER_SLOT;
uint32_t current_symbol_in_frame = ctx->current_absolute_symbol % num_symbols_per_frame;
@@ -850,135 +1078,56 @@ void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int sl
diff -= num_symbols_per_frame;
}
uint64_t target_absolute_symbol = ctx->current_absolute_symbol + diff;
bool is_ul_symbol = test_bit(ctx->ul_symbol_bitmask, target_absolute_symbol % ctx->symbol_bitmask_length);
if (!is_ul_symbol) {
ctx->thread_safe_stats.ul_tdd_mismatch++;
return;
}
ul_symbol_job_t *job = &ctx->ul_symbol_jobs[target_absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS];
if (job->state == SYM_UL_IDLE) {
ctx->thread_safe_stats.ul_cplane_missing++;
return;
}
int mu = ctx->numerology;
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
uint32_t num_mbufs = 0;
for (int aarx = 0; aarx < nb_rx; aarx++) {
if (!job->per_antenna[aarx].cplane_received) {
ctx->thread_safe_stats.ul_cplane_missing++;
continue;
}
int section_id = job->per_antenna[aarx].section_id;
int total_ul_rbs = job->per_antenna[aarx].num_prb;
int start_prb_base = job->per_antenna[aarx].start_prb;
int rbs_sent = 0;
size_t overhead = sizeof(struct rte_ether_hdr) + sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
int max_prb_per_packet = (int)((ctx->mtu - overhead) / (NR_NB_SC_PER_RB * sizeof(int32_t)));
while (rbs_sent < total_ul_rbs) {
int num_ul_rbs = total_ul_rbs - rbs_sent;
if (num_ul_rbs > max_prb_per_packet) {
num_ul_rbs = max_prb_per_packet;
}
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
if (pkt == NULL) {
ctx->thread_safe_stats.out_of_mbufs++;
break;
}
size_t header_length = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr) + sizeof(struct data_section_hdr)
+ sizeof(struct data_section_compression_hdr);
const uint num_sc = num_ul_rbs * NR_NB_SC_PER_RB;
size_t data_len = sizeof(int32_t) * num_sc;
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
if (buf == NULL) {
LOG_W(HW, "ORU: Failed to append data to mbuf (insufficient space)\n");
rte_pktmbuf_free(pkt);
// Do not increment rbs_sent, just break and let the next symbol or antenna be processed,
// or we could reduce max_prb_per_packet. Breaking is safer to prevent infinite loop.
break;
}
if (num_mbufs == (MAX_MBUFS_PER_SYMBOL - 1)) {
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
num_mbufs = 0;
}
mbufs[num_mbufs++] = pkt;
struct xran_ecpri_hdr *ecpri_header = (struct xran_ecpri_hdr *)buf;
// eCPRI payload size excludes the 4-byte common header
uint16_t ecpri_payload_size = (uint16_t)(header_length - 4 + data_len);
fill_ecpri_header(ecpri_header,
&ctx->eaxcid_config,
ECPRI_IQ_DATA,
ecpri_payload_size,
0,
aarx,
ctx->pusch_seq_id[aarx]++,
0);
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
fill_radio_app_header(radio_app_header, 0, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
fill_data_section_header(data_section_header, num_ul_rbs, start_prb_base + rbs_sent, section_id);
struct data_section_compression_hdr *compression_header = (struct data_section_compression_hdr *)(data_section_header + 1);
compression_header->ud_comp_hdr.ud_comp_meth = 0;
compression_header->ud_comp_hdr.ud_iq_width = XRAN_CONVERT_IQWIDTH(16);
compression_header->rsrvd = 0;
void *iq_data_start = (void *)(compression_header + 1);
uint16_t *src = (uint16_t *)&txdataF[aarx][(start_prb_base + rbs_sent) * NR_NB_SC_PER_RB];
uint16_t *dst = (uint16_t *)iq_data_start;
for (int i = 0; i < num_sc * 2; i++) {
*dst++ = rte_cpu_to_be_16(*src++);
}
rbs_sent += num_ul_rbs;
}
}
ctx->send_func(ctx->io_controller, mbufs, num_mbufs);
ctx->thread_safe_stats.total_uplane_sent += num_mbufs;
for (int i = 0; i < MAX_ANTENNAS; i++) {
job->per_antenna[i].cplane_received = false;
}
job->state = SYM_UL_IDLE;
}
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol)
{
oru_packet_processor_context_t *ctx = (oru_packet_processor_context_t *)context;
if (ctx == NULL)
return;
int mu = ctx->numerology;
uint64_t absolute_symbol = (frame * NR_NUMBER_OF_SUBFRAMES_PER_FRAME * (1 << mu) + slot_in_frame) * NR_SYMBOLS_PER_SLOT + symbol;
uint32_t job_index = absolute_symbol % NUM_CONCURRENT_UL_SYMBOL_WINDOWS;
prach_symbol_job_t *job = &ctx->prach_jobs[job_index];
if (job->absolute_symbol != absolute_symbol)
return;
struct rte_mbuf *mbufs[MAX_MBUFS_PER_SYMBOL];
uint32_t num_mbufs = 0;
for (int aarx = 0; aarx < nb_rx; aarx++) {
if (!job->per_antenna[aarx].cplane_received)
if (slot_in_frame < 0 || slot_in_frame >= MAX_SLOTS_PER_FRAME) {
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Invalid slot_in_frame %d\n", slot_in_frame); });
continue;
}
prach_job_t *job = &ctx->prach_jobs[slot_in_frame][aarx];
if (!job->active || target_absolute_symbol < job->start_absolute_symbol
|| target_absolute_symbol >= job->start_absolute_symbol + job->num_symbols) {
ctx->thread_safe_stats.prach_cplane_missing++;
if (!job->active) {
ctx->thread_safe_stats.prach_cplane_missing_inactive++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT,
{ LOG_W(HW, "PRACH UP: Missing C-Plane - Inactive job for slot %d, aarx %d\n", slot_in_frame, aarx); });
} else if (target_absolute_symbol < job->start_absolute_symbol) {
ctx->thread_safe_stats.prach_cplane_missing_early++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
LOG_W(HW,
"PRACH UP: Missing C-Plane - Early symbol %lu (job start %lu) for slot %d, aarx %d\n",
target_absolute_symbol,
job->start_absolute_symbol,
slot_in_frame,
aarx);
});
} else {
ctx->thread_safe_stats.prach_cplane_missing_stale++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, {
LOG_W(HW,
"PRACH UP: Missing C-Plane - Stale symbol %lu (job end %lu) for slot %d, aarx %d\n",
target_absolute_symbol,
job->start_absolute_symbol + job->num_symbols,
slot_in_frame,
aarx);
});
}
continue;
}
int section_id = job->per_antenna[aarx].section_id;
int num_ul_rbs = job->per_antenna[aarx].num_prb;
int start_prb = job->per_antenna[aarx].start_prb;
int filter_id = job->per_antenna[aarx].filter_id;
int section_id = job->section_id;
int num_ul_rbs = job->num_prb;
int start_prb = job->start_prb;
int filter_id = job->filter_id;
struct rte_mbuf *pkt = ctx->alloc_func(ctx->io_controller);
if (pkt == NULL) {
ctx->thread_safe_stats.prach_out_of_mbufs++;
ctx->thread_safe_stats.out_of_mbufs++;
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to allocate mbuf\n"); });
continue;
}
@@ -988,7 +1137,10 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
char *buf = rte_pktmbuf_append(pkt, (uint16_t)(header_length + data_len));
if (buf == NULL) {
ctx->thread_safe_stats.prach_out_of_mbufs++;
ctx->thread_safe_stats.out_of_mbufs++;
rte_pktmbuf_free(pkt);
RATELIMIT(PRACH_ERR_LOG_RATELIMIT, { LOG_W(HW, "PRACH UP: Failed to append data to mbuf\n"); });
continue;
}
@@ -1007,7 +1159,7 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
0);
struct radio_app_common_hdr *radio_app_header = (struct radio_app_common_hdr *)(ecpri_header + 1);
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, mu);
fill_radio_app_header(radio_app_header, filter_id, XRAN_DIR_UL, frame, slot_in_frame, symbol, numerology);
struct data_section_hdr *data_section_header = (struct data_section_hdr *)(radio_app_header + 1);
fill_data_section_header(data_section_header, num_ul_rbs, start_prb, section_id);
@@ -1018,8 +1170,6 @@ void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int
for (int i = 0; i < prach_length * 2; i++) {
*dst++ = rte_cpu_to_be_16(*src++);
}
job->per_antenna[aarx].cplane_received = false;
}
if (num_mbufs > 0) {

View File

@@ -2,6 +2,8 @@
* SPDX-License-Identifier: LicenseRef-CSSL-1.0
*/
#pragma once
#include <stdint.h>
#include <stddef.h>
@@ -17,8 +19,30 @@ typedef struct {
uint64_t count;
} txrx_histogram_t;
typedef struct {
int section_id;
int comp_method;
int iq_width;
} opaque_response_data_t;
typedef struct {
opaque_response_data_t response_payload;
uint64_t hyper_frame;
int frame;
int slot_in_frame;
int symbol;
int num_symbols;
int antenna_id;
int start_prb;
int num_prb;
} ul_job_t;
typedef struct {
uint64_t total_cplane;
uint64_t cplane_received_dl;
uint64_t cplane_received_ul;
uint64_t cplane_received_prach;
uint64_t cplane_received_other;
uint64_t total_uplane_received;
uint64_t total_uplane_sent;
uint64_t cplane_err_hdr; // apphdr or section extraction
@@ -26,6 +50,9 @@ typedef struct {
uint64_t cplane_err_early;
uint64_t cplane_err_late;
uint64_t cplane_err_dup; // duplicate cplane
uint64_t cplane_err_dup_dl;
uint64_t cplane_err_dup_ul;
uint64_t cplane_err_dup_prach;
uint64_t uplane_err_late;
uint64_t uplane_err_early;
uint64_t uplane_err_dup;
@@ -35,10 +62,19 @@ typedef struct {
uint64_t ul_tdd_mismatch;
uint64_t out_of_mbufs;
uint64_t ul_cplane_missing;
uint64_t prach_cplane_missing;
uint64_t prach_cplane_missing_ant;
uint64_t prach_cplane_missing_inactive;
uint64_t prach_cplane_missing_stale;
uint64_t prach_cplane_missing_early;
uint64_t prach_out_of_mbufs;
uint64_t prach_jobs_pool_exhausted;
txrx_histogram_t dl_uplane_hist;
txrx_histogram_t dl_cplane_hist;
txrx_histogram_t ul_cplane_hist;
txrx_histogram_t prach_cplane_hist;
int64_t ul_uplane_ota_delay_sum;
uint64_t ul_uplane_ota_delay_count;
} oru_packet_processor_stats_t;
typedef void *(*alloc_func_t)(void *io_controller);
@@ -60,7 +96,7 @@ void *init_packet_processor(int numerology,
void *io_controller,
size_t mtu,
int prach_eaxc_offset);
void write_ul_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
void write_ul_iq(void *context, uint32_t *rxdataF, int symbol, const ul_job_t *job);
void write_prach_iq(void *context, uint32_t **txdataF, int nb_rx, int frame, int slot_in_frame, int symbol);
void cleanup_packet_processor(void *context);
void handle_absolute_symbol_tick(void *context, uint64_t absolute_symbol);
@@ -68,8 +104,9 @@ void handle_uplane_packet(void *context, void *pkt);
void handle_cplane_packet(void *context, void *pkt);
void print_packet_processor_stats(void *context);
void get_packet_processor_stats(void *context, oru_packet_processor_stats_t *out_stats);
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, int *frame, int *slot, int *symbol);
void read_dl_iq(void *context, uint32_t **txdataF, int nb_tx, uint64_t *hyper_frame, int *frame, int *slot, int *symbol);
int get_ready_job_count(void *context);
int poll_ul_job(void *context, ul_job_t *job);
#ifdef __cplusplus
}

View File

@@ -25,10 +25,10 @@ target_link_libraries(test_oru_pcap PRIVATE oru_packet_processor log_headers ${d
target_include_directories(test_oru_pcap PRIVATE ${dpdk_INCLUDE_DIRS} ${PCAP_INCLUDE_DIRS})
file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/run_oru_pcap_test.sh DESTINATION ${CMAKE_CURRENT_BINARY_DIR})
add_test(NAME test_oru_pcap_1 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 9600 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_1")
add_test(NAME test_oru_pcap_1 COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://github.com/bpodrygajlo/openairinterface5g/releases/download/fhi72-pcaps/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 9600 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_1")
set_property(TEST test_oru_pcap_1 PROPERTY LABELS fronthaul)
add_test(NAME test_oru_pcap_frag COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://gitlab.eurecom.fr/api/v4/projects/7026/packages/generic/fhi72-pcaps/1.0.0/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 1500 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_frag")
add_test(NAME test_oru_pcap_frag COMMAND ${CMAKE_CURRENT_BINARY_DIR}/run_oru_pcap_test.sh https://github.com/bpodrygajlo/openairinterface5g/releases/download/fhi72-pcaps/capture-oai-odu-4x4-273prb-truncated.pcap.xz $<TARGET_FILE:test_oru_pcap> 1090 3 1 6 4 5 1500 4 -- ${DPDK_TEST_ARGS} "--file-prefix=test_oru_pcap_frag")
set_property(TEST test_oru_pcap_frag PROPERTY LABELS fronthaul)
add_dependencies(tests test_oru_pcap)

View File

@@ -11,6 +11,7 @@
#include <unistd.h>
#include "common/config/config_userapi.h"
#include <rte_eal.h>
#include <assert.h>
// OAI Linkage Satisfiers
void exit_function(const char *file, const char *function, const int line, const char *s, const int assertflag)
@@ -84,20 +85,32 @@ int main(int argc, char **argv)
return 1;
}
// Testing UTC Anchor Point and Hyper-frame
printf("Testing UTC Anchor Point and Hyper-frame...\n");
uint64_t hf;
uint32_t f, s;
struct timespec ts;
if (oru_fh_get_utc_anchor_point(handle, &hf, &f, &s, &ts) < 0) {
fprintf(stderr, "FAIL: oru_fh_get_utc_anchor_point failed\n");
oru_fh_cleanup(handle);
return 1;
}
printf("UTC Anchor Point: hf=%lu, f=%u, s=%u, ts=%ld.%09ld\n", hf, f, s, ts.tv_sec, ts.tv_nsec);
assert(f < 1024);
assert(s < (10 << cfg.numerology));
printf("Running live loop for 2 seconds...\n");
uint32_t *txData[1];
txData[0] = malloc(273 * 12 * sizeof(uint32_t));
for (int i = 0; i < 2000; i++) {
if (i % 400 == 0) {
oru_fh_rx_send_pusch(handle, txData, 1, 0, i / 400, 0);
}
uint64_t start_cycles = rte_get_timer_cycles();
uint64_t target_cycles = start_cycles + (rte_get_timer_hz() / 1000);
while (rte_get_timer_cycles() < target_cycles) {
int f, s, sym;
uint64_t hf;
while (oru_fh_get_ready_jobs(handle) > 0) {
oru_fh_tx_read_symbol(handle, txData, 1, &f, &s, &sym);
oru_fh_tx_read_symbol(handle, txData, 1, &hf, &f, &s, &sym);
}
}
}

View File

@@ -235,8 +235,9 @@ void test_cplane_uplane_match()
txdataF[0] = output_iq;
int frame, slot, symbol;
uint64_t hyper_frame;
do {
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
assert(symbol == target_sym % 14);
@@ -343,8 +344,9 @@ void test_frame_wrap_around()
txdataF[0] = output_iq;
int frame, slot, symbol;
uint64_t hyper_frame;
do {
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
assert(frame == (target_sym / num_symbols_per_frame) % 1024);
@@ -456,9 +458,10 @@ void test_cplane_14_symbols()
for (int i = 0; i < 14; i++) {
int frame, slot, symbol;
uint64_t hyper_frame;
uint64_t sym_i = target_sym + i;
do {
read_dl_iq(ctx, txdataF, 1, &frame, &slot, &symbol);
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
} while (!(frame == (sym_i / num_symbols_per_frame) % 1024 && symbol == sym_i % 14));
assert(frame == (sym_i / num_symbols_per_frame) % 1024);
@@ -566,8 +569,9 @@ void test_other_bw_4ant_prb_offset()
txdataF[3] = out_iq3;
int frame, slot, symbol;
uint64_t hyper_frame;
do {
read_dl_iq(ctx, txdataF, 4, &frame, &slot, &symbol);
read_dl_iq(ctx, txdataF, 4, &hyper_frame, &frame, &slot, &symbol);
} while (!(frame == frameId && symbol == startSymbolId));
// Verify memory contents for each antenna
@@ -611,7 +615,7 @@ void test_send_mbuf_no_frag(void *io_controller, struct rte_mbuf **mbufs, uint32
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
+ sizeof(struct data_section_hdr);
// In this test, we expect exactly 20 PRBs in a single packet
assert(sent_sec->fields.num_prbu == (uint8_t)XRAN_CONVERT_NUMPRBC(20));
@@ -659,7 +663,7 @@ void test_send_mbuf_frag(void *io_controller, struct rte_mbuf **mbufs, uint32_t
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
+ sizeof(struct data_section_hdr);
if (g_packets_sent == 1) {
// First fragment: 30 PRBs
@@ -688,7 +692,7 @@ void test_send_mbuf_large_mtu(void *io_controller, struct rte_mbuf **mbufs, uint
sent_sec->fields.all_bits = rte_be_to_cpu_32(sent_sec->fields.all_bits);
size_t expected_header_len = sizeof(struct xran_ecpri_hdr) + sizeof(struct radio_app_common_hdr)
+ sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr);
+ sizeof(struct data_section_hdr);
// With MTU 9600, 100 PRBs should fit in a single packet
assert(g_packets_sent == 1);
@@ -721,7 +725,7 @@ void test_send_mbuf_prb_offset(void *io_controller, struct rte_mbuf **mbufs, uin
// Check data: should match the offset in txdataF
uint32_t *iq =
(uint32_t *)((uint8_t *)sent_sec + sizeof(struct data_section_hdr) + sizeof(struct data_section_compression_hdr));
(uint32_t *)((uint8_t *)sent_sec + sizeof(struct data_section_hdr));
assert(rte_be_to_cpu_32(iq[0]) == 0x10101010);
rte_pktmbuf_free(mbuf);
@@ -779,15 +783,20 @@ void test_uplink_basic()
handle_cplane_packet(ctx, c_mbuf);
// 2. Call write_ul_iq
uint32_t *txdataF[1];
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
ul_job_t job;
int poll_ret = poll_ul_job(ctx, &job);
assert(poll_ret == 0);
assert(job.num_prb == 20);
assert(job.start_prb == 0);
assert(job.antenna_id == 0);
uint32_t iq_input[100 * 12];
for (int i = 0; i < 100 * 12; i++) {
iq_input[i] = 0x11223344;
}
txdataF[0] = iq_input;
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
write_ul_iq(ctx, iq_input, startSymbolId, &job);
oru_packet_processor_stats_t stats;
get_packet_processor_stats(ctx, &stats);
@@ -850,15 +859,20 @@ void test_uplink_fragmentation()
handle_cplane_packet(ctx, c_mbuf);
// 2. Call write_ul_iq
uint32_t *txdataF[1];
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
ul_job_t job;
int poll_ret = poll_ul_job(ctx, &job);
assert(poll_ret == 0);
assert(job.num_prb == 40);
assert(job.start_prb == 0);
assert(job.antenna_id == 0);
uint32_t iq_input[100 * 12];
for (int i = 0; i < 100 * 12; i++) {
iq_input[i] = 0x11223344;
}
txdataF[0] = iq_input;
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
write_ul_iq(ctx, iq_input, startSymbolId, &job);
oru_packet_processor_stats_t stats;
get_packet_processor_stats(ctx, &stats);
@@ -936,15 +950,20 @@ void test_uplink_large_mtu()
handle_cplane_packet(ctx, c_mbuf);
// 2. Call write_ul_iq
uint32_t *txdataF[1];
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
ul_job_t job;
int poll_ret = poll_ul_job(ctx, &job);
assert(poll_ret == 0);
assert(job.num_prb == 100);
assert(job.start_prb == 0);
assert(job.antenna_id == 0);
uint32_t iq_input[num_prb * 12];
for (int i = 0; i < num_prb * 12; i++) {
iq_input[i] = 0x55667788;
}
txdataF[0] = iq_input;
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
write_ul_iq(ctx, iq_input, startSymbolId, &job);
oru_packet_processor_stats_t stats;
get_packet_processor_stats(ctx, &stats);
@@ -1021,14 +1040,19 @@ void test_uplink_prb_offset()
handle_cplane_packet(ctx, c_mbuf);
// 2. Call write_ul_iq
uint32_t *txdataF[1];
// 2. Poll the UL job created by handle_cplane_packet and call write_ul_iq
ul_job_t job;
int poll_ret = poll_ul_job(ctx, &job);
assert(poll_ret == 0);
assert(job.num_prb == 20);
assert(job.start_prb == 10);
assert(job.antenna_id == 0);
uint32_t iq_input[100 * 12];
memset(iq_input, 0, sizeof(iq_input));
iq_input[10 * 12] = 0x10101010; // Mark PRB 10
txdataF[0] = iq_input;
write_ul_iq(ctx, txdataF, 1, frameId, slot_in_frame, startSymbolId);
write_ul_iq(ctx, iq_input, startSymbolId, &job);
oru_packet_processor_stats_t stats;
get_packet_processor_stats(ctx, &stats);
@@ -1129,6 +1153,102 @@ void test_prach_generation()
printf("PRACH generation passed!\n");
}
void test_hyper_frame_calculation()
{
printf("Testing hyper-frame calculation...\n");
int mu = 1; // 30kHz
int slots_per_subframe = 1 << mu;
void *ctx = init_packet_processor(mu, 273, 200, 400, 100, 300, 2, 2, 0, 0, 5, test_alloc_mbuf, test_send_mbuf, NULL, 1500, 0);
assert(ctx != NULL);
int num_symbols_per_frame = 10 * slots_per_subframe * 14; // 280
// One hyper-frame has 1024 frames. So 1024 * 280 = 286720 symbols.
// Target absolute symbol index: 3 * 286720 + 5 * 280 + 1 * 14 + 7 = 860160 + 1400 + 14 + 7 = 861581.
uint64_t target_sym = 861581;
uint64_t current_sym = target_sym - 7;
handle_absolute_symbol_tick(ctx, current_sym);
// 1. Send C-plane packet for target_sym
struct rte_mbuf *c_mbuf = rte_pktmbuf_alloc(mp);
struct xran_ecpri_hdr *ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_ecpri_hdr));
ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
struct xran_cp_radioapp_section1_header *apphdr =
(struct xran_cp_radioapp_section1_header *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1_header));
memset(apphdr, 0, sizeof(*apphdr));
apphdr->cmnhdr.field.dataDirection = XRAN_DIR_DL;
apphdr->cmnhdr.field.payloadVer = XRAN_PAYLOAD_VER;
apphdr->cmnhdr.field.frameId = (target_sym / num_symbols_per_frame) % 256;
int slot_in_frame = (target_sym % num_symbols_per_frame) / 14;
apphdr->cmnhdr.field.subframeId = slot_in_frame / slots_per_subframe;
apphdr->cmnhdr.field.slotId = slot_in_frame % slots_per_subframe;
apphdr->cmnhdr.field.startSymbolId = target_sym % 14;
apphdr->cmnhdr.sectionType = XRAN_CP_SECTIONTYPE_1;
apphdr->cmnhdr.field.all_bits = rte_cpu_to_be_32(apphdr->cmnhdr.field.all_bits);
struct xran_cp_radioapp_section1 *sec =
(struct xran_cp_radioapp_section1 *)rte_pktmbuf_append(c_mbuf, sizeof(struct xran_cp_radioapp_section1));
memset(sec, 0, sizeof(*sec));
sec->hdr.u.s1.numSymbol = 1;
sec->hdr.u1.common.numPrbc = 1;
*((uint64_t *)sec) = rte_be_to_cpu_64(*((uint64_t *)sec));
handle_cplane_packet(ctx, c_mbuf);
// 2. Send U-plane packet for target_sym
current_sym += 3;
handle_absolute_symbol_tick(ctx, current_sym);
struct rte_mbuf *u_mbuf = rte_pktmbuf_alloc(mp);
struct xran_ecpri_hdr *u_ecpri = (struct xran_ecpri_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct xran_ecpri_hdr));
u_ecpri->ecpri_xtc_id = xran_compose_cid(&g_eaxcid_config, 0, 0, 0, 0);
struct radio_app_common_hdr *u_app =
(struct radio_app_common_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct radio_app_common_hdr));
u_app->frame_id = (target_sym / num_symbols_per_frame) % 256;
u_app->sf_slot_sym.subframe_id = slot_in_frame / slots_per_subframe;
u_app->sf_slot_sym.slot_id = slot_in_frame % slots_per_subframe;
u_app->sf_slot_sym.symb_id = target_sym % 14;
u_app->sf_slot_sym.value = rte_cpu_to_be_16(u_app->sf_slot_sym.value);
struct data_section_hdr *u_data = (struct data_section_hdr *)rte_pktmbuf_append(u_mbuf, sizeof(struct data_section_hdr));
u_data->fields.num_prbu = 1;
u_data->fields.start_prbu = 0;
u_data->fields.sect_id = 0;
u_data->fields.all_bits = rte_cpu_to_be_32(u_data->fields.all_bits);
// IQ Data
uint16_t *iq = (uint16_t *)rte_pktmbuf_append(u_mbuf, 1 * 12 * 4);
assert(iq != NULL);
iq[0] = 0xAAAA;
handle_uplane_packet(ctx, u_mbuf);
// 3. Advance to trigger window expiry and job completion
current_sym += 10;
handle_absolute_symbol_tick(ctx, current_sym);
uint32_t *txdataF[1] = {0};
uint32_t output_iq[273 * 12] = {0};
txdataF[0] = output_iq;
int frame, slot, symbol;
uint64_t hyper_frame = 0xFFFFFFFF;
do {
read_dl_iq(ctx, txdataF, 1, &hyper_frame, &frame, &slot, &symbol);
} while (!(frame == (target_sym / num_symbols_per_frame) % 1024 && symbol == target_sym % 14));
assert(hyper_frame == 3);
assert(frame == 5);
assert(slot == 1);
assert(symbol == 7);
cleanup_packet_processor(ctx);
printf("Hyper-frame calculation test passed!\n");
}
int main(int argc, char **argv)
{
setup_dpdk(argc, argv);
@@ -1155,6 +1275,8 @@ int main(int argc, char **argv)
usleep(10000);
test_prach_generation();
usleep(10000);
test_hyper_frame_calculation();
usleep(10000);
printf("All tests passed!\n");
return 0;

View File

@@ -197,16 +197,23 @@ int main(int argc, char *argv[])
// For UL verification, we call write_ul_iq for every symbol.
// It will internally check TDD and UL C-plane presence.
ul_job_t job;
int poll_ret = poll_ul_job(ctx, &job);
if (poll_ret == 0) {
for (int i = 0; i < job.num_symbols; i++) {
write_ul_iq(ctx, txdataF[0], job.symbol + i, &job);
}
}
int frame = (s / 280) % 1024;
int slot = (s / 14) % 20;
int sym = s % 14;
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
// Drain ready jobs to prevent ring overflow
int f, sl, sy;
uint64_t hf;
while (get_ready_job_count(ctx) > 0) {
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
}
}
last_tick_sym = current_sym;
@@ -230,16 +237,11 @@ int main(int argc, char *argv[])
// Flush remaining symbols
for (int i = 0; i < 100; i++) {
handle_absolute_symbol_tick(ctx, ++last_tick_sym);
int frame = (last_tick_sym / 280) % 1024;
int slot = (last_tick_sym / 14) % 20;
int sym = last_tick_sym % 14;
write_ul_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
write_prach_iq(ctx, txdataF, MAX_ANTENNAS, frame, slot, sym);
// Drain ready jobs to prevent ring overflow during flush
int f, sl, sy;
uint64_t hf;
while (get_ready_job_count(ctx) > 0) {
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &f, &sl, &sy);
read_dl_iq(ctx, txdataF, MAX_ANTENNAS, &hf, &f, &sl, &sy);
}
}

View File

@@ -1747,10 +1747,13 @@ void stop_nr_nfapi_vnf()
}
}
void configure_nr_nfapi_vnf(eth_params_t params)
void configure_nr_nfapi_vnf(const char *vnf_addr, uint16_t vnf_p5_port, uint16_t vnf_p7_port)
{
#ifndef ENABLE_AERIAL
nfapi_setmode(NFAPI_MODE_VNF);
#else
UNUSED(vnf_addr);
UNUSED(vnf_p7_port);
#endif
vnf_info *vnf = calloc(1, sizeof(vnf_info));
memset(vnf->p7_vnfs, 0, sizeof(vnf->p7_vnfs));
@@ -1759,22 +1762,21 @@ void configure_nr_nfapi_vnf(eth_params_t params)
vnf->p7_vnfs[0].aperiodic_timing_enabled = 0;
vnf->p7_vnfs[0].periodic_timing_period = 1;
vnf->p7_vnfs[0].config = nfapi_vnf_p7_config_create();
AssertFatal(params.remote_portc == 0 && params.remote_portd == 0, "remote ports not used, use 0\n");
#ifndef ENABLE_AERIAL
NFAPI_TRACE(NFAPI_TRACE_INFO,
"[VNF] %s() vnf->p7_vnfs[0].config:%p VNF ADDRESS:%s:%d\n",
__FUNCTION__,
vnf->p7_vnfs[0].config,
params.my_addr,
params.my_portc);
strcpy(vnf->p7_vnfs[0].local_addr, params.my_addr);
vnf->p7_vnfs[0].local_port = params.my_portd;
vnf_addr,
vnf_p5_port);
strcpy(vnf->p7_vnfs[0].local_addr, vnf_addr);
vnf->p7_vnfs[0].local_port = vnf_p7_port;
#endif
vnf->p7_vnfs[0].mac = malloc(sizeof(mac_t));
config = nfapi_vnf_config_create();
config->malloc = malloc;
config->free = free;
config->vnf_p5_port = params.my_portc;
config->vnf_p5_port = vnf_p5_port;
config->vnf_ipv4 = 1;
config->vnf_ipv6 = 0;
config->pnf_list = 0;

View File

@@ -4,7 +4,6 @@
#ifndef NFAPI_VNF_H_
#define NFAPI_VNF_H_
#include <common_lib.h>
#include "nfapi_vnf_interface.h"
typedef struct {
@@ -130,7 +129,7 @@ typedef struct {
} vnf_info;
void configure_nfapi_vnf(char *vnf_addr, int vnf_p5_port, char *pnf_ip_addr, int pnf_p7_port, int vnf_p7_port);
void configure_nr_nfapi_vnf(eth_params_t params);
void configure_nr_nfapi_vnf(const char *vnf_addr, uint16_t vnf_p5_port, uint16_t vnf_p7_port);
void stop_nr_nfapi_vnf();
nfapi_vnf_config_t *get_config();
vnf_p7_t *get_p7_vnf();

View File

@@ -463,6 +463,7 @@ typedef struct {
uint8_t power_control_offset; // Ratio of PDSCH EPRE to NZP CSI-RSEPRE [3GPP TS 38.214, sec 5.2.2.3.1], Value: 0->23 representing -8 to 15 dB in 1dB steps; 255: L1 is configured with ProfileSSS
uint8_t power_control_offset_ss; // Ratio of NZP CSI-RS EPRE to SSB/PBCH block EPRE [3GPP TS 38.214, sec 5.2.2.3.1], Values: 0: -3dB; 1: 0dB; 2: 3dB; 3: 6dB; 255: L1 is configured with ProfileSSS
uint8_t measurement_bitmap; // bit 0 RSRP, bit 1 RI, bit 2 LI, bit 3 PMI, bit 4 CQI, bit 5 i1
uint8_t last_trs_slot; // indicates to PHY if the slot is end of TRS burst
} fapi_nr_dl_config_csirs_pdu_rel15_t;
typedef enum{vrb_to_prb_mapping_non_interleaved = 0, vrb_to_prb_mapping_interleaved = 1} vrb_to_prb_mapping_t;

View File

@@ -67,7 +67,7 @@ int nr_get_ssb_start_symbol(const NR_DL_FRAME_PARMS *fp, uint8_t i_ssb)
return symbol;
}
static void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
void set_scs_parameters(NR_DL_FRAME_PARMS *fp, int mu, int N_RB_DL, int ssb_case)
{
switch(mu) {
case NR_MU_0: //15kHz scs

View File

@@ -14,7 +14,6 @@
#include "PHY/MODULATION/modulation_UE.h"
#include "PHY/LTE_ESTIMATION/lte_estimation.h"
#include "PHY/LTE_REFSIG/lte_refsig.h"
#include "common_lib.h"
#include "PHY/INIT/phy_init.h"
extern openair0_config_t openair0_cfg[];

View File

@@ -7,8 +7,10 @@
#include "PHY/NR_REFSIG/nr_mod_table.h"
#include "executables/softmodem-common.h"
#include <simde/x86/avx512.h>
// Lacking declaration in present simde external package, will be detected as compilation error when they will add it
// Lacking declaration in older implementations of simde external package, so let's keep it for now to be backwards compatible
#if !defined(simde_mm512_extracti64x2_epi64)
#define simde_mm512_extracti64x2_epi64(a...) _mm512_extracti64x2_epi64(a)
#endif
// #define DEBUG_DLSCH_PRECODING_PRINT_WITH_TRIVIAL // TODO: For debug, to be removed if want to merge to develop
// #define DEBUG_LAYER_MAPPING

View File

@@ -235,9 +235,11 @@ int nr_dlsch_encoding(PHY_VARS_gNB *gNB,
for (int r = 0; r < TB_parameters->C; r++) {
nrLDPC_segment_encoding_parameters_t *segment_parameters = &TB_parameters->segments[r];
int E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, rel15->nrOfLayers, r);
if (E < 0)
return -1;
segment_parameters->c = dlsch->c[r];
segment_parameters->E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, rel15->nrOfLayers, r);
segment_parameters->E = E;
reset_meas(&segment_parameters->ts_interleave);
reset_meas(&segment_parameters->ts_rate_match);
reset_meas(&segment_parameters->ts_ldpc_encode);

View File

@@ -12,6 +12,202 @@
#include "PHY/NR_TRANSPORT/nr_transport_common_proto.h"
#include "openair1/PHY/NR_TRANSPORT/nr_prach.h"
typedef struct {
int reps;
int Ncp;
int dftlen;
int N_ZC;
int k;
dft_size_idx_t dftsize;
int sample_offset_slot;
} prach_ru_params_t;
static prach_ru_params_t get_prach_ru_params(prach_item_t *p,
int prachStartSymbol,
NR_DL_FRAME_PARMS *fp)
{
prach_ru_params_t par = {0};
const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples;
const int sum0 = fp->ofdm_symbol_size + fp->nb_prefix_samples0;
if (prachStartSymbol == 0) {
par.sample_offset_slot = 0;
} else if (fp->slots_per_subframe == 1) {
if (prachStartSymbol <= 7)
par.sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
else
par.sample_offset_slot = sum * (prachStartSymbol - 2) + sum0 * 2;
} else {
if (!(p->slot % (fp->slots_per_subframe / 2)))
par.sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
else
par.sample_offset_slot = sum * prachStartSymbol;
}
int mu = p->numerology_index;
if (p->prach_sequence_length == 0) {
switch (p->pdu.prach_format) {
case 0:
par.reps = 1;
par.Ncp = 3168;
par.dftlen = 24576;
break;
case 1:
par.reps = 2;
par.Ncp = 21024;
par.dftlen = 24576;
break;
case 2:
par.reps = 4;
par.Ncp = 4688;
par.dftlen = 24576;
break;
case 3:
par.reps = 4;
par.Ncp = 3168;
par.dftlen = 6144;
break;
default:
AssertFatal(1 == 0, "Illegal prach format %d for length 839\n", p->pdu.prach_format);
break;
}
} else {
switch (p->pdu.prach_format) {
case 4: // A1
par.reps = 2;
par.Ncp = 288 >> mu;
break;
case 5: // A2
par.reps = 4;
par.Ncp = 576 >> mu;
break;
case 6: // A3
par.reps = 6;
par.Ncp = 864 >> mu;
break;
case 7: // B1
par.reps = 2;
par.Ncp = 216 >> mu;
break;
case 8: // B4
par.reps = 12;
par.Ncp = 936 >> mu;
break;
case 9: // C0
par.reps = 1;
par.Ncp = 1240 >> mu;
break;
case 10: // C2
par.reps = 4;
par.Ncp = 2048 >> mu;
break;
default:
AssertFatal(1 == 0, "unknown prach format %x\n", p->pdu.prach_format);
break;
}
par.dftlen = 2048 >> mu;
}
if (p->numerology_index == 0) {
if (prachStartSymbol == 0 || prachStartSymbol == 7)
par.Ncp += 16;
} else {
if (p->slot % (fp->slots_per_subframe / 2) == 0 && prachStartSymbol == 0)
par.Ncp += 16;
}
switch(fp->samples_per_subframe) {
case 7680:
// 5 MHz @ 7.68 Ms/s
par.Ncp >>= 2;
par.dftlen >>= 2;
break;
case 15360:
// 10, 15 MHz @ 15.36 Ms/s
par.Ncp >>= 1;
par.dftlen >>= 1;
break;
case 23040:
// 20 MHz @ 23.04 Ms/s
par.Ncp = (par.Ncp * 3) / 4;
par.dftlen = (par.dftlen * 3) / 4;
break;
case 30720:
// 20, 25, 30 MHz @ 30.72 Ms/s
break;
case 46080:
// 40 MHz @ 46.08 Ms/s
par.Ncp = (par.Ncp*3)/2;
par.dftlen = (par.dftlen*3)/2;
break;
case 61440:
// 40, 50, 60 MHz @ 61.44 Ms/s
par.Ncp <<= 1;
par.dftlen <<= 1;
break;
case 92160:
// 50, 60, 70, 80, 90 MHz @ 92.16 Ms/s
par.Ncp *= 3;
par.dftlen *= 3;
break;
case 122880:
// 70, 80, 90, 100 MHz @ 122.88 Ms/s
par.Ncp <<= 2;
par.dftlen <<= 2;
break;
case 184320:
// 100 MHz @ 184.32 Ms/s
par.Ncp = par.Ncp*6;
par.dftlen = par.dftlen*6;
break;
case 245760:
// 200 MHz @ 245.76 Ms/s
par.Ncp <<= 3;
par.dftlen <<= 3;
break;
default:
AssertFatal(1==0,"sample rate %f MHz not supported for numerology %d\n", fp->samples_per_subframe / 1000.0, mu);
}
par.dftsize = get_dft(par.dftlen);
par.N_ZC = (p->prach_sequence_length == 0) ? 839 : 139;
const unsigned int K = get_prach_K(p->prach_sequence_length, p->pdu.prach_format, p->numerology_index, p->mu);
const uint8_t kbar = get_PRACH_k_bar(p->mu, p->numerology_index);
int n_ra_prb = p->msg1_frequencystart;
int k = (12*n_ra_prb) - 6*fp->N_RB_UL;
if (k<0) k+=(fp->ofdm_symbol_size);
k*=K;
k+=kbar;
par.k = k;
return par;
}
void init_nr_prach(PHY_VARS_gNB *gNB)
{
int num_prach = 16;
@@ -114,6 +310,38 @@ void nr_schedule_rx_prach(PHY_VARS_gNB *gNB, int SFN, int Slot, nfapi_nr_prach_p
LOG_W(NR_PHY, "%4d.%2d PRACH occ queue is full: dropping PRACH request\n", SFN, Slot);
}
static void rx_nr_prach_ru_internal_rep(prach_item_t *p,
int ant_offset,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset,
int rep,
const prach_ru_params_t *params,
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L])
{
AssertFatal(rep >= 0 && rep < params->reps, "rep %d is out of range (reps = %d)\n", rep, params->reps);
int slot2 = p->prach_sequence_length ? p->slot : p->slot;
int sample_offset = get_samples_slot_timestamp(fp, slot2) + params->sample_offset_slot - N_TA_offset + params->Ncp + rep * params->dftlen;
for (int aa = 0; aa < p->nb_rx; aa++) {
int idx = ant_offset + aa;
c16_t *prach2 = (c16_t *)&rxdata[idx][sample_offset];
// do DFT for the specific repetition
c16_t tmp[params->dftlen] __attribute__((aligned(32)));
dft(params->dftsize, (int16_t *)prach2, (int16_t *)tmp, 1);
// Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH)
LOG_D(PHY, "Doing PRACH combining of repetition %d/%d N_ZC %d\n", rep, params->reps, params->N_ZC);
int k2 = params->k;
for (int j = 0; j < params->N_ZC; j++, k2++) {
if (k2 == params->dftlen)
k2 = 0;
rxsigF[aa][j] = c16add(rxsigF[aa][j], tmp[k2]);
}
}
}
static void rx_nr_prach_ru_internal(prach_item_t *p,
int prachStartSymbol,
int prachOccasion,
@@ -122,229 +350,9 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
int N_TA_offset,
bool das)
{
int sample_offset_slot;
const int sum = fp->ofdm_symbol_size + fp->nb_prefix_samples;
const int sum0 = fp->ofdm_symbol_size + fp->nb_prefix_samples0;
if (prachStartSymbol == 0) {
sample_offset_slot = 0;
} else if (fp->slots_per_subframe == 1) {
if (prachStartSymbol <= 7)
sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
else
sample_offset_slot = sum * (prachStartSymbol - 2) + sum0 * 2;
} else {
if (!(p->slot % (fp->slots_per_subframe / 2)))
sample_offset_slot = sum * (prachStartSymbol - 1) + sum0;
else
sample_offset_slot = sum * prachStartSymbol;
}
LOG_D(PHY,
"frame %d, slot %d: doing rx_nr_prach_ru for format %d, numRA %d, prachStartSymbol %d, prachOccasion %d\n",
p->frame,
p->slot,
p->pdu.prach_format,
p->pdu.num_ra,
prachStartSymbol,
prachOccasion);
int reps;
int Ncp;
int dftlen;
int mu = p->numerology_index;
if (p->prach_sequence_length == 0) {
LOG_D(PHY,
"PRACH in %d.%d, format %d, msg1_frequencyStart %d\n",
p->frame,
p->slot,
p->pdu.prach_format,
p->msg1_frequencystart);
switch (p->pdu.prach_format) {
case 0:
reps = 1;
Ncp = 3168;
dftlen = 24576;
break;
case 1:
reps = 2;
Ncp = 21024;
dftlen = 24576;
break;
case 2:
reps = 4;
Ncp = 4688;
dftlen = 24576;
break;
case 3:
reps = 4;
Ncp = 3168;
dftlen = 6144;
break;
default:
AssertFatal(1 == 0, "Illegal prach format %d for length 839\n", p->pdu.prach_format);
break;
}
} else {
LOG_D(PHY,
"PRACH in %d.%d, format %s, msg1_frequencyStart %d,startSymbol %d\n",
p->frame,
p->slot,
prachfmt[p->pdu.prach_format],
p->msg1_frequencystart,
prachStartSymbol);
switch (p->pdu.prach_format) {
case 4: // A1
reps = 2;
Ncp = 288 >> mu;
break;
case 5: // A2
reps = 4;
Ncp = 576 >> mu;
break;
case 6: // A3
reps = 6;
Ncp = 864 >> mu;
break;
case 7: // B1
reps = 2;
Ncp = 216 >> mu;
break;
/*
// B2 and B3 do not exist in FAPI
case 4: //B2
reps = 4;
Ncp = 360 >> mu;
break;
case 5: //B3
reps = 6;
Ncp = 504 >> mu;
break;
*/
case 8: // B4
reps = 12;
Ncp = 936 >> mu;
break;
case 9: // C0
reps = 1;
Ncp = 1240 >> mu;
break;
case 10: // C2
reps = 4;
Ncp = 2048 >> mu;
break;
default:
AssertFatal(1 == 0, "unknown prach format %x\n", p->pdu.prach_format);
break;
}
dftlen = 2048 >> mu;
}
//actually what we should be checking here is how often the current prach crosses a 0.5ms boundary.
//I am not quite sure for which paramter set this would be the case,
//so I will ignore it for now and just check if the prach starts on a 0.5ms boundary
if (p->numerology_index == 0) {
if (prachStartSymbol == 0 || prachStartSymbol == 7)
Ncp += 16;
} else {
if (p->slot % (fp->slots_per_subframe / 2) == 0 && prachStartSymbol == 0)
Ncp += 16;
}
switch(fp->samples_per_subframe) {
case 7680:
// 5 MHz @ 7.68 Ms/s
Ncp >>= 2;
dftlen >>= 2;
break;
case 15360:
// 10, 15 MHz @ 15.36 Ms/s
Ncp >>= 1;
dftlen >>= 1;
break;
case 23040:
// 20 MHz @ 23.04 Ms/s
Ncp = (Ncp * 3) / 4;
dftlen = (dftlen * 3) / 4;
break;
case 30720:
// 20, 25, 30 MHz @ 30.72 Ms/s
break;
case 46080:
// 40 MHz @ 46.08 Ms/s
Ncp = (Ncp*3)/2;
dftlen = (dftlen*3)/2;
break;
case 61440:
// 40, 50, 60 MHz @ 61.44 Ms/s
Ncp <<= 1;
dftlen <<= 1;
break;
case 92160:
// 50, 60, 70, 80, 90 MHz @ 92.16 Ms/s
Ncp *= 3;
dftlen *= 3;
break;
case 122880:
// 70, 80, 90, 100 MHz @ 122.88 Ms/s
Ncp <<= 2;
dftlen <<= 2;
break;
case 184320:
// 100 MHz @ 184.32 Ms/s
Ncp = Ncp*6;
dftlen = dftlen*6;
break;
case 245760:
// 200 MHz @ 245.76 Ms/s
Ncp <<= 3;
dftlen <<= 3;
break;
default:
AssertFatal(1==0,"sample rate %f MHz not supported for numerology %d\n", fp->samples_per_subframe / 1000.0, mu);
}
const dft_size_idx_t dftsize = get_dft(dftlen);
// Do forward transform
if (LOG_DEBUGFLAG(DEBUG_PRACH)) {
LOG_D(PHY, "rx_prach: Doing PRACH FFT for nb_rx:%d Ncp:%d dftlen:%d\n", p->nb_rx, Ncp, dftlen);
}
const unsigned int K = get_prach_K(p->prach_sequence_length, p->pdu.prach_format, p->numerology_index, p->mu);
const uint8_t kbar = get_PRACH_k_bar(p->mu, p->numerology_index);
int n_ra_prb = p->msg1_frequencystart;
int k = (12*n_ra_prb) - 6*fp->N_RB_UL;
int N_ZC = (p->prach_sequence_length == 0) ? 839 : 139;
if (k<0) k+=(fp->ofdm_symbol_size);
k*=K;
k+=kbar;
prach_ru_params_t params = get_prach_ru_params(p, prachStartSymbol, fp);
c16_t rxsigF_tmp[p->nb_rx][NR_PRACH_SEQ_LEN_L];
memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp));
const uint8_t num_beams = p->pdu.beamforming.dig_bf_interface;
// When more than one beams, then each occasion is on one beam
@@ -353,36 +361,20 @@ static void rx_nr_prach_ru_internal(prach_item_t *p,
AssertFatal(prachOccasion < num_beams, "Num of PRACH Occasions must be same as number of beams in beamforming mode\n");
ant_offset = prachOccasion * p->nb_rx;
}
// TODO: Remove assumption of contiguous ports after DAS is properly handled in beamforming
uint16_t ant_start =
get_first_ant_idx(das,
p->nb_rx,
p->pdu.beamforming.prgs_list[0].dig_bf_interface_list[0].beam_idx,
p->pdu.param_v4.numSpatialStreamIndices > 0 ? p->pdu.param_v4.spatialStreamIndices[ant_offset] : 0);
for (int aa = 0; aa < p->nb_rx; aa++) {
// Fixme: slot or slot makes no sense ???
int slot2 = p->prach_sequence_length ? p->slot : p->slot;
int idx = ant_start + aa;
c16_t *prach = (c16_t *)&rxdata[idx][get_samples_slot_timestamp(fp, slot2) + sample_offset_slot - N_TA_offset];
// do DFT
c16_t *prach2 = prach + Ncp;
c16_t rxsigF_tmp[N_ZC];
memset(rxsigF_tmp, 0, sizeof(rxsigF_tmp));
for (int i = 0; i < reps; i++, prach2 += dftlen) {
c16_t tmp[dftlen] __attribute__((aligned(32)));
dft(dftsize, (int16_t *)prach2, (int16_t *)tmp, 1);
// Coherent combining of PRACH repetitions (assumes channel does not change, to be revisted for "long" PRACH)
LOG_D(PHY, "Doing PRACH combining of %d reptitions N_ZC %d\n", reps, N_ZC);
// if (k+N_ZC > dftlen) { // PRACH signal is split around DC
int k2 = k;
for (int j = 0; j < N_ZC; j++, k2++) {
if (k2 == dftlen)
k2 = 0;
rxsigF_tmp[j] = c16add(rxsigF_tmp[j], tmp[k2]);
}
}
memcpy(p->prach_buf[aa][prachOccasion], rxsigF_tmp, sizeof(rxsigF_tmp));
for (int rep = 0; rep < params.reps; rep++) {
rx_nr_prach_ru_internal_rep(p, ant_start, rxdata, fp, N_TA_offset, rep, &params, rxsigF_tmp);
}
for (int aa = 0; aa < p->nb_rx; aa++) {
memcpy(p->prach_buf[aa][prachOccasion], rxsigF_tmp[aa], sizeof(c16_t) * params.N_ZC);
}
}
@@ -399,6 +391,20 @@ void rx_nr_prach_ru(prach_item_t *p, int32_t **rxdata, NR_DL_FRAME_PARMS *fp, in
}
}
void rx_nr_prach_ru_rep(prach_item_t *p,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset,
int rep,
int prachOccasion,
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L])
{
int N_dur = get_nr_prach_duration(p->pdu.prach_format);
int prachStartSymbol = p->pdu.prach_start_symbol + prachOccasion * N_dur;
prach_ru_params_t params = get_prach_ru_params(p, prachStartSymbol, fp);
rx_nr_prach_ru_internal_rep(p, 0, rxdata, fp, N_TA_offset, rep, &params, rxsigF);
}
rx_prach_out_t rx_nr_prach(const prach_item_t *in, int occasion)
{
rx_prach_out_t out = {};

View File

@@ -19,13 +19,15 @@ nr_get_G(uint16_t nb_rb, uint16_t nb_symb_sch, uint8_t nb_re_dmrs, uint16_t leng
return (G);
}
uint32_t nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r)
int nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r)
{
if (Nl <= 0 || Qm <= 0) {
LOG_E(PHY, "Invalid input parameter in nr_get_E: Qm %d Nl %d\n", Qm, Nl);
return -1;
}
uint32_t E;
uint8_t Cprime = C; // assume CBGTI not present
AssertFatal(Nl > 0, "Nl is 0\n");
AssertFatal(Qm > 0, "Qm is 0\n");
if (r <= Cprime - ((G / (Nl * Qm)) % Cprime) - 1)
E = Nl * Qm * (G / (Nl * Qm * Cprime));
else

View File

@@ -35,7 +35,7 @@ uint32_t nr_get_G(uint16_t nb_rb,
uint8_t Qm,
uint8_t Nl);
uint32_t nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r);
int nr_get_E(uint32_t G, uint8_t C, uint8_t Qm, uint8_t Nl, uint8_t r);
void compute_nr_prach_seq(uint8_t short_sequence, uint8_t num_sequences, uint8_t rootSequenceIndex, c16_t X_u[64][839]);

View File

@@ -124,6 +124,13 @@ typedef struct rx_prach_out {
rx_prach_out_t rx_nr_prach(const prach_item_t *, int occasion);
void rx_nr_prach_ru(prach_item_t *, int32_t **, NR_DL_FRAME_PARMS *frame_parms, int N_TA_offset, bool das);
void rx_nr_prach_ru_rep(prach_item_t *p,
int32_t **rxdata,
NR_DL_FRAME_PARMS *fp,
int N_TA_offset,
int rep,
int prachOccasion,
c16_t (*rxsigF)[NR_PRACH_SEQ_LEN_L]);
void nr_fill_pucch(PHY_VARS_gNB *gNB,
int frame,

View File

@@ -226,11 +226,13 @@ int nr_ulsch_decoding(PHY_VARS_gNB *phy_vars_gNB,
}
nrLDPC_TB_decoding_parameters_t *TB_parameters = &TBs[pusch_id];
int E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, TB_parameters->nb_layers, 0);
if (E < 0)
return -1;
TB_parameters->llr = ulsch_llr;
TB_parameters->d = harq_process->d;
TB_parameters->c = harq_process->c;
TB_parameters->E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, TB_parameters->nb_layers, 0);
TB_parameters->E = E;
TB_parameters->first_rE2 = TB_parameters->C;
TB_parameters->E2 = TB_parameters->E;
TB_parameters->R = nr_get_R_ldpc_decoder(TB_parameters->rv_index,

View File

@@ -262,6 +262,8 @@ static int nr_csi_rs_channel_estimation(
int16_t *log2_maxh,
uint32_t *noise_power)
{
const int meas_bitmap = csirs_config_pdu->measurement_bitmap;
AssertFatal(meas_bitmap != 1, "No need to do CSI-RS channel estimation for only RSRP measurment\n");
*noise_power = 0;
int maxh = 0;
int count = 0;
@@ -301,10 +303,16 @@ static int nr_csi_rs_channel_estimation(
const c16_t *rx_csi_rs_signal = &csi_rs_received_signal[ant_rx][symbol_offset];
c16_t tmp =
c16MulConjShift(tx_csi_rs_signal[k_tx], rx_csi_rs_signal[k_rx], nr_csi_info->csi_rs_generated_signal_bits);
// This is not just the LS estimation for each (k,l), but also the sum of the different contributions
// for the sake of optimizing the memory used.
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].r += tmp.r;
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].i += tmp.i;
if (csirs_config_pdu->csi_type != 0) {
// This is not just the LS estimation for each (k,l), but also the sum of the different contributions
// for the sake of optimizing the memory used.
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].r += tmp.r;
csi_rs_ls_estimated_channel[ant_rx][port_tx][kinit_tx].i += tmp.i;
} else {
// for tracking we want estimates of all sub carriers having CSI-RS
csi_rs_ls_estimated_channel[ant_rx][port_tx][k_tx].r = tmp.r;
csi_rs_ls_estimated_channel[ant_rx][port_tx][k_tx].i = tmp.i;
}
}
}
}
@@ -362,6 +370,10 @@ static int nr_csi_rs_channel_estimation(
}
}
// we need only ls estimates for tracking CSI. So stop here.
if (meas_bitmap == 0)
continue;
/// Power noise estimation
AssertFatal(csirs_config_pdu->nr_of_rbs > 0, " nr_of_rbs needs to be greater than 0\n");
uint16_t noise_real[fp->nb_antennas_rx][csi_mapping->ports][csirs_config_pdu->nr_of_rbs];
@@ -406,9 +418,11 @@ static int nr_csi_rs_channel_estimation(
}
*noise_power /= (fp->nb_antennas_rx * csi_mapping->ports);
*log2_maxh = log2_approx(maxh - 1);
*log2_re = log2_approx(count - 1);
if (meas_bitmap > 1) {
*noise_power /= (fp->nb_antennas_rx * csi_mapping->ports);
*log2_maxh = log2_approx(maxh - 1);
*log2_re = log2_approx(count - 1);
}
#ifdef NR_CSIRS_DEBUG
LOG_I(NR_PHY, "Noise power estimation based on CSI-RS: %i\n", *noise_power);
@@ -774,6 +788,61 @@ static void nr_csi_im_power_estimation(const PHY_VARS_NR_UE *ue,
#endif
}
static double get_cfo(const double phase_diff, const int sym0, const int sym1, const NR_DL_FRAME_PARMS *fp)
{
const double one_fs = 1.0 / (fp->samples_per_frame * 100);
int sample_count = 0;
for (int s = sym0 + 1; s <= sym1; s++) {
const int prefix = (s % (7 * (1 << fp->numerology_index))) ? fp->nb_prefix_samples : fp->nb_prefix_samples0;
sample_count += (fp->ofdm_symbol_size + prefix);
}
const double delta_time = sample_count * one_fs;
const double cfo = phase_diff / (2 * M_PI * delta_time);
return cfo;
}
static void nr_ue_trs_processing(PHY_VARS_NR_UE *ue,
const c16_t res0_est[][1][ue->frame_parms.ofdm_symbol_size],
const c16_t res1_est[][1][ue->frame_parms.ofdm_symbol_size],
const c16_t freq_interp_est[][1][ue->frame_parms.ofdm_symbol_size],
const fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
const csi_mapping_parms_t *csi_mapping,
const int sym0,
const int sym1,
int *cfo,
int *time_offset)
{
AssertFatal((sym0 > -1) && (sym1 > -1) && (sym1 > sym0), "Invalid symbol index for TRS estimation\n");
const NR_DL_FRAME_PARMS *fp = &ue->frame_parms;
// CFO estimation
const int ant_rx = 0; // Estimate only on first antenna port
cd_t phase_diff = {0.0};
AssertFatal(csirs_config_pdu->freq_density == 3,
"CSI-RS for tracking must have freq density of 3 but has %d\n",
csirs_config_pdu->freq_density);
AssertFatal(csi_mapping->kprime == 0 && csi_mapping->lprime == 0, "Invalid kprime, lprime for CSI-RS for tracking (row 1)\n");
for (int rb = csirs_config_pdu->start_rb; rb < (csirs_config_pdu->start_rb + csirs_config_pdu->nr_of_rbs); rb++) {
for (int cdm_id = 0; cdm_id < csi_mapping->size; cdm_id++) {
uint16_t kinit = rb * NR_NB_SC_PER_RB;
uint16_t k = kinit + csi_mapping->koverline[cdm_id];
const c16_t *res0 = res0_est[ant_rx][0];
const c16_t *res1 = res1_est[ant_rx][0];
phase_diff.r += res1[k].r * res0[k].r + res1[k].i * res0[k].i;
phase_diff.i += res1[k].i * res0[k].r - res1[k].r * res0[k].i;
}
}
*cfo = (int)get_cfo(atan2(phase_diff.i, phase_diff.r), sym0, sym1, fp);
if (time_offset) {
// Time offset estimation
__attribute__((aligned(32))) c16_t time_est[fp->ofdm_symbol_size];
delay_t delay = {0};
nr_est_delay(fp->ofdm_symbol_size, freq_interp_est[ant_rx][0], time_est, &delay);
*time_offset = delay.delay_max_pos;
}
}
void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
const fapi_nr_dl_config_csiim_pdu_rel15_t *csiim_config_pdu)
@@ -796,7 +865,10 @@ void nr_ue_csi_im_procedures(PHY_VARS_NR_UE *ue,
void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
const UE_nr_rxtx_proc_t *proc,
const c16_t rxdataF[][ue->frame_parms.samples_per_slot_wCP],
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu)
fapi_nr_dl_config_csirs_pdu_rel15_t *csirs_config_pdu,
c16_t trs_estimates[][1][ue->frame_parms.ofdm_symbol_size],
const int res_idx,
const int trs_sym0)
{
#ifdef NR_CSIRS_DEBUG
@@ -816,11 +888,6 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
LOG_I(NR_PHY, "csirs_config_pdu->power_control_offset_ss = %i\n", csirs_config_pdu->power_control_offset_ss);
#endif
if(csirs_config_pdu->csi_type == 0) {
LOG_E(NR_PHY, "Handling of CSI-RS for tracking not handled yet at PHY\n");
return;
}
if(csirs_config_pdu->csi_type == 2) {
LOG_E(NR_PHY, "Handling of ZP CSI-RS not handled yet at PHY\n");
return;
@@ -870,29 +937,25 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
&rsrp_dBm,
rxdataF);
if (csirs_config_pdu->measurement_bitmap == 0) {
LOG_D(NR_PHY, "No CSI-RS measurements configured\n");
return;
}
uint32_t noise_power = 0;
int16_t log2_re = 0;
int16_t log2_maxh = 0;
// if we need to measure only RSRP no need to do channel estimation
if (csirs_config_pdu->measurement_bitmap > 1)
if (csirs_config_pdu->measurement_bitmap != 1) {
const bool use_trs_buff = (csirs_config_pdu->csi_type == 0 && res_idx == 0);
nr_csi_rs_channel_estimation(frame_parms,
csirs_config_pdu,
csi_info,
(const c16_t **)csi_info->csi_rs_generated_signal,
csi_rs_received_signal,
&mapping_parms,
CDM_group_size,
csi_rs_ls_estimated_channel,
csi_rs_estimated_channel_freq,
&log2_re,
&log2_maxh,
&noise_power);
csirs_config_pdu,
csi_info,
(const c16_t **)csi_info->csi_rs_generated_signal,
csi_rs_received_signal,
&mapping_parms,
CDM_group_size,
(use_trs_buff) ? trs_estimates : csi_rs_ls_estimated_channel,
csi_rs_estimated_channel_freq,
&log2_re,
&log2_maxh,
&noise_power);
}
uint8_t rank_indicator = 0;
// bit 1 in bitmap to indicate RI measurment
@@ -926,9 +989,34 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
nr_csi_rs_cqi_estimation(precoded_sinr_dB, &cqi);
}
int trs_cfo = 0;
const bool do_trs_est = (csirs_config_pdu->csi_type == 0) && (res_idx == 1);
if (do_trs_est) {
start_meas_nr_ue_phy(ue, TRS_PROCESSING);
nr_ue_trs_processing(ue,
trs_estimates,
csi_rs_ls_estimated_channel,
csi_rs_estimated_channel_freq,
csirs_config_pdu,
&mapping_parms,
trs_sym0,
csirs_config_pdu->symb_l0,
&trs_cfo,
NULL); // Time offset not estimated because it is corrected using PBCH DMRS
stop_meas_nr_ue_phy(ue, TRS_PROCESSING);
}
switch (csirs_config_pdu->measurement_bitmap) {
case 1 :
LOG_I(NR_PHY, "[UE %d] RSRP = %i dBm\n", ue->Mod_id, rsrp_dBm);
case 0:
if (do_trs_est)
LOG_I(NR_PHY,
"%d.%d TRS estimated CFO: %d Hz\n",
proc->frame_rx,
proc->nr_slot_rx,
trs_cfo);
break;
case 1:
LOG_I(NR_PHY, "%d.%d [UE %d] RSRP = %i dBm\n", proc->frame_rx, proc->nr_slot_rx, ue->Mod_id, rsrp_dBm);
break;
case 26 :
LOG_I(NR_PHY, "RI = %i i1 = %i.%i.%i, i2 = %i, SINR = %i dB, CQI = %i\n",
@@ -942,6 +1030,10 @@ void nr_ue_csi_rs_procedures(PHY_VARS_NR_UE *ue,
AssertFatal(false, "Not supported measurement configuration\n");
}
if (!ue->cont_fo_comp && do_trs_est && csirs_config_pdu->last_trs_slot) {
trs_freq_correction(ue, trs_cfo);
}
// Send CSI measurements to MAC
if (!ue->if_inst || !ue->if_inst->dl_indication)
return;

View File

@@ -147,9 +147,12 @@ void nr_dlsch_decoding(PHY_VARS_NR_UE *phy_vars_ue,
TB_parameters.C,
TB_parameters.Qm,
TB_parameters.llr);
int E = nr_get_E(TB_parameters.G, TB_parameters.C, TB_parameters.Qm, TB_parameters.nb_layers, 0);
if (E < 0)
return;
TB_parameters.c = harq_process->c;
TB_parameters.d = harq_process->d;
TB_parameters.E = nr_get_E(TB_parameters.G, TB_parameters.C, TB_parameters.Qm, TB_parameters.nb_layers, 0);
TB_parameters.E = E;
TB_parameters.E2 = TB_parameters.E;
TB_parameters.first_rE2 = TB_parameters.C;
for (int r = 1; r < TB_parameters.C; r++) {

View File

@@ -193,8 +193,8 @@ static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz,
uint32_t csi_res_odd = (csi_res_bitmap >> 16) & 0xfff;
AssertFatal((dmrs_rb_bitmap & csi_res_even) == 0, "DMRS RE overlapping with CSI RE, it shouldn't happen\n");
AssertFatal((dmrs_rb_bitmap & csi_res_odd) == 0, "DMRS RE overlapping with CSI RE, it shouldn't happen\n");
uint32_t dmrs_csi_overlap_even = csi_res_even + dmrs_rb_bitmap;
uint32_t dmrs_csi_overlap_odd = csi_res_odd + dmrs_rb_bitmap;
uint32_t dmrs_csi_overlap_even = csi_res_even | dmrs_rb_bitmap;
uint32_t dmrs_csi_overlap_odd = csi_res_odd | dmrs_rb_bitmap;
int8_t validDmrsEst;
if (chest_time_type == 0)
validDmrsEst = get_valid_dmrs_idx_for_channel_est(dlsch_config->dlDmrsSymbPos, symbol);
@@ -203,16 +203,17 @@ static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz,
int pos = 0;
int block_start, block_end;
int offset = 0;
while (find_next_rb_block(freq_alloc->bitmap, dlsch_config->BWPSize, &pos, &block_start, &block_end)) {
int start_rb = block_start + dlsch_config->BWPStart;
int nb_rb = block_end - block_start + 1;
const int start_re = (fp->first_carrier_offset + start_rb * NR_NB_SC_PER_RB) % fp->ofdm_symbol_size;
for (int aarx = 0; aarx < fp->nb_antennas_rx; aarx++) {
c16_t *rxF_ext = rxdataF_ext[aarx];
c16_t *rxF_ext = rxdataF_ext[aarx] + offset;
c16_t *rxF = &rxdataF[aarx][symbol * fp->ofdm_symbol_size];
for (int l = 0; l < Nl; l++) {
int32_t *dl_ch0 = &dl_ch_estimates[(l * fp->nb_antennas_rx) + aarx][validDmrsEst * fp->ofdm_symbol_size];
int32_t *dl_ch0_ext = dl_ch_estimates_ext[(l * fp->nb_antennas_rx) + aarx];
int32_t *dl_ch0_ext = dl_ch_estimates_ext[(l * fp->nb_antennas_rx) + aarx] + offset;
if (pilots == 0 && csi_res_bitmap == 0) { // data symbol only
if (l == 0) {
if (start_re + nb_rb * NR_NB_SC_PER_RB <= fp->ofdm_symbol_size) {
@@ -247,6 +248,7 @@ static void nr_dlsch_extract_rbs(uint32_t rxdataF_sz,
}
}
}
offset += nb_rb * NR_NB_SC_PER_RB;
}
}
@@ -835,205 +837,199 @@ int nr_rx_pdsch(PHY_VARS_NR_UE *ue,
//----------------------------------------------------------
const bool meas_enabled = cpumeas(CPUMEAS_GETSTATE);
int nb_rb_pdsch = freq_alloc->num_rbs;
{
start_meas_nr_ue_phy(ue, DLSCH_EXTRACT_RBS_STATS);
__attribute__((aligned(32))) c16_t rxdataF_ext[nbRx][rx_size_symbol];
memset(rxdataF_ext, 0, sizeof(rxdataF_ext));
uint32_t csi_res_bitmap = build_csi_overlap_bitmap(dlsch_config, symbol);
start_meas_nr_ue_phy(ue, DLSCH_EXTRACT_RBS_STATS);
__attribute__((aligned(32))) c16_t rxdataF_ext[nbRx][rx_size_symbol];
memset(rxdataF_ext, 0, sizeof(rxdataF_ext));
LOG_D(PHY, "%d.%d symbol %d csi overlap bitmap %d\n", frame, nr_slot_rx, symbol, csi_res_bitmap);
uint32_t csi_res_bitmap = build_csi_overlap_bitmap(dlsch_config, symbol);
LOG_D(PHY, "%d.%d symbol %d csi overlap bitmap %d\n", frame, nr_slot_rx, symbol, csi_res_bitmap);
nr_dlsch_extract_rbs(fp->samples_per_slot_wCP,
rxdataF,
rx_size_symbol,
pdsch_est_size,
dl_ch_estimates,
rxdataF_ext,
dl_ch_estimates_ext,
symbol,
pilots,
dlsch_config,
freq_alloc,
nl,
fp,
csi_res_bitmap,
ue->chest_time);
stop_meas_nr_ue_phy(ue, DLSCH_EXTRACT_RBS_STATS);
if (scope_req->copy_chanest_to_scope) {
size_t size = sizeof(c16_t) * nb_rb_pdsch * NR_NB_SC_PER_RB;
int copy_index = symbol - dlsch_config->start_symbol;
int offset = copy_index * size;
UEscopeCopyUnsafe(ue, pdschChanEstimates, dl_ch_estimates_ext[0], size, offset, copy_index);
nr_dlsch_extract_rbs(fp->samples_per_slot_wCP,
rxdataF,
rx_size_symbol,
pdsch_est_size,
dl_ch_estimates,
rxdataF_ext,
dl_ch_estimates_ext,
symbol,
pilots,
dlsch_config,
freq_alloc,
nl,
fp,
csi_res_bitmap,
ue->chest_time);
stop_meas_nr_ue_phy(ue, DLSCH_EXTRACT_RBS_STATS);
if (scope_req->copy_chanest_to_scope) {
size_t size = sizeof(c16_t) * nb_rb_pdsch * NR_NB_SC_PER_RB;
int copy_index = symbol - dlsch_config->start_symbol;
int offset = copy_index * size;
UEscopeCopyUnsafe(ue, pdschChanEstimates, dl_ch_estimates_ext[0], size, offset, copy_index);
}
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Pilot/Data extraction %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_EXTRACT_RBS_STATS].p_time / (cpuf * 1000.0));
}
if (ue->phy_sim_pdsch_rxdataF_ext)
memcpy(ue->phy_sim_pdsch_rxdataF_ext + symbol * sizeof(rxdataF_ext), rxdataF_ext, sizeof(rxdataF_ext));
nb_re_pdsch = (pilots == 1) ?
((config_type == NFAPI_NR_DMRS_TYPE1) ? nb_rb_pdsch * (12 - 6 * dlsch_config->n_dmrs_cdm_groups) :
nb_rb_pdsch * (12 - 4 * dlsch_config->n_dmrs_cdm_groups)):
(nb_rb_pdsch * 12);
// Subtract CSI-RS REs from PDSCH RE count
if (csi_res_bitmap != 0) {
uint32_t csi_re_count = 0;
uint32_t csi_res_even = csi_res_bitmap & 0xfff;
uint32_t csi_res_odd = (csi_res_bitmap >> 16) & 0xfff;
uint32_t count_even = count_bits(&csi_res_even, 1);
uint32_t count_odd = count_bits(&csi_res_odd, 1);
int start = freq_alloc->first_rb + dlsch_config->BWPStart;
int end = freq_alloc->last_rb + 1;
for (int rb = start; rb < end; rb++) {
if ((freq_alloc->bitmap[rb / 32] >> (rb % 32)) & 0x01)
csi_re_count += (rb % 2 == 0) ? count_even : count_odd;
}
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Pilot/Data extraction %5.2f \n",
nb_re_pdsch = (nb_re_pdsch > csi_re_count) ? (nb_re_pdsch - csi_re_count) : 0;
if (csi_re_count > 0) {
LOG_D(NR_PHY,
"[CSI OVERLAP] Frame/Slot %d.%d Symbol %d: CSI-RS overlapping PDSCH - %d CSI-RS REs skipped, %d data REs extracted\n",
frame,
nr_slot_rx,
slot,
symbol,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_EXTRACT_RBS_STATS].p_time / (cpuf * 1000.0));
csi_re_count,
nb_re_pdsch);
}
if (ue->phy_sim_pdsch_rxdataF_ext)
memcpy(ue->phy_sim_pdsch_rxdataF_ext + symbol * sizeof(rxdataF_ext), rxdataF_ext, sizeof(rxdataF_ext));
}
nb_re_pdsch = (pilots == 1) ? ((config_type == NFAPI_NR_DMRS_TYPE1) ? nb_rb_pdsch * (12 - 6 * dlsch_config->n_dmrs_cdm_groups)
: nb_rb_pdsch * (12 - 4 * dlsch_config->n_dmrs_cdm_groups))
: (nb_rb_pdsch * 12);
// Subtract CSI-RS REs from PDSCH RE count
if (csi_res_bitmap != 0) {
uint32_t csi_re_count = 0;
uint32_t csi_res_even = csi_res_bitmap & 0xfff;
uint32_t csi_res_odd = (csi_res_bitmap >> 16) & 0xfff;
uint32_t count_even = count_bits(&csi_res_even, 1);
uint32_t count_odd = count_bits(&csi_res_odd, 1);
int start = freq_alloc->first_rb + dlsch_config->BWPStart;
int end = freq_alloc->last_rb + 1;
for (int rb = start; rb < end; rb++) {
if ((freq_alloc->bitmap[rb / 32] >> (rb % 32)) & 0x01)
csi_re_count += (rb % 2 == 0) ? count_even : count_odd;
if (scope_req->copy_rxdataF_to_scope) {
size_t size = sizeof(c16_t) * nb_re_pdsch;
int copy_index = symbol - dlsch_config->start_symbol;
UEscopeCopyUnsafe(ue, pdschRxdataF, rxdataF_ext[0], size, scope_req->scope_rxdataF_offset, copy_index);
scope_req->scope_rxdataF_offset += size;
}
//----------------------------------------------------------
//--------------------- Channel Scaling --------------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_SCALE_STATS);
nr_scale_channel(rx_size_symbol, dl_ch_estimates_ext, 0, nb_re_pdsch, nl, nbRx, 0);
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_SCALE_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_SCALE_STATS].p_time / (cpuf * 1000.0));
}
//----------------------------------------------------------
//--------------------- Channel Level Calc. ----------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS);
if (first_symbol_flag) {
int32_t avg[nl * nbRx];
if (nb_re_pdsch)
nr_channel_level(0, rx_size_symbol, (c16_t (*)[rx_size_symbol])dl_ch_estimates_ext, nbRx, nl, avg, nb_re_pdsch);
else
LOG_E(NR_PHY, "Average channel level is 0: nb_rb_pdsch = %d, nb_re_pdsch = %d\n", nb_rb_pdsch, nb_re_pdsch);
int avgs = 0;
int32_t median[MAX_ANT][MAX_ANT];
for (int l = 0; l < nl; l++)
for (int aarx = 0; aarx < nbRx; aarx++) {
avgs = cmax(avgs, avg[l * nbRx + aarx]);
LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, l, avg[l * nbRx + aarx]);
LOG_D(PHY, "avgs Power per SC is %d\n", avgs);
median[l][aarx] = avg[l * nbRx + aarx];
}
nb_re_pdsch = (nb_re_pdsch > csi_re_count) ? (nb_re_pdsch - csi_re_count) : 0;
if (csi_re_count > 0) {
LOG_D(NR_PHY,
"[CSI OVERLAP] Frame/Slot %d.%d Symbol %d: CSI-RS overlapping PDSCH - %d CSI-RS REs skipped, %d data REs extracted\n",
frame,
nr_slot_rx,
symbol,
csi_re_count,
nb_re_pdsch);
}
}
if (scope_req->copy_rxdataF_to_scope) {
size_t size = sizeof(c16_t) * nb_re_pdsch;
int copy_index = symbol - dlsch_config->start_symbol;
UEscopeCopyUnsafe(ue, pdschRxdataF, rxdataF_ext[0], size, scope_req->scope_rxdataF_offset, copy_index);
scope_req->scope_rxdataF_offset += size;
}
//----------------------------------------------------------
//--------------------- Channel Scaling --------------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_SCALE_STATS);
nr_scale_channel(rx_size_symbol, dl_ch_estimates_ext, 0, nb_re_pdsch, nl, nbRx, 0);
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_SCALE_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d: Channel Scale %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_SCALE_STATS].p_time / (cpuf * 1000.0));
}
//----------------------------------------------------------
//--------------------- Channel Level Calc. ----------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS);
if (first_symbol_flag) {
int32_t avg[nl * nbRx];
if (nb_re_pdsch)
nr_channel_level(0, rx_size_symbol, (c16_t(*)[rx_size_symbol])dl_ch_estimates_ext, nbRx, nl, avg, nb_re_pdsch);
else
LOG_E(NR_PHY, "Average channel level is 0: nb_rb_pdsch = %d, nb_re_pdsch = %d\n", nb_rb_pdsch, nb_re_pdsch);
int avgs = 0;
int32_t median[MAX_ANT][MAX_ANT];
for (int l = 0; l < nl; l++)
if (nl > 1) {
nr_dlsch_channel_level_median(rx_size_symbol, dl_ch_estimates_ext, median, nl, nbRx, nb_re_pdsch);
for (int l = 0; l < nl; l++) {
for (int aarx = 0; aarx < nbRx; aarx++) {
avgs = cmax(avgs, avg[l * nbRx + aarx]);
LOG_D(PHY, "nb_rb %d avg_%d_%d Power per SC is %d\n", nb_rb_pdsch, aarx, l, avg[l * nbRx + aarx]);
LOG_D(PHY, "avgs Power per SC is %d\n", avgs);
median[l][aarx] = avg[l * nbRx + aarx];
}
if (nl > 1) {
nr_dlsch_channel_level_median(rx_size_symbol, dl_ch_estimates_ext, median, nl, nbRx, nb_re_pdsch);
for (int l = 0; l < nl; l++) {
for (int aarx = 0; aarx < nbRx; aarx++) {
avgs = cmax(avgs, median[l][aarx]);
}
avgs = cmax(avgs, median[l][aarx]);
}
}
// Output shift: half channel energy (log2|h|^2/2) + MRC antenna gain.
// Single-layer adds +1 guard bit (raw peak); multi-layer uses median so no guard needed.
if (nl == 1)
*log2_maxh = (log2_approx(avgs) >> 1) + 1 + log2_approx(nbRx >> 1);
else
*log2_maxh = (log2_approx(avgs) >> 1) + log2_approx(nbRx >> 1);
LOG_D(PHY, "[DLSCH] AbsSubframe %d.%d log2_maxh = %d (%d)\n", frame % 1024, nr_slot_rx, *log2_maxh, avgs);
}
// Output shift: half channel energy (log2|h|^2/2) + MRC antenna gain.
// Single-layer adds +1 guard bit (raw peak); multi-layer uses median so no guard needed.
if (nl == 1)
*log2_maxh = (log2_approx(avgs) >> 1) + 1 + log2_approx(nbRx >> 1);
else
*log2_maxh = (log2_approx(avgs) >> 1) + log2_approx(nbRx >> 1);
LOG_D(PHY, "[DLSCH] AbsSubframe %d.%d log2_maxh = %d (%d)\n", frame % 1024, nr_slot_rx, *log2_maxh, avgs);
#if T_TRACER
T(T_UE_PHY_PDSCH_ENERGY,
T_INT(gNB_id),
T_INT(frame % 1024),
T_INT(nr_slot_rx),
T_INT(avg[0]), // layer 0, antenna 0
T_INT(nbRx > 1 ? avg[1] : 0), // layer 0, antenna 1
T_INT(nl > 1 ? avg[nbRx] : 0), // layer 1, antenna 0
T_INT(nl > 1 && nbRx > 1 ? avg[nbRx + 1] : 0)); // layer 1, antenna 1
#endif
}
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
first_symbol_flag,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_LEVEL_STATS].p_time / (cpuf * 1000.0));
}
//----------------------------------------------------------
//--------------------- channel compensation ---------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_COMPENSATION_STATS);
nr_channel_compensation(rx_size_symbol,
nbRx,
nl,
rxdataF_ext,
chFext,
dl_ch_mag[symbol],
dl_ch_magb[symbol],
dl_ch_magr[symbol],
p_rxComp,
need_rho ? (c16_t(*)[nl][rx_size_symbol])rho_dl[symbol] : NULL,
dlsch->cw_info.qamModOrder,
0, // symbol already baked into p_rxComp
*log2_maxh);
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_COMPENSATION_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d Channel Comp %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
*log2_maxh,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_COMPENSATION_STATS].p_time / (cpuf * 1000.0));
}
// Please keep it: useful for debugging
#ifdef DEBUG_PDSCH_RX
char filename[50];
snprintf(filename, 50, "rxdataF0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF0", &rxdataF[0][symbol * fp->ofdm_symbol_size], fp->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "dl_ch_estimates0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates0", &dl_ch_estimates[0][symbol * fp->ofdm_symbol_size], fp->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "rxdataF_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_ext0", &rxdataF_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "dl_ch_estimates_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates_ext0", &dl_ch_estimates_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "rxdataF_comp00_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_comp00", rxdataF_comp[symbol][0], rx_size_symbol, 1, 1);
T(T_UE_PHY_PDSCH_ENERGY,
T_INT(gNB_id),
T_INT(frame % 1024),
T_INT(nr_slot_rx),
T_INT(avg[0]), // layer 0, antenna 0
T_INT(nbRx > 1 ? avg[1] : 0), // layer 0, antenna 1
T_INT(nl > 1 ? avg[nbRx] : 0), // layer 1, antenna 0
T_INT(nl > 1 && nbRx > 1 ? avg[nbRx + 1] : 0)); // layer 1, antenna 1
#endif
}
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_LEVEL_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d first_symbol_flag %d: Channel Level %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
first_symbol_flag,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_LEVEL_STATS].p_time / (cpuf * 1000.0));
}
//----------------------------------------------------------
//--------------------- channel compensation ---------------
//----------------------------------------------------------
start_meas_nr_ue_phy(ue, DLSCH_CHANNEL_COMPENSATION_STATS);
nr_channel_compensation(rx_size_symbol,
nbRx,
nl,
rxdataF_ext,
chFext,
dl_ch_mag[symbol],
dl_ch_magb[symbol],
dl_ch_magr[symbol],
p_rxComp,
need_rho ? (c16_t(*)[nl][rx_size_symbol])rho_dl[symbol] : NULL,
dlsch->cw_info.qamModOrder,
0, // symbol already baked into p_rxComp
*log2_maxh);
stop_meas_nr_ue_phy(ue, DLSCH_CHANNEL_COMPENSATION_STATS);
if (meas_enabled) {
LOG_D(PHY,
"[AbsSFN %u.%d] Slot%d Symbol %d log2_maxh %d Channel Comp %5.2f \n",
frame,
nr_slot_rx,
slot,
symbol,
*log2_maxh,
ue->phy_cpu_stats.cpu_time_stats[DLSCH_CHANNEL_COMPENSATION_STATS].p_time / (cpuf * 1000.0));
}
// Please keep it: useful for debugging
#ifdef DEBUG_PDSCH_RX
char filename[50];
snprintf(filename, 50, "rxdataF0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF0", &rxdataF[0][symbol * fp->ofdm_symbol_size], fp->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "dl_ch_estimates0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates0", &dl_ch_estimates[0][symbol * fp->ofdm_symbol_size], fp->ofdm_symbol_size, 1, 1);
snprintf(filename, 50, "rxdataF_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_ext0", &rxdataF_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "dl_ch_estimates_ext0_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "dl_ch_estimates_ext0", &dl_ch_estimates_ext[0][0], rx_size_symbol, 1, 1);
snprintf(filename, 50, "rxdataF_comp00_symb_%d_nr_slot_rx_%d.m", symbol, nr_slot_rx);
write_output(filename, "rxdataF_comp00", &rxdataF_comp[0][0][symbol * rx_size_symbol], rx_size_symbol, 1, 1);
#endif
// MRC is performed inline by nr_channel_compensation; apply MMSE for multi-layer
start_meas_nr_ue_phy(ue, DLSCH_MRC_MMSE_STATS);

View File

@@ -12,7 +12,6 @@
#include "SCHED_NR_UE/defs.h"
#include "common/utils/nr/nr_common.h"
#include "common_lib.h"
#include <math.h>
#include "PHY/NR_REFSIG/pss_nr.h"
@@ -333,6 +332,8 @@ static void nr_scan_ssb(void *arg)
continue;
}
ssbInfo->pssCorrAvgPower = search_params.pss_res.avg;
ssbInfo->pssCorrPeakPower = search_params.pss_res.peak;
ssbInfo->ssbOffset = search_params.pss_res.pos - search_params.nb_prefix_samples;
ssbInfo->nidCell = search_params.sss_res.nid_cell;

View File

@@ -156,17 +156,14 @@ int nr_ulsch_encoding(PHY_VARS_NR_UE *ue,
for (int r = 0; r < TB_parameters->C; r++) {
nrLDPC_segment_encoding_parameters_t *segment_parameters = &TB_parameters->segments[r];
int E = nr_get_E(TB_parameters->G, TB_parameters->C, TB_parameters->Qm, TB_parameters->nb_layers, r);
if (E < 0)
return -1;
segment_parameters->c = harq_process->c[r];
segment_parameters->E = nr_get_E(TB_parameters->G,
TB_parameters->C,
TB_parameters->Qm,
TB_parameters->nb_layers,
r);
segment_parameters->E = E;
reset_meas(&segment_parameters->ts_interleave);
reset_meas(&segment_parameters->ts_rate_match);
reset_meas(&segment_parameters->ts_ldpc_encode);
} // TB_parameters->C
} // pusch_id

View File

@@ -882,15 +882,23 @@ static void map_overlapped_ack(uci_on_pusch_bit_type_t *template,
const int32_t num_ack_remaining = G_ack - ack_bits_marked;
if (num_ack_remaining <= 0)
continue;
const uint32_t d_factor_re = get_d_factor_re(num_ack_remaining, num_reserved_bits_on_sym);
for (uint32_t i = 0; i < num_reserved_bits_on_sym && ack_bits_marked < G_ack; i += d_factor_re) {
uint32_t pos = reserved_indices_on_this_sym[i];
int bit_in_group = pos % Qm;
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
else // puncturing CSIp2
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER_CSI2 : BIT_TYPE_ACK_RESERVED_CSI2;
ack_bits_marked++;
AssertFatal(num_reserved_bits_on_sym % Qm == 0,
"reserved bits on symbol (%u) not a multiple of Qm (%d)\n",
num_reserved_bits_on_sym,
Qm);
const uint32_t num_reserved_re = num_reserved_bits_on_sym / Qm;
const uint32_t num_ack_re_remaining = num_ack_remaining / Qm;
const uint32_t d_factor_re = get_d_factor_re(num_ack_re_remaining, num_reserved_re);
for (uint32_t re = 0; re < num_reserved_re && ack_bits_marked < G_ack; re += d_factor_re) {
for (int b = 0; b < Qm; b++) {
uint32_t pos = reserved_indices_on_this_sym[re * Qm + b];
int bit_in_group = pos % Qm;
if (template[pos] == BIT_TYPE_ULSCH) // puncturing ULSCH
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER : BIT_TYPE_ACK_RESERVED;
else // puncturing CSIp2
template[pos] = (bit_in_group >= placeholder_start) ? BIT_TYPE_ACK_PLACEHOLDER_CSI2 : BIT_TYPE_ACK_RESERVED_CSI2;
ack_bits_marked++;
}
}
}
}

View File

@@ -733,26 +733,15 @@ void nr_generate_pucch2(c16_t **txdataF,
const int seq_sz = ((startingPRB + pucch_pdu->prb_size) * NR_PUCCH_DMRS_RB * DMRS_MOD_ORDER + 31) / 32;
uint32_t *seq = gold_cache(temp_x2, seq_sz);
for (int rb=0; rb<pucch_pdu->prb_size; rb++) {
//startingPRB = startingPRB + rb;
const bool nb_rb_is_even = frame_parms->N_RB_DL & 1;
const bool nb_rb_is_even = !(frame_parms->N_RB_DL & 1);
const int halfRBs = frame_parms->N_RB_DL / 2;
const int baseRB = rb + startingPRB;
int re_offset = (l + startingSymbolIndex) * frame_parms->ofdm_symbol_size + 12 * baseRB;
if (nb_rb_is_even) {
if (baseRB < halfRBs) // if number RBs in bandwidth is even and current PRB is lower band
re_offset += frame_parms->first_carrier_offset;
else
re_offset -= halfRBs;
} else {
if (baseRB < halfRBs) // if number RBs in bandwidth is odd and current PRB is lower band
re_offset += frame_parms->first_carrier_offset;
else if (baseRB > halfRBs) // if number RBs in bandwidth is odd and current PRB is upper band
re_offset += -halfRBs + 6;
else
re_offset += frame_parms->first_carrier_offset;
}
int re_offset = (l + startingSymbolIndex) * frame_parms->ofdm_symbol_size + NR_NB_SC_PER_RB * baseRB;
if (baseRB > halfRBs)
re_offset -= (nb_rb_is_even) ? (halfRBs * NR_NB_SC_PER_RB) : (halfRBs * NR_NB_SC_PER_RB + 6);
else
re_offset += frame_parms->first_carrier_offset;
//txptr = &txdataF[0][re_offset];
int k=0;
#ifdef DEBUG_NR_PUCCH_TX
int kk=0;

View File

@@ -46,13 +46,12 @@
#define INITIAL_SSS_NR (7)
static void init_context_sss_nr(int amp, int16_t d_sss[N_ID_2_NUMBER][N_ID_1_NUMBER][LENGTH_SSS_NR])
static void init_context_sss_nr(int amp, int N_ID_2, int16_t d_sss[N_ID_1_NUMBER][LENGTH_SSS_NR])
{
int16_t x0[LENGTH_SSS_NR];
int16_t x1[LENGTH_SSS_NR];
int16_t dss_current;
int m0, m1;
int nid_2_num = get_softmodem_params()->sl_mode == 0 ? N_ID_2_NUMBER : N_ID_2_NUMBER_SL;
const int x0_initial[INITIAL_SSS_NR] = { 1, 0, 0, 0, 0, 0, 0 };
const int x1_initial[INITIAL_SSS_NR] = { 1, 0, 0, 0, 0, 0, 0 };
@@ -65,21 +64,19 @@ static void init_context_sss_nr(int amp, int16_t d_sss[N_ID_2_NUMBER][N_ID_1_NUM
x1[i + 7] = (x1[i + 1] + x1[i]) % (2);
}
for (int N_ID_2 = 0; N_ID_2 < nid_2_num; N_ID_2++) {
for (int N_ID_1 = 0; N_ID_1 < N_ID_1_NUMBER; N_ID_1++) {
m0 = 15 * (N_ID_1 / 112) + (5 * N_ID_2);
m1 = N_ID_1 % 112;
for (int n = 0; n < LENGTH_SSS_NR; n++) {
dss_current = (1 - 2 * x0 [(n + m0) % (LENGTH_SSS_NR)]) * (1 - 2 * x1[(n + m1) % (LENGTH_SSS_NR)]);
for (int N_ID_1 = 0; N_ID_1 < N_ID_1_NUMBER; N_ID_1++) {
m0 = 15 * (N_ID_1 / 112) + (5 * N_ID_2);
m1 = N_ID_1 % 112;
for (int n = 0; n < LENGTH_SSS_NR; n++) {
dss_current = (1 - 2 * x0[(n + m0) % (LENGTH_SSS_NR)]) * (1 - 2 * x1[(n + m1) % (LENGTH_SSS_NR)]);
/* Modulation of SSS is a BPSK TS 36.211 chapter 5.1.2 BPSK */
#if 1
d_sss[N_ID_2][N_ID_1][n] = dss_current;// * amp;
(void) amp;
d_sss[N_ID_1][n] = dss_current; // * amp;
(void)amp;
#else
(void) amp;
d_sss[N_ID_2][N_ID_1][n] = (dss_current * SHRT_MAX)>>SCALING_PSS_NR;
(void)amp;
d_sss[N_ID_1][n] = (dss_current * SHRT_MAX) >> SCALING_PSS_NR;
#endif
}
}
}
@@ -191,8 +188,8 @@ sss_detection_result_t rx_sss_nr(nr_sss_params_t *params,
c16_t pss_ext[params->nb_antennas_rx][LENGTH_PSS_NR];
c16_t sss_ext[params->nb_antennas_rx][LENGTH_SSS_NR];
const int Nid2 = GET_NID2(pss->nid2);
int16_t d_sss[N_ID_2_NUMBER][N_ID_1_NUMBER][LENGTH_SSS_NR];
init_context_sss_nr(AMP, d_sss);
int16_t d_sss[N_ID_1_NUMBER][LENGTH_SSS_NR];
init_context_sss_nr(AMP, Nid2, d_sss);
pss_sss_extract_nr(params, pss_ext, sss_ext, rxdataF); /* subframe */
@@ -234,7 +231,7 @@ sss_detection_result_t rx_sss_nr(nr_sss_params_t *params,
(c64_t){round(cos(M_PI / 3 / 15 * (phase_to_try[idx])) * 32767), round(sin(M_PI / 3 / 15 * (phase_to_try[idx])) * 32767)};
for (int n1 = Nid1_start; n1 < Nid1_end; n1++) { // all possible Nid1 values
int64_t metric = 0;
int16_t *d = d_sss[Nid2][n1];
int16_t *d = d_sss[n1];
for (int i = 0; i < LENGTH_SSS_NR; i++) {
// metric is only real part because sss is a pure real signal (imaginary is 0)
metric += d[i] * (rot.r * sss_comp[i].r - rot.i * sss_comp[i].i);
@@ -277,7 +274,7 @@ sss_detection_result_t rx_sss_nr(nr_sss_params_t *params,
int Nid1 = GET_NID1(res.nid_cell);
LOG_D(PHY, "Nid2 %d Nid1 %d metric %d, phase_max %d \n", Nid2, Nid1, res.metric, res.phase);
int16_t *d = d_sss[Nid2][Nid1];
int16_t *d = d_sss[Nid1];
c32_t sig_sum = {};
for (int i = 0; i < LENGTH_SSS_NR; i++) {
sig_sum.r += d[i] * sss_comp[i].r;

View File

@@ -1,5 +1,7 @@
#ifndef CALIB_SCOPE_H
#define CALIB_SCOPE_H
#include "common_lib.h"
void CalibrationInitScope(void **samplesRx, openair0_device_t *rfdevice);
#endif

View File

@@ -107,8 +107,6 @@ int main(int argc, char **argv) {
.eth_params=NULL,
//! record player data, definition in record_player.h
.recplay_state=NULL,
/* !brief Indicates if device already initialized */
.is_init=0,
/*!brief Can be used by driver to hold internal structure*/
.priv=NULL,
/* Functions API, which are called by the application*/

View File

@@ -24,13 +24,13 @@
#define MAX_KBYTES_WRITTEN_PER_SESSION (1000 * 1000)
ImScopeDataWrapper scope_array[EXTRA_SCOPE_TYPES];
typedef struct alignas(hardware_destructive_interference_size) ImScopeDumpInstruction {
typedef struct ImScopeDumpInstruction {
int slot;
int frame;
const char *cause;
} ImScopeDumpInstruction;
std::atomic<ImScopeDumpInstruction> dump_instruction;
alignas(hardware_destructive_interference_size) std::atomic<ImScopeDumpInstruction> dump_instruction;
ImScopeDumpInstruction void_instruction({-1, -1, nullptr});
extern "C" void imscope_record_autoinit(void *dataptr)

View File

@@ -16,7 +16,6 @@
#include <malloc.h>
#include <string.h>
#include <math.h>
#include "common_lib.h"
#include "openair2/PHY_INTERFACE/IF_Module_NB_IoT.h"
#include "defs_eNB.h"
//#include <complex.h>
@@ -93,7 +92,6 @@ static inline void* malloc16_clear( size_t size )
#include "PHY/LTE_TRANSPORT/defs_NB_IoT.h"
#include <pthread.h>
#include "radio/COMMON/common_lib.h"
#include "common/openairinterface5g_limits.h"
#define NUM_DCI_MAX_NB_IoT 32

View File

@@ -29,7 +29,6 @@
#include <malloc.h>
#include <string.h>
#include <math.h>
#include "common_lib.h"
#include <common/utils/LOG/log.h>
#include "assertions.h"

View File

@@ -30,7 +30,6 @@
#include <sys/types.h>
#include <unistd.h>
#include "common_lib.h"
#include "defs_common.h"
#include "defs_RU.h"
#include "impl_defs_top.h"

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