ns-3 / 5G-LENA · nr moduleGSoC 2026 · mentor review
Architecture & Workflow Report

NR Energy
Framework

A PHY-driven energy consumption framework: the listener decoupling in d5f36416, the TR 38.840 UE and TR 38.864 gNB power models, the connected-mode DRX state machine — and validation against measured 5G base-station hardware.
01 — The decoupling decisionlistener
02 — Component architecturemodels
03 — Workflow sequences · UE / DRX / gNBtraces
04 — Flexible symbols & micro-sleepTDD
05 — ns-3 validation, real traces & calibrationdata
p. 01 / 11
01 — The decoupling decision
commit d5f36416 · 2026-07-10
2 files · +2 / −35

The early NrPhyEnergyListener deliberately held pointers into the scheduler and the UE-MAC. They were added with the ambition of one day driving a future energy-aware scheduler that could react to the energy model in the loop. But that scheduler never materialised, and the pointers were never read. Everything the energy formulas actually need is already visible at the PHY:

  • sf (bandwidth utilization) is derived from PHY-layer per-slot stats — not from the scheduler.
  • DRX state belongs to a dedicated model — not to a UE-MAC hook.
Removed in d5f36416
✕  SetScheduler(Ptr<NrMacSchedulerNs3>)
✕  SetUeMac(Ptr<NrUeMac>)
✕  m_scheduler · m_ueMac
✕  #include "nr-mac-scheduler-ns3.h" · "nr-ue-mac.h"
The commit locked in the framework's core design rule: listeners are driven purely by PHY trace sources.
Before
NrPhyEnergyListener
combined UE + gNB bridge
m_uePhy · m_gnbPhy
m_ueModel · m_gnbModel
m_scheduler ← never read
m_ueMac     ← never read
NrMacSchedulerNs3
MAC · unused coupling
NrUeMac
MAC · unused coupling
After
NrPhyEnergyListener
PHY events only
m_uePhy · m_gnbPhy
m_ueModel · m_gnbModel
What it enabled next
→ split: NrUePhyEnergyListener
→ split: NrGnbPhyEnergyListener
→ DRX owned by NrUeDrxModel
p. 02 / 11
02 — Component architecture
one listener per node type ·
listeners know events, not formulas
UE node
gNB node
NrUePhy
· existing PHY
traces: ReportDownlinkTbSize · ReportUplinkTbSize
NrUePhyEnergyListener
bridge · extracts params, drives models
SetPhy() · SetEnergyModel() · SetDrxModel()
NrUeEnergyModel
TR 38.840 §8 power states
ChangeState() · ApplyBwpScaling()
SetUlTxPowerDbm() · E = P·Δt
NrUeDrxModel
C-DRX timers · owns sleep
Start() · NotifyDataActivity()
NrGnbPhy
· existing PHY + one new hook
NEW TRACE SlotEnergyStats · fires every slot
splits used symbols by direction — DL-data ·
UL-data · PDCCH · PUCCH/SRS, + REG & RB
counts, so each is charged at its own power
NrGnbPhyEnergyListener
bridge · computes sa / sf / sp
SetPhy() · SetEnergyModel()
NrGnbEnergyModel
TR 38.864 §5 · P1..P5 + P_DL / P_UL formulas
UpdateSymbolPower(sa,sf,sp,type) ×14 · FinalizeSlotEnergy()
energy::EnergySource
standard ns-3 DeviceEnergyModel attach
p. 03 / 11
03a — UE workflow · sequence
TR 38.840 §8.1 event triggers ·
BWP-based active-state scaling
TR 38.840 Table 18 · rel. power
StateFR1FR2
Deep sleep11
Light sleep2020
Micro sleep4538
PDCCH-only100175
SSB / CSI-RS100175
PDCCH+PDSCH300350
UL Tx250–700350
Active DL states additionally carry the cached BWP × antenna × blind-decoding multipliers (§8.1.3). Sleep and UL states are unscaled.
p. 04 / 11
03b — DRX workflow · sleep ownership
NrUeDrxModel · TS 38.321 semantics ·
power model, not protocol MAC
Timer state machine (runs on the ns-3 scheduler)
StartCycle every LongCycle → ChangeState(PDCCH_ONLY)
NotifyDataActivity (from listener) → restart inactivity timer
EndOnDuration + no inactivity running → GoToSleep()
InactivityExpired → GoToSleep()
GoToSleep() — sleep-depth decision
gap = nextOnDuration − now
gap ≥ DeepSleepThreshold  →  DEEP_SLEEP (1 unit)
gap < DeepSleepThreshold  →  LIGHT_SLEEP (20 units)
Deeper sleep only amortises its transition cost over a long inactive gap (TR 38.840 §8.1) — the threshold encodes that break-even point.
Power over one long cycle (relative units, log-feel scale)
LongCycle = 160 ms
OnDuration = 8 ms
InactivityTimer = 10 ms
DeepSleepThreshold = 20 ms
Attributes follow TS 38.321 names; defaults match the TR 38.840 power-saving evaluation baseline.
p. 05 / 11
03c — gNB workflow · sequence
TR 38.864 §5.2 symbol-level accounting ·
driven by the SlotEnergyStats trace
TR 38.864 §5.1 active-power formulas
P_DL = P3 + sa·(P4−P3)·[A + (sf·sp/η)·(1−A)]
P_UL = P3 + sa·(P5−P3)  · no sf/sp term
η = 1.0  (or 0.76 when sf·sp < 0.5, EtaDual)
Table 5.1-3 · BsCat1 / Set1 (macro FR1)
P1 deepP2 lightP3 microP4 DLP5 UL
12555280110
Also in the lookup: Cat1/Set2, Cat1/Set3, Cat2/Set1. A = 0.4 default (AntennaRatio_A).
p. 06 / 11
04 — Flexible slots are not idle
per-symbol classification ·
when micro-sleep (P3) is charged
The issue. A TDD pattern names slots D / F / U. Mapping the F ("flexible") slot type to guard-or-idle power under-charges the gNB: in 5G-LENA an F slot is dually schedulable. DL ctrl + DL data + UL data + UL ctrl can all be placed in it.
The fix. Ignore the slot type entirely. The scheduler resolves every allocated symbol to a definite DL or UL direction (var-TTIs) before the slot executes, and SlotEnergyStats reports that resolution. The energy model keys off per-symbol activity, not slot labels . (a fully-scheduled F slot has zero idle symbols. Only a symbol that no var-TTI allocates is genuinely idle.)
Example resolved F slot — 14 OFDM symbols
0
13
DL ctrl → P_DL @ sf_ctrl DL data → P_DL @ sf_data UL data / ctrl → P_UL unallocated → P3 micro-sleep
When is micro-sleep chosen?
SideRule
gNBAny symbol no var-TTI allocates is charged at P3 (55 units) — idle = 14 − scheduled symbols, per slot.
gNBP3 is also the static baseline: the model's initial discrete state, and the approximation used while TRANSITIONING (wake-up).
UEMicro-sleep (45 / 38 units) is defined in the table, but sleep is DRX-owned: light vs deep chosen by the 20 ms gap rule; after an active slot the UE returns to PDCCH-only monitoring.
p. 07 / 11
05a — Time-domain check A
model driven directly by the load trace ·
replayed through the compiled NrGnbEnergyModel
NETDATA 24h REPLAY · measured vs model(t)MAPE 4.0% · 7.2%
Two NetData cells: measured RRU power vs NrGnbEnergyModel driven by the measured load, over 24 hours
Each cell's 24h PRB trace drives the compiled model (PRB% → sf); its own OLS-fit PowerUnit / AntennaRatio_A emit absolute watts.
The two measurement datasets
DatasetSourceScale · cadenceLoad / power signal
ITUITU AI/ML challenge (Kaggle)923 BSs · 1 hnormalized load / energy
NetDataTsinghua fib-lab (Git-LFS)~20 k cells · 30 minPRB usage % (= sf) / RRU W
Complementary: ITU is a large macro fleet on a normalized load axis; NetData reports the PRB usage ratio — which is the TR 38.864 sf directly — alongside absolute RRU watts. Shutdown / deep-sleep rows are excluded so both describe the active-power curve.
The two replayed cells · scope
· 12326-0 diurnal — 229–500 W, morning ramp + evening fall-off, MAPE 4.0%.
· 15598-5 bursty on/off — 59–272 W, near-idle then sharp evening bursts, MAPE 7.2%.
· Each cell's own OLS-fit PowerUnit / AntennaRatio_A emit its absolute watts — a genuine per-cell calibration.
· Check A calls the model formula in a loop. Check B (next) removes that shortcut — power is read from the live listener inside a running sim.
p. 08 / 11
05b — Time-domain check B
full 5G-LENA scenario · power read from the live
NrGnbPhyEnergyListener — no formula in the loop
NETDATA 24h · LIVE-LISTENER SIMcorr 0.99 · MAPE 9.0% / 4.6%
Two NetData cells: measured RRU power vs ns-3 power read from NrGnbPhyEnergyListener in a running simulation, over 24 hours
Power read from the real listener in a running sim tracks every ramp, peak and dip — corr(load, power) = 0.99 for both cells.
Injecting the load · as a time-varying UDP rate
PRB usage is a scheduler output — it cannot be injected directly. So cttc-nr-energy-loadtrace modulates the aggregate UDP-downlink rate over time to load(t) · (saturating rate), lets the NR scheduler decide the resulting resource use, and samples the gNB power once per step.
trace load(t) → UDP DL rate → NR scheduler → used symbols/slot
  → NrGnbPhy "SlotDataStats" → NrGnbPhyEnergyListener
    → NrGnbEnergyModel → power(t)
