Skip to content

Publish only the uplink slots no UE transmits in, on the L1-KPM v2 grammar - #16

Open
aferaudo wants to merge 3 commits into
mainfrom
feat/ul-ue-free-slots
Open

aferaudo wants to merge 3 commits into
mainfrom
feat/ul-ue-free-slots

Conversation

@aferaudo

@aferaudo aferaudo commented Nov 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • slot_filter.ue_free_only: the slot codelet skips every slot with a PUSCH, PUCCH or SRS reception (counts from the gNB hook), before any convert; skipped slots are counted as ue_busy. slot_filter.slots picks slot indices.
  • L1-KPM v2: e3sm_layer1.asn is the E3SM-L1-KPM v2 grammar; indications carry cells[0] (iq_samples, n_rx_ant, valid_symbol_mask), the RAN function data lists the 80 stream descriptors. Breaking for dApps: read cells[0].
  • Includes Send the gNB's uplink symbol mask in every L1-KPM indication #11 (valid-symbol-mask), rebased on main.

Tested (arm64 gNB, RU and phone, over the air):

  • Every UL slot is captured at 100/s (4 per frame with TDD 7/6/2, 14 with 2DS7U); with no UE all of them are published, in both modes. v2 JSON on the wire; the dApp reads cells[0].
  • UE traffic with ue_free_only: true: published + ue_busy = all captured UL slots, ue_busy matches the slots with PUSCH/PUCCH in the gNB log, and 0 of ~440k published slots had a PUSCH/PUCCH.
  • test_l1_kpm_sm (new, -DE3C_BUILD_TESTS=ON); codelets verified on x86 and arm64; the v2 commit builds on its own.
  • Not run: an ASN.1 dApp (needs the v2 grammar), ue_free_only: false with a UE.

Known, not from this PR: with 2DS7U at ~130 Mbit/s uplink the gNB rejects ~35 % of UL slots (UL processor is busy), also without jbpf; those slots are neither published nor counted.

Type of change

  • Bug fix
  • New feature / enhancement
  • New Service Model
  • Refactor (no behavior change)
  • Documentation
  • Test / CI / packaging
  • Other (explain):

Linked issue

None.

Twin-repo coordination

  • This PR does not change the E3 wire protocol or public ABI, OR a paired PR exists in each affected twin repo (link below).

Paired PR(s): https://github.com/wineslab/ocudu-e3-private/pull/7 (gNB hook counts, same shared headers). The L1-KPM payload changes for dApps (cells[0]); the dApp side is not in a PR yet.

🤖 Generated with Claude Code

The gNB now tells the slot hook which symbols of each slot are uplink
(valid_symbol_mask: bit s = symbol s is uplink; DL and guard symbols of
a mixed slot are 0; FDD sets all). Carry it to the dApp:

- codelet: copy it from jbpf_ran_slot_ctx into e3_slot_desc, which
  uses 2 of its reserved bytes (same size);
- controller: pass it through SlotSample and send it as
  validSymbolMask (APER) and valid_symbol_mask (JSON), like OAI's
  L1-KPM SM;
- verifier: the slot ctx is now 49 B, not 47.
- docs: describe the mask in docs/service-models.md.

jbpf_e3_slot_api.h and jbpf_e3_ids.h are byte-identical to the ocudu
side, so build the codelets against an ocudu tree with the same change.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
e3sm_layer1.asn is now the grammar of the E3SM-L1-KPM specification,
version 2 (module E3SM-L1-KPM-IEs), which follows NVIDIA's E3 schema
v1.2.0: slot values at the root, cell values in cells[].

- Indications, APER and JSON: timestamp, sfn, slot and n_cells = 1 at
  the root; iq_samples, n_rx_ant and valid_symbol_mask in cells[0].
  n_ue and ues are not sent, since n_ue must count the ues carried.
- RAN function data: version 2, one stream descriptor for each of the
  80 telemetry identifiers (e3sm_layer1_streams.h). The served ones (1,
  4, 5, 6, 8, 10, 1000) are available; ID 1 points at the shared memory
  region, sized from the config because libe3 asks for this data before
  init() opens the region.
- tests/test_l1_kpm_sm (-DE3C_BUILD_TESTS=ON) round-trips both
  encodings.

Breaking change for dApps: iq_samples, n_rx_ant and valid_symbol_mask
moved from the root into cells[0].

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
With slot_filter.ue_free_only, the slot codelet skips every slot in
which the gNB expects a PUSCH, PUCCH or SRS reception, using the counts
the hook now passes (n_ue, n_pucch, n_srs_ue; an unknown count is
busy). It decides before the helper converts the grid, so a skipped
slot costs no convert and no row. It still sends a descriptor, flagged
E3_SLOT_FLAG_UE_BUSY, which the controller counts as drop ue_busy.

- YAML: slot_filter {ue_free_only, slots}; slots fills the selector's
  slot_mask (empty = every slot).
- The selector carries ue_free_only, the descriptor the three counts.
- Empty uplink slots reach the hook only with the gNB's
  expert_phy.allow_request_on_empty_uplink_slot: true.

jbpf_e3_slot_api.h and jbpf_e3_ids.h are byte-identical to the ocudu
side, and the slot ctx is now 52 B: build the codelets against an ocudu
tree with the same change.

Assisted-by: Claude Opus 5.5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant