Reads mac->antenna_state to count active TX ports via
__builtin_popcount(nr_get_tx_antenna_bitmask). Caps the partner-search
while-loop bound to (active_tx - 1), so:
active_tx = 4 -> up to 4-way MU-MIMO (3 partners)
active_tx = 3 -> up to 3-way (2 partners)
active_tx = 2 -> up to 2-way (1 partner)
active_tx = 1 -> SU-MIMO only (0 partners)
This couples antenna muting (the existing PHY-side socket control on
port 9999) to spatial parallelism in the MU-MIMO scheduler. Muting a
chain reduces the codebook's orthogonal codeword count, which is the
right semantic for our antenna-muting research: muting reduces the
number of UEs co-scheduled per slot, not the per-UE rate.
Verified runtime behavior on rfsim (30s windows per state):
TX mask 0x0F: 4-way 952 3-way 484 2-way 333 (full 4-TX)
TX mask 0x03: 4-way 2 3-way 8 2-way 5935 (2-TX -> 2-way)
TX mask 0x01: 4-way 0 3-way 0 2-way 11 (1-TX -> SU-MIMO)
Antenna-control commands tested via existing test interface:
echo "SET TX MASK 0x03" | nc -w 2 localhost 9999
Extends the partner search from 1 partner to up to MAX_MU_MIMO_PARTNERS=3
partners, totalling 4 UEs per slot — one per orthogonal x2 class of the
4-port Type I rank-1 codebook.
Orthogonality test relaxed: any distinct x2 (with same x1) is accepted.
The 4 codewords w_(x1,n) for n=0..3 are mutually orthogonal because
they share spatial vector v_x1 and differ only in co-phasing j^n;
<w_a, w_b> = 0 for any a != b.
Each partner gets a distinct DMRS port:
leader -> port 1000 (CDM group 0, mask 0x1)
partner 0 -> port 1001 (CDM group 0, mask 0x2)
partner 1 -> port 1002 (CDM group 1, mask 0x4)
partner 2 -> port 1003 (CDM group 1, mask 0x8)
numDmrsCdmGrpsNoData=2 reserves both DMRS groups.
Class-uniqueness tracked via classes_used bitmask: each successive
partner must be from an unused x2 class.
Pair log: "[nakaolab MU-MIMO] F.S N-way pair RNTI A (x2=0) + RNTI B
(x2=1) + ... on RBs [..]"
Result on rfsim with 9 UEs cycling 4 classes:
8069 total pairings observed
920 4-way (all 4 classes co-scheduled)
2756 3-way
4404 2-way
Phase 1 (rfsim): per-UE deterministic channel injection.
Each UE container sets OAI_UE_CHANNEL_CLASS=k (k in {0..3}). The rfsim
no-chanmod sample-mixing path applies a 4x4 channel:
H^(k)[r,a] = H_orig[r,a] * w_k[a]
where w_k is the k-th codeword of the Type I 4-port rank-1 codebook
(N1=2, N2=1). Multiplying by w_k rotates the channel's optimal PMI
direction to k while preserving the original symmetric kernel's
full-rank reception (so SSB sync still works on all UE classes).
10 UEs cycle through 4 classes (UE_index mod 4), giving 4 mutually
orthogonal PMI populations.
Phase 3 (scheduler): pf_dl partner search now scans connected_ue_list
(not just UE_sched). After leader UE A is allocated, iterate over all
connected UEs, run update_dlsch_buffer/dlsch_to_schedule per candidate,
and pair the first orthogonal-PMI partner that has data + free CCE/PUCCH.
This bypasses PF time-multiplexing — multiple UEs needn't be in
UE_sched simultaneously for MU-MIMO to fire.
Per-slot pairing tag (mumimo_paired_slot in NR_UE_sched_ctrl_t) prevents
double-allocation: incremented per pf_dl call, leader and partner both
tagged on success; subsequent leader iterations skip already-tagged UEs.
Results on rfsim with 10 UEs cycling 4 classes (10-min run):
17000+ pairings observed
Distribution by (leader_class, partner_class):
0->2: 6261 (XOR=2)
3->1: 4382 (XOR=2)
2->0: 2765 (XOR=2)
1->3: 1920 (XOR=2)
All pairs strictly orthogonal per Type I codebook rank-1 rule
(same x1, x2 differ by 2). 2-way MU-MIMO working end-to-end.
Also includes:
- ri_bitlen=0 fix in evaluate_cqi_report (uninitialized r_index)
- OAI_MU_MIMO=1 env to enable, OAI_MU_MIMO_RELAXED=1 for relaxed test
- Diagnostic logs every 200th call: ENTER + per-skip-reason
Two changes on top of the initial MU-MIMO patch (commit 766d0a4):
1. CSI-parse fix in evaluate_cqi_report path: when maxMIMO_layers=1
(and thus ri_Restriction has only one bit set, ri_bitlen=0),
evaluate_ri_report was being skipped, leaving r_index=-1 which
trips the DevAssert(ri>=0) inside evaluate_cqi_report. Fix: when
ri_bitlen=0, pick the single allowed RI directly from the
restriction bitmap. Affects all three reportQuantity branches
(cri_RI_PMI_CQI, cri_RI_LI_PMI_CQI, cri_RI_CQI).
2. MU-MIMO scheduler: relaxed-orthogonality flag and broader
partner search:
- OAI_MU_MIMO_RELAXED=1 bypasses the strict (same-x1, x2-diff-2)
orthogonality test for testbed validation on rfsim where the
symmetric channel makes all UEs report identical PMI.
- Partner search now scans the full UE_sched (skipping leader and
already-paired entries) instead of only iterator+1; this finds
candidates regardless of PF position.
- Diagnostic LOG_W messages on partner-search entry and per-skip
reason (capped to 20 events to avoid log flood).
Findings on this w17 build with 6 UEs at 5Mbps DL each:
- Patch compiles, deploys, gNB does not crash, scheduler reaches
partner-search block on a fraction of slots.
- However eligible_others=0 in every observed partner-search entry:
PF puts only one UE in UE_sched per pf_dl call almost always,
even at cell saturation. The other attached UEs are skipped
earlier (likely HARQ pids pending feedback). So the partner
search has nothing to pair.
- Architectural conclusion: forcing concurrent multi-UE scheduling
on OAI's PF + finite-HARQ scheduler likely needs scanning
connected_ue_list (not just UE_sched), and may need broader
refactoring to pre-allocate slot resources for MU-MIMO pairs.
Adds opt-in MU-MIMO downlink co-scheduling to the production
proportional-fair scheduler (pf_dl). When OAI_MU_MIMO=1 is set in
the gNB process env, after the leader UE is allocated we look ahead
in the PF-sorted candidate list for an orthogonal-PMI partner. If
found and the partner can secure its own CCE+PUCCH, both UEs share
the same RB allocation with distinct DMRS CDM groups (port 1000 vs
port 1001) and are forced to the same MCS/TBS.
Orthogonality test (4-port Type I rank-1 codebook, N1=2, N2=1):
same x1 (same beam), x2 differing by 2 (co-phasing pairs {0,2} or
{1,3}). Both UEs must report rank-1.
Default OFF — set OAI_MU_MIMO=1 to enable. Builds on the upstream
FAPI/PHY MU-MIMO plumbing (commits 3707d52c95, 498d241d75,
9952996c5d) which carries antenna-port and DMRS-CDM metadata down
to the PHY transmit path.
Brings the O-RAN compliant antenna muting feature (originally introduced
on the 'new' branch) onto the latest upstream 2026.w17 tag.
Files added:
openair1/PHY/NR_TRANSPORT/nr_antenna_state.{c,h}
openair1/PHY/NR_TRANSPORT/nr_antenna_control.{c,h}
Files modified (integration hooks):
CMakeLists.txt, executables/nr-gnb.c
openair1/PHY/INIT/nr_init.c, openair1/PHY/defs_gNB.h
openair1/PHY/MODULATION/{beamforming.c,nr_modulation.h,nr_beamforming.c}
openair1/SCHED_NR/nr_ru_procedures.c
openair2/LAYER2/NR_MAC_gNB/mac_proto.h
openair2/LAYER2/NR_MAC_gNB/gNB_scheduler_{primitives,dlsch,uci}.c
Conflicts resolved:
- defs_gNB.h: kept upstream's new headers + appended antenna_state.h
- nr_init.c: dropped reference to upstream-removed nr_srs_info init,
kept nr_init_antenna_state() call
- beamforming.c: upstream moved nr_beam_precoding into new file
nr_beamforming.c; nr_beam_precoding_with_muting added there with
upstream's signature (no slot param)
- nr_modulation.h: aligned _with_muting decl to upstream signature
- nr_ru_procedures.c: muting check applied around upstream's new
fft_shift in nr_feptx, and around per-port memcpy in nr_feptx_prec
- gNB_scheduler_uci.c: SINR-based MCS hint uses get_dl_nrOfLayers(NULL,
sched_ctrl, ...) since nrmac is not in scope at the call site
- mac_proto.h: get_dl_nrOfLayers signature updated to take gNB_MAC_INST*
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Integration `2026.w17`
* !4054 Minor fixes to gNB/UE behavior in RFsim
* !4062 Minor fixes/cleanup in NGAP lib
* !4036 Fix CSI-RS estimation
* !4055 fix (RRC): do not trigger N2 HO when no active PDU sessions are present
* !3988 asn1c inside local tree
* !3762 L1 gNB type0 PDSCH
* !4069 ctest: fix tests when compiling with sanitizers, ignore build directories, fix spsc_q build
* !4067 Small compilation fixes
* !3444 7.2 FHI with XRAN K release
* !4061 T-Tracer & Data Recording v1.1: UL PHY trace modularization and recording enhancements
* !3975 ZMQ radio
* Increase PRACH queue capacity from 8 to 16
* !4059 CI: Various CI adjustments
Closes#1074 and #1073
See merge request oai/openairinterface5g!4063
CI: Various CI adjustments
- RAN-SA-FHI72-CN5G: activate/inactive carriers on VVDN RU - to check if
it improves reliability and stability of the pipeline
- RAN-SA-FHI72-CN5G: update CN with new IP range - avoid IP address
overlap with CN used in RAN-SA-AERIAL-XXX pipelines
- RAN-SA-B200-Module-SABOX-Container: test with deltaMCS in SC-FDMA test
case
- RAN-NSA-B200-Module-LTEBOX-Container: adjust PRACH DTX threshold to
reduce number of false RA attempts on eNB
- remove retx check in RAN-SA-Multi-Antenna-CN5G and RAN-SA-AERIAL-CN5G -
to avoid false CI failure caused by unsuccessful retx check of
interfering UE
- adjust RAN-SA-Multi-Antenna-CN5G and RAN-SA-AERIAL-CN5G configs to
improve UL throughput
Manual testing showed that lowering the RX gain reduces UL
retransmissions and improves overall UL throughput in the 100 MHz 2x2
test case of the SA-Multi-Antenna CN5G pipeline.
Add vvdn-activate-carriers.sh to verify RU PTP synchronization and
configuration. Activates carriers on VVDN RU by editing sysrepo running
datastore (INACTIVE->ACTIVE).
Add vvdn-inactive-carriers.sh to reverse the operation, setting all
carriers back to INACTIVE via sysrepocfg.
After the changes introduced in !3991, multiple PRACH PDUs can be
pushed from L2 to L1 before being processed. This leads to the PRACH
queues filling up and triggering "PRACH occ queue is full" issues on
the gNB.
This commit increases the queue size from 8 to 16, providing additional
buffering and preventing queue overflows as reported in #1072.
ZMQ radio
This is a zmq radio implementation that attempts to integrate NR UE with
the ocudu project.
OAI integration status:
This can currently be used to connect OAI gnb and OAI UE.
Compilation:
cmake --build . --target nr-softmodem nr-uesoftmodem ldpc params_libconfig zmq_radio
Running
- gNB command:
sudo ./nr-softmodem -O ../targets/PROJECTS/GENERIC-NR-5GC/CONF/gnb.sa.band78.fr1.106PRB.usrpb210.conf --gNBs.[0].min_rxtxtime 6 --device.name zmq_radio --zmq.[0].tx_channels tcp://127.0.0.1:4556 --zmq.[0].rx_channels tcp://127.0.0.1:4557
- UE command:
sudo ./nr-uesoftmodem -r 106 --numerology 1 --band 78 -C 3619200000 --ssb 516 --device.name zmq_radio --zmq.[0].tx_channels tcp://127.0.0.1:4557 --zmq.[0].rx_channels tcp://127.0.0.1:4556
A new CI testcase for 2x2 configuration was added.
OCUDU integration status:
The OAI NR UE enters RRC Connected state.
For more details, please refer to the description of MR !3975.
T-Tracer & Data Recording v1.1: UL PHY trace modularization and recording
enhancements
Based on the plan with @schmidtr and @roux-cedric, we agreed to enhance
the data recording in two phases:
- Phase 1: Update Data Recording framework to get All internal Massive
Updates and enhancements through the whole framework public
- Phase2: Restructure T-Tracer Apps and remove all Mems in gNB and UE
Softmodems to Get data on T-tarcer Apps for all activated Messages
symbol-by-symbol, and related Meta-data, then do the aggregation on
the T-tracer App. So, there is No Memory Creation at all on the 5G NR
Stack.
This Merge Request is for Phase 1. We address the comments given in: !3632
Summary: Refactor T-Tracer instrumentation for UL PHY data capture and
improve the data recording application (v1.0 → v1.1). This MR extracts
inline T() macro calls into modular wrapper functions, adds Unix timestamp
fields to trace messages, implements a 4-state shared memory protocol for
gNB/UE tracers, and delivers an improved synchronized recording architecture
in Python.
Changes:
- T-Tracer: Modular UL PHY trace wrappers
- Add T_messages_creator.c/.h with 7 wrapper functions (log_ul_fd_dmrs,
log_ul_fd_chan_est_dmrs_pos, log_ul_fd_pusch_iq,
log_ul_fd_chan_est_dmrs_interpl, log_ul_payload_rx_bits,
log_ul_payload_tx_bits, log_ul_scrambled_tx_bits) and a static
log_ul_common() helper
- Register T_messages_creator.c in the T library CMake build
- Update T_messages.txt definitions: Remove string timestamps and get
time stamp directly from sending_time of T-macro.
- PHY: Replace inline T() calls with wrappers
- gNB (nr_ulsch_demodulation.c, nr_ul_channel_estimation.c,
phy_procedures_nr_gNB.c): replace inline T() calls with
T_messages_creator wrappers
- UE (nr_ulsch_coding.c, nr_ulsch_ue.c): replace inline T() calls with
log_ul_payload_tx_bits and log_ul_scrambled_tx_bits wrappers
- Remove deprecated log_tools.c/.h and its CMake reference
- T-Tracer gNB & UE apps: Enhancements
- Implement 4-state shared memory protocol (WAIT → CONFIG → RECORD → STOP)
for coordination with the data recording app
- Add event_trace_msg_ul_data struct with 27 UL metadata fields
- Refactor event loop to poll()-based with proper signal handling
- Separate project IDs (gNB=2335, UE=2336) and ftok paths for independent
shared memory segments
- Update shared memory sizing: Limit number of records to
NUM_MESSAGES_PER_SLOT=5, 100 slots
- Data Recording App v1.1
- Improved architecture with thread pool and barrier synchronization
- Frame/slot-based message grouping with per-slot validation
- atexit/signal-based cleanup for graceful shared memory detach
- Delete legacy data_recording_app_v1.0.py
- Library improvements
- config_interface.py: remove legacy YAML config reader, streamline to
JSON-only
- shared_memory_interface.py: new module for shared memory
attach/read/protocol
- wireless_parameters_mapper.py: new module for NR parameter mapping
- data_recording_messages_def.py: updated message definitions for
27-field struct
- sigmf_interface.py, sync_service.py, common_utils.py: remove unused
imports and dead code
- Documentation
- Update data_recording.md for v1.1 architecture and usage
7.2 FHI with XRAN K release
Purpose:
Implement the O-RAN 7.2 Fronthaul interface using O-RAN Software Community's
XRAN release K. The new features of the release K can be leveraged and it is
compatible with DPDK version 24.11.4.
For more details please refer to the MR !3444 description.
- refactor timestamp handling
- Replace string-based SENDING_TIME with integer SEC/NSEC fields.
- Designed the shared memory protocol with a 4-state machine (STATE_WAIT=0, STATE_CONFIG=1, STATE_RECORD=2, STATE_STOP=3) for command/control between the recording app and the T-Tracer service
- Implemented two shared memory segments: one for reading commands (addr_rd) and one for writing captured data (addr_wr)
- Created event_trace_msg_ul_data struct mapping all 27 UL metadata fields (frame, slot, datetime, MCS, DMRS parameters, etc.) by name from the T-Tracer database
- Implemented setup_trace_msg_ul_data() using the G() macro to map field names to indices at startup
- Wrote the main event loop using poll() to avoid busy-waiting, with get_event() to receive T-Tracer events from the softmodem socket
Use log_ul_fd_dmrs, log_ul_fd_pusch_iq, log_ul_fd_chan_est_dmrs_pos,
log_ul_fd_chan_est_dmrs_interpl, log_ul_payload_rx_bits.
Remove redundant T_ACTIVE() guards and old timestamp formatting.
ctest: fix tests when compiling with sanitizers, ignore build directories,
fix spsc_q build
- Fix the directory to the current build directory to enable running with
sanitizers with ctest
- add build* to .gitignore to close#1074
- fix spsc_q compilation on non-C++23 compilers to close#1073
This commit was tested with MicroAmp FR2 with 2x2 MIMO,
where PRACH Rx antennas used is 1.
OAI L1 sets the number of PRACH antennas to be the same as for PUSCH.
Ideally, this should be properly fixed. In the meantime, I modified the
logging to debug to reduce noise.
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
First process PUSCH and then PRACH because the PUSCH callback
is called before PRACH callback.
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
* rename read_prach_data() to xran_rx_prach_read_slot()
* in F release, there is no PRACH queue so `frame` and `slot` are
passed as input; in K release, `frame` and `slot` are taken from the PRACH
queue
Note: since the PUSCH callback is the called before PRACH callback, this
commit is correct but the timing for PRACH processing is just a little
bit late. In previous state, PRACH processing was done inside the
function of PUSCH processing.
=> next commit will just reverse the order.
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
* Move PRACH callback in the oaioran.h/c since now used in K release.
* Reset the number of PRACH packets.
* Handle PRACH queue in the same manner as for PUSCH (95c8fa46).
Co-authored-by: Romain Beurdouche <romain.beurdouche@eurecom.fr>
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
K releases added RX error counters
This commits adds a printing of these counters with LOG_I
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
* Initialize leak detector
* Fill IO config field holding the number of mbufs to be allocated
* Allocate and fill `activeMUs` in the FH config
* Start timing source, worker thread and activate CCs upon starting the FH
* Fill `mu_number[0]` in FH config
* Invert trx_oran end and stop
* Set `neAxcUl`
* Delete duplicated oran_eth_state_t struct definition
* gxran_handle is an array which length is equal to the number of RUs
* Fill BBDev VF token in fh_init
Co-authored-by: Romain Beurdouche <romain.beurdouche@eurecom.fr>
Signed-off-by: Teodora Vladić <teodora.vladic@openairinterface.org>
stdatomic.h is a C++23 feature [1]. Thus, "older" compilers such as
gcc-11 (default in Ubuntu 22) do not know this, and can therefore not
correctly compile spsc_q. Fix this by alternatively including atomic for
C++.
Fixes errors such as
stdatomic.h:40:9: error: ‘_Atomic’ does not name a type
[1] https://en.cppreference.com/cpp/header/stdatomic.h
Signed-off-by: Robert Schmidt <robert.schmidt@openairinterface.org>