Unlike check A (formula in a loop), the whole PHY → listener → model chain runs live — a ~48 s simulation per cell.
The full pipeline behaves
· 12326-0 diurnal MAPE 9.0% · 12838-2 bursty MAPE 4.6% — correlation 0.99 both.
· Idle floor matches (sim 239 W vs measured 229 W); at high load the full symbol-level pipeline runs a few % hotter.
· That heat is the symbol-accounting signature — the same reason the sweep saturates at 303 W where the bare formula gives 280 — re-absorbed by the U / A knobs per deployment.
· Load matching is approximate by construction: driving PRB usage through traffic is open-loop.
p. 09 / 11
05c — Calibrating the two knobs
PowerUnit (U) & AntennaRatio_A (A) ·
closed-form from a per-cell OLS fit
The model · fixed table + two free knobs
P_DL(sf) = U · [ P3 + (P4−P3)·( A + sf·(1−A) ) ]
{P1..P5} = {1, 25, 55, 280, 110} fixed by TR 38.864 Table 5.1-3. Only U and A are tunable — exposed as ns-3 attributes.
KnobAttributeMeaning
UPowerUnitwatts per relative power-unit — sets absolute scale
AAntennaRatio_Astatic power fraction ∈ [0,1] — idle-vs-active split
Closed-form calibration · per BS / cell
fit an OLS line:   power = b + m · load
U = (b + m) / P4   — full load (sf=1) anchors P4
A = ( b/U − P3 ) / (P4 − P3)   — where idle sits in the P3→P4 range
The intercept b is the measured idle power, b+m the full-load power. Pinned to the compiled model by asserting the unit-test anchors P_DL(1)=280 · P_DL(0)=145 · 167.5 @ sa=0.5.
Fitting U and A from measured power vs load
Recovered on real fleets
FleetFittedRecovered AReading
ITU688 macro BSs0.41≈ the 0.40 default — macro reference needs no re-tuning
NetData4 775 cells−0.10 → 0 flooridle below P3 — small/medium RRUs more energy-proportional
p. 10 / 11
ns-3 / 5G-LENA · nr moduleGSoC 2026 · mentor review

PHY-driven. Spec-anchored.
Hardware-checked.

Decoupled
Listeners hold no scheduler or MAC references — every energy input is a PHY trace. One listener per node type, single responsibility.
Spec-anchored
TR 38.840 Table 18 UE states with §8.1.3 scalings; TR 38.864 P1–P5 + P_DL/P_UL formulas with symbol-level slot accounting; TS 38.321 DRX semantics.
Validated
Real 24h NetData load traces reproduced in ns-3. Model-driven (check A) MAPE 4.0% / 7.2%; power read from the live listener in a full sim (check B) MAPE 9.0% / 4.6%, correlation 0.99.
p. 11 / 11