Link Adaptation: OLLA & ILLA (DL & UL) in 5G NR
How the gNB tracks the channel — inner-loop (CQI→MCS) and outer-loop (HARQ-driven BLER control), for both downlink and uplink.
A radio channel changes faster than any measurement can describe it. Link adaptation solves that with two loops running at different speeds: a fast one that turns the most recent channel report into a modulation and coding scheme, and a slow one that corrects the fast one's systematic bias using the only ground truth available -- whether the transport block actually decoded. The fast loop is specified in detail. The slow loop is specified nowhere at all, which is why it is where vendors differ, where things go wrong quietly, and where a stale offset can hold a UE's throughput down for minutes after the condition that caused it has gone. This document covers both loops in both directions, the CSI and SRS machinery that feeds them, the interaction with uplink power control, eleven worked calculations and nine failure modes.
Contents
- 01Why Fixed Modulation and Coding Cannot Work
- 02The Asymmetry That Shapes Everything
- 03Two Loops, Two Time Scales
- 04CSI Reporting: CQI, PMI, RI, CRI and LI
- 05CSI-ReportConfig: What, When, and On What
- 06The Three CQI Tables
- 07From CQI to MCS to Transport Block Size
- 08The Age of the Estimate: a Latency Budget
- 09The Downlink Outer Loop: What OLLA Actually Is
- 10The Step Ratio and the Convergence Time
- 11OLLA Pathologies: Where the Field Problems Live
- 12The Uplink Inner Loop: SRS and PUSCH DMRS
- 13Uplink MCS Selection and the Bandwidth Trade
- 14The Uplink Outer Loop and the Ambiguous CRC
- 15Link Adaptation Against Power Control
- 16Choosing a Target BLER
- 17Rank Adaptation: When Fewer Layers Win
- 18Parameter and Configuration Reference
- 19Failure Modes and What Each One Means
- 20Configuration Reference (ASN.1)
- 21Two More Worked Calculations
- 22Illustrative Message Traces
- 23Release Deltas: Rel-15 to Rel-18
- 24Reading Link Adaptation in Logs: A Checklist
- 25Glossary
- 26References
1. Why Fixed Modulation and Coding Cannot Work
Every downlink and uplink transport block in NR is sent with a chosen modulation order, a chosen code rate and a chosen number of spatial layers. Nothing forces those choices to change from one slot to the next. A gNB could be built to send every PDSCH at MCS 15 forever, and it would work — for exactly one UE, standing in exactly one place, on a day when nothing else was transmitting.
The problem is the range. Across a single FR1 macro cell the useful post-combining SINR spans roughly 25 to 30 dB, from about −6 dB at the far edge with interference from three neighbours to well past +25 dB under the antenna. Over that range the spectral efficiency a receiver can actually sustain moves by more than a factor of twenty. MCS table 1 spans 0.2344 to 5.5547 bits per resource element TS 38.214 Table 5.1.3.1-1 — a 23.7× range, or 13.8 dB in rate terms — and it needs all 29 usable entries to cover the cell. Pick any single entry and you have chosen which half of your cell to fail.
| If the MCS were fixed at | Spectral efficiency | First-tx BLER at 5 dB SINR | Rate at 20 dB SINR, against what was possible | Net effect |
|---|---|---|---|---|
| MCS 4 (QPSK, R = 308/1024) | 0.6016 bits/RE | well under 1 % | 0.60 of 5.55 bits/RE — 11 % of capacity | The cell edge works. Everybody else is throttled by a factor of nine. |
| MCS 15 (16QAM, R = 616/1024) | 2.4063 bits/RE | ≈ 90 % | 2.41 of 5.55 bits/RE — 43 % of capacity | The mid-cell works. The edge cannot decode at all and burns HARQ retransmissions until RLC gives up. |
| Adapted per grant | 0.2344 … 5.5547 bits/RE | held at the target, ≈ 10 % | essentially all of it | Every UE gets the highest rate its own channel supports, at a controlled error rate. |
Table 1. Why the mechanism exists at all. The BLER figures come from the illustrative SINR-to-BLER model used throughout this document (§10); the spectral efficiencies are exact from TS 38.214 Table 5.1.3.1-1.
So link adaptation is not a feature that gets triggered. It runs on every single scheduling decision, for every UE, in both directions, for as long as the UE is in RRC_CONNECTED. There is no linkAdaptationConfig IE and no procedure clause describing it end to end. What 3GPP specifies are the inputs — the CSI reporting framework, the CQI tables, the SRS configuration — and the outputs — the MCS tables and the transport block size procedure. The decision in the middle is left to the implementation, deliberately.
This is the single most important fact about the topic and it explains most of what is confusing about it. The two ends of the chain are rigidly specified and bit-exact; the decision joining them is a vendor algorithm. Two gNBs given a byte-identical CSI report may schedule different MCS indices and neither is non-compliant. When a link-adaptation problem turns out to be a difference between two vendors' schedulers, that is not a bug in either of them.
2. The Asymmetry That Shapes Everything
Before any of the detail, one structural fact. In the downlink, the channel the gNB needs to know about is the channel at the UE. The gNB cannot measure it. It has to transmit a reference signal, wait for the UE to measure it, wait for the UE's report to arrive on an uplink control channel, and then work with a four-bit number that a receiver it did not design produced using a model it cannot see.
In the uplink, the channel the gNB needs to know about is the channel at the gNB. It measures it directly, on the UE's sounding reference signals and on the demodulation reference signals of every PUSCH the UE has ever sent. No reporting, no quantisation, no round trip, no other vendor's receiver model in the path.
| Consequence | Downlink | Uplink |
|---|---|---|
| Estimate freshness at decision time | 3–13 ms typical with reportSlotConfig = slots20; see §8 | 0.5–2 ms from periodic SRS, and effectively zero from the DMRS of the PUSCH just received |
| Estimate resolution | 4-bit CQI, ~1.5–1.8 dB per step in the 64QAM region | Whatever the gNB's own estimator produces — commonly 0.1 dB internal resolution |
| What the outer loop's ground truth is | A HARQ-ACK bit the UE reports on PUCCH or multiplexed in PUSCH | The gNB's own CRC check on the received PUSCH |
| How the ground truth can lie | A missed PDCCH means no HARQ-ACK at all; DTX read as NACK | A missed grant, an LBT failure on shared spectrum, or a genuine decode failure are indistinguishable |
| Coupled control loops | None with overlapping authority — downlink power is not per-UE closed-loop | Uplink power control is a second closed loop responding to the same channel (§15) |
Table 2. The asymmetry, itemised. Everything on this table is a consequence of one thing: in the downlink the gNB must ask, in the uplink it can look.
3. Two Loops, Two Time Scales
Both directions use the same two-loop structure, and it is worth being precise about what each loop is for, because the names invite the wrong intuition.
The inner loop — inner-loop link adaptation, ILLA — is a feedforward mapping. It takes the most recent channel estimate, adds the outer loop's correction, and looks up an MCS. It has no memory and no error signal of its own: if the channel estimate is wrong, the inner loop's output is wrong in exactly the same way. It is recomputed for every grant, so it is fast, and it is the part 3GPP specifies (the tables at both ends of it, at least).
The outer loop — outer-loop link adaptation, OLLA — is a feedback controller, specifically an integrator with an asymmetric, deliberately tiny step. It has exactly one state variable per UE: an offset in decibels. It has exactly one input: did the last transport block decode. It updates on every HARQ feedback, which is more often than the inner loop's input arrives — and yet it is the slow loop, because a single update moves the offset by a few hundredths of a decibel. Its time constant is set by the step size, not the update rate.
| Inner loop (ILLA) | Outer loop (OLLA) | |
|---|---|---|
| What it is | A feedforward table lookup | A feedback integrator with one state variable per UE |
| Input | CQI report (DL) or SRS/DMRS SINR estimate (UL), plus the outer loop's offset | HARQ-ACK (DL) or PUSCH CRC result (UL) |
| Update rate | Every grant — up to once per slot | Every HARQ feedback — also up to once per slot |
| Effective time constant | None; it has no memory | Tens to hundreds of TTIs, set by the step size |
| What it cannot do | Notice that it is wrong | React to a fast fade — it is far too slow, and that is by design |
| Specified in 3GPP? | The tables yes, the mapping between them no | Not at all — not the rule, not the steps, not the state, not the reset behaviour |
| Typical symptom when it breaks | MCS that does not track the reported CQI at all | MCS that tracks the CQI faithfully but sits several steps below it for minutes |
Table 3. The division of labour. The last row is the one worth memorising: it is the fastest way to tell which loop you are looking at in a log.
The inner loop is fast and wrong; the outer loop is slow and right. The inner loop is wrong because its input is stale and quantised and came from someone else's receiver. The outer loop is right because its input is the only unambiguous fact in the whole system — the CRC either passed or it did not — but it is right only on average, and only after enough transport blocks to average over.
4. CSI Reporting: CQI, PMI, RI, CRI and LI
Downlink link adaptation begins with the UE measuring channel state information on reference signals the gNB transmits, and reporting a set of quantities back. Five quantities matter here, and they are not interchangeable: three of them describe how much the channel can carry, and two of them describe how to transmit into it.
| Quantity | Expands to | What it answers | Size on the wire | Who uses it |
|---|---|---|---|---|
| CRI | CSI-RS Resource Indicator | Which of the CSI-RS resources in the set gave the best channel — in practice, which beam | ⌈log₂(K)⌉ bits for K resources in the set | Beam selection, before the MCS decision is even meaningful |
| RI | Rank Indicator | How many spatial layers this channel can usefully carry | 1–3 bits, bounded by maxRank / ri-Restriction | Layer count in the DCI antenna port field; multiplies the TBS |
| PMI | Precoding Matrix Indicator | Which precoder from the configured codebook the gNB should use | Codebook-dependent; 5 bits for Type I single-panel, 4 ports, rank 1–2 | The precoder, not the MCS. A wrong PMI shows up as a low CQI next report. |
| CQI | Channel Quality Indicator | Which table entry the UE believes it could decode at the table's target error rate | 4 bits per CQI value; one wideband value, plus differentials per subband if configured | The inner loop. This is the number the MCS comes from. |
| LI | Layer Indicator | Which of the reported layers is the strongest | ⌈log₂(RI)⌉ bits | PT-RS association and DMRS port ordering — see the companion 33 DMRS |
Table 4. The five CSI quantities. Only CQI feeds the inner loop directly, but RI multiplies its result and CRI and PMI determine the channel the CQI was measured on — which is why a CQI value reported against a stale beam is worthless.
The crucial subtlety is what CQI means. It is not a measurement of SINR and it is not a measurement of anything else. TS 38.214 clause 5.2.2.1 defines it as the highest index in the configured CQI table such that the UE could receive a hypothetical PDSCH, with a specified set of assumptions about its structure, with a transport block error probability not exceeding the table's target. That is a statement about the UE's own receiver, expressed as a rate. Two UEs standing beside each other, seeing the identical channel, may legitimately report CQI values two steps apart because one has a better receiver — and the reported value is correct for both of them.
This is why the outer loop is not optional. The bias between what a UE reports and what it can actually decode is per-UE, per-chipset, per-firmware-version and per-environment. No amount of care in the gNB's CQI-to-SINR mapping removes it, because it is not a property of the gNB.
| reportQuantity value | Fields reported | Used for |
|---|---|---|
none | Nothing | A report configuration used only to trigger measurement, or a placeholder |
cri-RI-PMI-CQI | CRI, RI, PMI, wideband and optionally subband CQI | The workhorse for closed-loop MIMO PDSCH scheduling |
cri-RI-i1-CQI | CRI, RI, i1, CQI | As above, with a rate estimate |
cri-RI-CQI | CRI, RI, CQI — no PMI | Non-codebook or reciprocity-based precoding, where the gNB derives the precoder from SRS instead |
cri-RSRP, ssb-Index-RSRP | Beam identity and RSRP | Beam management, not link adaptation — see the companion 20 Measurements and Events |
cri-RI-LI-PMI-CQI | All five | PDSCH with PT-RS, where the layer indicator is needed |
Table 5. reportQuantity values in CSI-ReportConfig TS 38.331. Two of these carry no CQI at all, and a scheduler configured with one of them is running its inner loop on something other than a reported rate.
A configuration with reportQuantity = cri-RSRP on the only CSI-ReportConfig a UE has is a real and easily-made mistake. The reports arrive, the counters increment, the beam management works perfectly — and the inner loop has no CQI, so it falls back to whatever the vendor's default is, usually a fixed low MCS. The symptom is a UE with excellent RSRP, healthy reporting, near-zero BLER and about a third of the throughput it should have. Check reportQuantity before you check anything else.
5. CSI-ReportConfig: What, When, and On What
A CSI-ReportConfig is the object that binds three things together: a set of CSI-RS resources to measure on, a set of quantities to report, and a schedule and channel to report them on. It lives in CSI-MeasConfig inside ServingCellConfig, alongside the NZP-CSI-RS-ResourceSet and CSI-IM-Resource definitions it points at. A UE typically has several — one for the serving beam with a short period, one aperiodic configuration for on-demand reporting, sometimes one for beam management with cri-RSRP.
| Field | Values | Typical | What it decides |
|---|---|---|---|
reportConfigId | 0 … 47 | 0, 1, 2 | The handle used by the aperiodic trigger states and by csi-ReportConfigToReleaseList |
resourcesForChannelMeasurement | A CSI-ResourceConfigId | the serving-beam NZP set | Which CSI-RS the CQI describes. Get this wrong and the report is about a different beam. |
csi-IM-ResourcesForInterference | A CSI-ResourceConfigId, or absent | configured in loaded networks | Whether the reported CQI includes measured inter-cell interference or only thermal noise. Omitting it makes every CQI optimistic under load. |
reportConfigType | periodic / semiPersistentOnPUCCH / semiPersistentOnPUSCH / aperiodic | periodic | When the report happens and on which channel. See the next table. |
reportQuantity | See §4 | cri-RI-PMI-CQI | Which of the five quantities are in the payload |
reportFreqConfiguration | cqi-FormatIndicator widebandCQI/subbandCQI; csi-ReportingBand bitmap | widebandCQI | One CQI for the whole BWP, or one per subband. Subband reporting is what makes frequency-selective scheduling possible. |
timeRestrictionForChannelMeasurements | configured / notConfigured | notConfigured | configured forbids the UE from averaging across CSI-RS occasions — it must use only the most recent one. Fresher, noisier. |
cqi-Table | table1 / table2 / table3 | table2 for eMBB | Which CQI table the reported index refers to. See §6. |
codebookConfig | Type I single/multi-panel, Type II, port selection | Type I single-panel | The PMI codebook, and therefore the PMI payload size and the achievable precoding accuracy |
Table 6. The CSI-ReportConfig fields that matter to link adaptation TS 38.331. The csi-IM-ResourcesForInterference row is the one most often left unconfigured and the one that most often explains a whole cell reporting optimistically.
| reportConfigType | Carried on | Triggered by | Latency | When you want it |
|---|---|---|---|---|
periodic | PUCCH | Nothing — it just happens, every reportSlotConfig slots at reportSlotOffset | Fixed, and up to a full period stale | Steady-state connected-mode operation. Cheap, predictable, always there. |
semiPersistentOnPUSCH | PUSCH | Activated by DCI 0_1 with an SP-CSI-RNTI-scrambled CRC | As periodic once active | Larger payloads — Type II codebooks, many subbands — that will not fit in a PUCCH |
aperiodic | PUSCH | One CSI request field in DCI 0_1, selecting a trigger state | Bounded by the CSI computation time Z / Z′ TS 38.214 cl. 5.4 | Immediately before a large transmission, or to resolve a disagreement between the loops |
Table 7. The four report types. Aperiodic reporting is the only one that lets a scheduler ask a question, and it is the tool of choice when the outer loop and the reported CQI have diverged and you need a fresh, unfiltered opinion.
Periodic reporting overhead, worked. One CSI-ReportConfig, wideband cri-RI-PMI-CQI, 4 CSI-RS ports, Type I single-panel, maxRank 2. Payload: CRI ⌈log₂2⌉ = 1 bit, RI 1 bit, wideband CQI 4 bits, PMI i₁,₁ 3 bits + i₁,₃ 1 bit + i₂ 1 bit = 5 bits. Total 11 UCI bits, which fits PUCCH format 2.
At reportSlotConfig = slots20 and 30 kHz SCS, that is one report every 10 ms, so 1100 bit/s of uplink control per UE — negligible. The cost is not the bits, it is the PUCCH resource: format 2 over 1 PRB and 2 symbols, once per 20 slots, per UE. At 200 connected UEs that is 10 PUCCH occasions per slot to place, and it is the PUCCH resource pool, not the bit rate, that sets the practical floor on report periodicity.
6. The Three CQI Tables
TS 38.214 clause 5.2.2.1 defines three CQI tables, selected by cqi-Table in CSI-ReportConfig. They are not three levels of precision on the same scale. They are three different scales, and two of them target different error rates.
| cqi-Table value | TS 38.214 table | Highest modulation | Efficiency range (bits/RE) | Target block error rate | Configured for |
|---|---|---|---|---|---|
table1 | Table 5.2.2.1-2 | 64QAM | 0.1523 … 5.5547 | 10⁻¹ | The Rel-15 baseline; still the default in many deployments |
table2 | Table 5.2.2.1-3 | 256QAM | 0.1523 … 7.4063 | 10⁻¹ | eMBB on a UE that supports 256QAM PDSCH and mcs-Table = qam256 |
table3 | Table 5.2.2.1-4 | 64QAM | 0.0586 … 4.5234 | 10⁻⁵ | URLLC, alongside mcs-Table = qam64LowSE or an MCS-C-RNTI |
Table 8. The three CQI tables. The target BLER column is the one that surprises people: the choice of table is how the target error rate is signalled to the UE. Nothing else in the CSI configuration carries it.
That last point deserves stating plainly. A UE reporting CQI 9 against table1 is saying "I could decode 2.4063 bits/RE with at most a 10 % block error probability." A UE reporting CQI 9 against table3 is saying "I could decode 1.4766 bits/RE with at most a 0.001 % block error probability." Those are wildly different statements about the channel, and the difference is not in the reported number — it is in the table the reader is expected to already know about.
| CQI index | Modulation | Code rate × 1024 | Efficiency (bits/RE) |
|---|---|---|---|
| 0 | out of range | — | — |
| 1 | QPSK | 78 | 0.1523 |
| 2 | QPSK | 120 | 0.2344 |
| 3 | QPSK | 193 | 0.3770 |
| 4 | QPSK | 308 | 0.6016 |
| 5 | QPSK | 449 | 0.8770 |
| 6 | QPSK | 602 | 1.1758 |
| 7 | 16QAM | 378 | 1.4766 |
| 8 | 16QAM | 490 | 1.9141 |
| 9 | 16QAM | 616 | 2.4063 |
| 10 | 64QAM | 466 | 2.7305 |
| 11 | 64QAM | 567 | 3.3223 |
| 12 | 64QAM | 666 | 3.9023 |
| 13 | 64QAM | 772 | 4.5234 |
| 14 | 64QAM | 873 | 5.1152 |
| 15 | 64QAM | 948 | 5.5547 |
Table 9. CQI table 1 in full — TS 38.214 Table 5.2.2.1-2, the 4-bit CQI table for cqi-Table = table1. Index 0 is not a rate; it means no rate in this table is decodable, which is a different and much more serious statement than CQI 1.
CQI 0 is worth its own paragraph in a runbook. It is not "very bad channel" — it is out of range, meaning the UE cannot decode even 0.1523 bits/RE at the table's target. A scheduler that treats CQI 0 as "CQI 1 minus a bit" and schedules MCS 0 will get a NACK, drive the outer loop down, and try again. The correct response to a sustained CQI 0 is not a lower MCS, it is a beam change, a rank reduction, slot aggregation, or an admission that the UE has left coverage — see the companion 16 RLM and RLF.
Note the internal structure of table 1: its 64QAM entries — 466, 567, 666, 772, 873, 948 over 1024 — are exactly the code rates of MCS indices 18, 20, 22, 24, 26 and 28 in MCS table 1. This is not a coincidence and it is useful: for the upper half of table 1, the CQI-to-MCS mapping has an exact answer with no interpolation at all. CQI 11 corresponds precisely to MCS 20. The vendor freedom that §7 describes lives in the corrections applied around that exact mapping, not in the mapping itself.
A mismatch between the cqi-Table the UE was configured with and the table the gNB's scheduler assumes is one of the nastiest faults in this whole area, because everything looks healthy. Suppose the UE has table2 and the scheduler assumes table1. The UE reports CQI 11 meaning 5.1152 bits/RE; the scheduler reads 3.3223 and schedules accordingly. Reports arrive on time, BLER is beautiful — well under 1 % — and the cell runs at about 65 % of the throughput it is capable of, forever, because the outer loop's job is to push the rate up towards the target BLER and it can only do that 0.056 dB at a time until it hits its clamp. Look for the combination: healthy CQI, healthy reports, BLER an order of magnitude below target, MCS pinned by the offset clamp.
7. From CQI to MCS to Transport Block Size
Here is the whole chain, worked end to end for one report, with real numbers. The geometry is the same one the companion 05 HARQ document uses for its transport block size example, so the two can be read against each other.
Given: a periodic wideband report arrives with CRI 0, RI 1, CQI 11, cqi-Table = table1. The UE is allocated 100 PRB at 30 kHz SCS, 12 OFDM symbols of PDSCH, one front-loaded DMRS symbol with two CDM groups without data (N_DMRS = 12 RE/PRB), xOverhead absent so N_oh = 0, mcs-Table absent so MCS table 1 applies. The outer-loop offset for this UE currently stands at −1.5 dB.
Step 1 — CQI to spectral efficiency. CQI table 1, index 11: 64QAM, R = 567/1024, efficiency 3.3223 bits/RE.
Step 2 — efficiency to an equivalent SINR. Not specified. On this document's illustrative 3 dB-per-bit/RE scale, 3.3223 bits/RE at a 10 % target corresponds to 14.1 dB.
Step 3 — apply the outer loop. 14.1 − 1.5 = 12.6 dB.
Step 4 — SINR to MCS. The highest MCS table 1 entry whose 10 % point sits at or below 12.6 dB is MCS 18: Q_m = 6, R = 466/1024 = 0.4551, efficiency 2.7305 bits/RE. Without the offset it would have been MCS 20 — the exact table-1 twin of CQI 11.
Step 5 — MCS to TBS TS 38.214 cl. 5.1.3.2:
N_RE′ = 12 × 12 − 12 − 0 = 132 RE per PRB
N_RE = min(156, 132) × 100 = 13 200 RE
N_info = 13 200 × 0.4551 × 6 × 1 = 36 042 bits
N_info > 3824, so n = ⌊log₂(36 042 − 24)⌋ − 5 = 15 − 5 = 10, 2¹⁰ = 1024
N_info′ = max(3840, 1024 × round(36 018 / 1024)) = 1024 × 35 = 35 840
R = 0.455 > 1/4 and N_info′ > 8424, so C = ⌈35 864 / 8424⌉ = 5 code blocks
TBS = 8 × 5 × ⌈35 864 / 40⌉ − 24 = 40 × 897 − 24 = 35 856 bits = 4482 bytes
Check. Coded bits available = 13 200 × 6 = 79 200. Payload plus the transport-block CRC plus five code-block CRCs = 35 856 + 24 + 120 = 36 000. Effective code rate = 36 000 / 79 200 = 0.4545 against a target of 0.4551 — 0.1 % under, which is the quantisation cost.
Rate. One such block per 0.5 ms slot is 71.7 Mbit/s on a single layer in 100 PRB.
What the offset cost, exactly. Repeat step 5 with MCS 20 (Q_m = 6, R = 567/1024 = 0.5537), the MCS the raw CQI implied:
N_info = 13 200 × 0.5537 × 6 = 43 854 bits → n = 10, N_info′ = 1024 × 43 = 44 032 → C = ⌈44 056/8424⌉ = 6 → TBS = 48 × 918 − 24 = 44 040 bits.
So 1.5 dB of outer-loop correction moved the transport block from 44 040 to 35 856 bits — an 18.6 % reduction in instantaneous rate. That is what the outer loop costs when it is wrong, and what it saves when it is right: at MCS 20 against a channel that only supports MCS 18, the first-transmission error rate would be roughly 87 %, so the delivered rate would be 44 040 × 0.13 ≈ 5700 bits per slot against 35 856 × 0.9 ≈ 32 270. A factor of 5.7 in favour of the correction.
| MCS | Q_m | R × 1024 | Efficiency (bits/RE) | TBS in this geometry | Code blocks |
|---|---|---|---|---|---|
| 13 | 4 | 490 | 1.9141 | 25 104 bits | 3 |
| 15 | 4 | 616 | 2.4063 | 31 752 bits | 4 |
| 16 | 4 | 658 | 2.5703 | 33 816 bits | 5 |
| 17 | 6 | 438 | 2.5664 | 33 552 bits | 4 |
| 18 | 6 | 466 | 2.7305 | 35 856 bits | 5 |
| 20 | 6 | 567 | 3.3223 | 44 040 bits | 6 |
| 24 | 6 | 772 | 4.5234 | 59 976 bits | 8 |
| 28 | 6 | 948 | 5.5547 | 73 704 bits | 9 |
Table 10. Selected MCS table 1 entries TS 38.214 Table 5.1.3.1-1 carried through the TBS procedure for the §7 geometry — 100 PRB, 12 symbols, 1 layer, N_DMRS = 12. Note MCS 16 and 17: 16QAM at R = 658 and 64QAM at R = 438 give almost identical efficiency, which is the seam between modulation orders and the point where a scheduler's choice is essentially arbitrary.
8. The Age of the Estimate: a Latency Budget
The inner loop always operates on a stale channel. Not occasionally — always, structurally, by an amount that can be written down. Here it is, for a common configuration.
reportSlotConfig = slots20 and reportSlotOffset = 4. The two coloured PDSCH slots are the best and worst cases: the estimate is 3.0 ms old at the earliest possible use and 12.5 ms old at the last transmission before the next report lands. At 30 km/h and 3.5 GHz the coherence time is about 4.4 ms.| Stage | Slots at 30 kHz | ms | What sets it |
|---|---|---|---|
| Channel measured on the CSI-RS occasion | 0 (reference instant) | 0 | NZP-CSI-RS-Resource periodicity and offset |
| UE filters and computes CQI, RI, PMI | 3 | 1.5 | The CSI computation delay requirements Z and Z′ TS 38.214 cl. 5.4, plus any internal time-domain filtering the UE applies when timeRestrictionForChannelMeasurements = notConfigured |
| Wait for the configured report slot | 1 | 0.5 | reportSlotConfig and reportSlotOffset |
| PUCCH transmitted, gNB decodes the UCI | 1 | 0.5 | PUCCH format and the gNB's own decode pipeline |
| gNB writes the CSI record; scheduler can use it | 1 | 0.5 | Implementation |
| Earliest PDSCH using this report | 6 | 3.0 | The floor. Nothing in the configuration reduces this much below 3 ms at 30 kHz. |
| …and it stays the freshest report available until the next one lands | up to 25 | up to 12.5 | reportSlotConfig. This is where most of the age comes from. |
Table 11. The latency budget. Note the shape of it: the fixed processing chain contributes 3 ms and the reporting period contributes up to another 9.5 ms. Shortening the period is by far the most effective lever, and it is paid for in PUCCH.
Is 12.5 ms old actually a problem? Worked against the coherence time. At 3.5 GHz, a UE moving at v gives a maximum Doppler shift f_D = v·f_c/c.
3 km/h → v = 0.83 m/s → f_D = 9.7 Hz
30 km/h → v = 8.33 m/s → f_D = 97.2 Hz
120 km/h → v = 33.3 m/s → f_D = 389 Hz
Using the common 50 %-correlation coherence time T_c ≈ 0.423 / f_D:
3 km/h → T_c ≈ 43.6 ms — the 12.5 ms report is still describing the same channel
30 km/h → T_c ≈ 4.4 ms — the best-case 3.0 ms report is already marginal and the worst-case 12.5 ms report describes a channel that has decorrelated nearly three times over
120 km/h → T_c ≈ 1.1 ms — every report is describing a channel that no longer exists
The conclusion is structural, not marginal. Above walking pace, the inner loop is not tracking the fast fading at all. What it is tracking is the slow component — path loss, shadowing, average interference — and what the outer loop is doing is finding the margin that makes a decision based on the slow component survive the fast one.
9. The Downlink Outer Loop: What OLLA Actually Is
Outer-loop link adaptation is a single number per UE. Call it Δ_olla, in decibels. It is added to whatever SINR the inner loop derived from the CQI report, before the MCS lookup. That is the entire mechanism. Everything interesting about it is in the update rule and in what happens to that number when the UE's circumstances change.
The update rule is asymmetric. On an ACK, the offset moves up by a small step. On a NACK, it moves down by a much larger step:
on HARQ-ACK for a first transmission: delta_olla += step_up on HARQ-NACK for a first transmission: delta_olla -= step_down delta_olla = min(clamp_hi, max(clamp_lo, delta_olla)) with step_down >> step_up and step_up / step_down = p / (1 - p) for a target BLER of p
Listing 1. This is the whole of OLLA. It is not in any 3GPP specification. There is no IE for step_up, no counter for it in any performance-measurement standard, and no requirement that a gNB implement it at all — but every scheduler does, because §4 makes it necessary.
9.1 Why the asymmetry, and why it must be in dB
Consider what the loop is trying to hold. It has one binary observation per transport block. It wants the long-run fraction of NACKs to equal the target BLER p. If the offset moves up by u on each ACK and down by d on each NACK, then at equilibrium the expected drift must be zero:
Deriving the step ratio. Let p be the target first-transmission block error rate. In steady state a fraction (1 − p) of transport blocks are ACKed and a fraction p are NACKed, so the expected change in the offset per transport block is
E[Δ] = (1 − p)·step_up − p·step_down
Setting E[Δ] = 0 gives the design equation:
step_up = step_down · p / (1 − p)
For the eMBB target p = 0.1 with a commonly-used step_down of 0.5 dB:
step_up = 0.5 × 0.1 / 0.9 = 0.5 / 9 = 0.0556 dB
So the loop climbs at one ninth of the rate it falls, and the equilibrium it converges to is the offset at which one transport block in ten fails. The target BLER is not a threshold anywhere in the code — it is encoded entirely in the ratio of the two step sizes. Change the ratio and you have changed the target, whether you meant to or not.
9.2 What counts as a NACK for this purpose
The rule above says first transmission, and that qualifier does a lot of work. A scheduler that fed every HARQ outcome into the loop, including retransmissions, would be measuring the residual error rate after combining rather than the first-transmission error rate, and the loop would settle at the wrong place. Most implementations therefore update only on the feedback for a transmission with a toggled NDI. Beyond that there are genuine design choices, and they are visible in logs:
| Design choice | Options seen in practice | What it changes |
|---|---|---|
| Which HARQ outcomes update the loop | First transmissions only / every transmission / first transmissions plus a weighted contribution from retransmissions | Where the equilibrium sits. Including retransmissions pushes the first-transmission BLER above the nominal target. |
| What to do with DTX (no HARQ-ACK received at all) | Treat as NACK / treat as nothing / treat as NACK but with a reduced step | Whether a PDCCH coverage problem gets converted into a link-adaptation problem. See §11.4. |
| Code-block-group feedback | Any CBG NACK counts as a NACK / a fractional update proportional to the NACKed CBGs | The effective step size, and therefore the target. With codeBlockGroupTransmission configured the two options differ by several dB of equilibrium offset. |
| Scope of the offset | Per UE / per UE per beam / per UE per carrier / per UE per BWP | How much state has to be re-learned after a beam switch or a carrier activation. See §11.5. |
| Clamp range | ±3 dB / ±6 dB / −8…+4 dB / unbounded | How far a pathological excursion can go, and how long recovery takes |
Table 12. The five decisions a vendor makes when implementing OLLA. None of them is specified, all of them are observable from a log, and differences between two vendors' choices here account for most "the other vendor's scheduler is more aggressive" observations.
Because none of this is specified, the outer loop's behaviour is the clearest fingerprint of a particular scheduler implementation you will find. If you have logs from two vendors' gNBs serving similar traffic in similar radio conditions, plot MCS against reported CQI for both. The slope is the CQI-to-SINR mapping; the offset is the outer loop's equilibrium; the scatter is the step size. You can read all three off the plot without any documentation.
10. The Step Ratio and the Convergence Time
The step ratio sets where the loop settles. The absolute step sizes set how long it takes to get there, and the answer is startlingly asymmetric.
The model behind the figure, stated so the numbers can be checked. MCS index m is taken to have a 50 % first-transmission error point at SINR_50(m) = 3·SE(m) + 3 dB, where SE(m) is the exact spectral efficiency from MCS table 1 — that is, 3 dB of SINR per bit/RE plus a 3 dB implementation gap. The first-transmission error rate at SINR γ is taken as a logistic in the margin:
BLER(γ, m) = 1 / (1 + exp((γ − SINR_50(m)) / 0.5))
which gives 50 % at the 50 % point, 10 % at +1.10 dB of margin (0.5·ln 9 = 1.099) and 1 % at +2.30 dB. Those two numbers — 1.1 dB of margin for 10 % and a further 1.2 dB for 1 % — are the only properties of the model that the rest of this document leans on, and they are representative of a real LDPC transport block of a few tens of kilobits. The scheduler picks the MCS whose predicted BLER is closest to target.
Convergence time, both directions. Take a UE whose offset is 6 dB away from equilibrium, step_down = 0.5 dB, step_up = 0.0556 dB, scheduled in every slot at 30 kHz SCS (2000 TTIs/s).
From an optimistic start (offset 6 dB too high). The MCS is far above what the channel supports, so BLER is near 1 and almost every transport block NACKs. Each NACK moves the offset 0.5 dB:
6.0 / 0.5 = 12 NACKs, so about 12–14 TTIs = 6–7 ms.
From a pessimistic start (offset 6 dB too low). The MCS is far below what the channel supports, so BLER is ≈ 0.5 % and almost every block ACKs. Net drift per TTI = 0.995 × 0.0556 − 0.005 × 0.5 = 0.0528 dB:
6.0 / 0.0528 = 114 TTIs = 57 ms.
A factor of nine, exactly as designed — and that is the best case, with the UE scheduled in every slot. Now make the UE realistic. A UE with a 40 ms traffic period getting one transport block per burst produces 25 outer-loop updates per second:
114 updates / 25 per second = 4.6 seconds.
And a UE on a background keep-alive, two transport blocks per second:
114 / 2 = 57 seconds.
Widen the excursion to a −8 dB clamp and take the clamp to equilibrium at 2 blocks per second and the recovery is over two minutes.
| Starting error | Direction | Updates needed | At 2000 TTI/s (saturated) | At 25 TTI/s (40 ms bursts) | At 2 TTI/s (keep-alive) |
|---|---|---|---|---|---|
| 2 dB | too optimistic | 4 NACKs | 2 ms | 0.16 s | 2 s |
| 6 dB | too optimistic | 12 NACKs | 6 ms | 0.5 s | 6 s |
| 6 dB | too pessimistic | 114 ACKs | 57 ms | 4.6 s | 57 s |
| 11 dB (clamp to clamp) | too pessimistic | 208 ACKs | 104 ms | 8.3 s | 104 s |
Table 13. Recovery time against traffic pattern. The right-hand column is the operationally important one, and it is why a stuck offset on a lightly-loaded UE is measured in minutes. The loop is not broken; it is simply an integrator being fed at 2 Hz.
The asymmetry is not a flaw — it is the correct engineering trade. Being too optimistic costs decoding failures, which cost latency and spectrum immediately, so the loop must escape that state fast. Being too pessimistic costs unused capacity, which is invisible and free in the short term. A loop that recovered from pessimism as fast as it escapes optimism would also over-react to every single unlucky NACK. The price of the asymmetry is that pathological excursions — §11 — heal slowly.
One more consequence of the arithmetic, and it catches people out. For a URLLC target of p = 10⁻⁵ the same design equation gives step_up = 0.5 × 10⁻⁵ / (1 − 10⁻⁵) = 5 × 10⁻⁶ dB. Recovering one decibel would take 200 000 successful transport blocks. An ACK-counting outer loop is arithmetically useless at URLLC targets, so implementations use something else: a windowed BLER estimator over thousands of blocks with a proportional correction, or a time-decay term that pulls the offset back towards zero in the absence of NACKs, or simply a fixed conservative offset plus the low-SE table. If you are debugging link adaptation on a URLLC bearer, do not assume the mechanism in §9 is running — find out what is.
11. OLLA Pathologies: Where the Field Problems Live
Everything so far has been the loop working. This section is the loop misbehaving, and it is the reason this document exists. The outer loop has no specification, no standard counter, no mandatory reset behaviour and one hidden state variable per UE. It is exactly the kind of component that fails silently.
11.1 Windup: the offset that stops being updated
The offset is only updated when there is HARQ feedback, and there is only HARQ feedback when there is data. A UE that stops receiving downlink data — the user closed the app, the TCP connection went idle, the video buffer filled — freezes its offset at whatever value the last few transport blocks happened to leave it at. If those transport blocks were a burst of NACKs during a fade, the frozen value is several decibels negative, and it will still be several decibels negative when the next data arrives, however much later that is and however good the channel has become in the meantime.
Symptom in a log: the first few transport blocks of every downlink burst on a bursty UE are scheduled at a noticeably lower MCS than the steady state the burst converges to, and the size of the gap correlates with how the previous burst ended rather than with anything about the current one. On short bursts the loop never converges at all, so the UE spends its entire life at whatever offset its worst recent moment produced.
11.2 Poisoning: NACKs that are not about SINR
The outer loop's one input is did the transport block decode, and it interprets every failure as "the SINR was lower than I thought". Most of the time that is right. When it is wrong, the loop does the wrong thing with total confidence, and keeps doing it.
| What actually caused the NACK | Why the outer loop cannot tell | What it does instead | Log signature |
|---|---|---|---|
A DMRS configuration mismatch — wrong dmrs-AdditionalPosition, wrong number of CDM groups without data, wrong dmrs-Type | The channel estimate at the UE is wrong, so demodulation fails regardless of SINR | Drives the offset to the clamp and holds it there for as long as the misconfiguration lasts | BLER near 100 % at every MCS including MCS 0, reported CQI healthy, offset pinned at the clamp. See the companion 33 DMRS. |
| A short interference burst on PDSCH resources only — the case in Figure 9 | CSI-RS and CSI-IM occupy different resource elements and may be in different slots | Winds down during the burst, then climbs back at 0.056 dB per ACK for several hundred TTIs | An offset excursion that begins and ends long after the interference counter spike |
| A missed PDCCH — the UE never saw the grant | There is no HARQ-ACK, and DTX is commonly counted as a NACK | Attributes a control channel coverage problem to the data channel, and lowers the data rate, which does nothing about it | NACK rate correlating with PDCCH aggregation level or CORESET load rather than with CQI. See the companions 30 CORESET and Search Space and 31 PDCCH Blind Decoding. |
| Pre-emption — a URLLC transmission overwrote part of this PDSCH | The pre-empted resource elements are simply noise to the decoder | Lowers the offset for a UE whose channel is fine | Offset excursions on eMBB UEs correlating with URLLC arrivals, and with the downlink pre-emption indication if it is configured |
Table 14. Four ways to poison the outer loop. Each has the same shape: something other than SINR caused a decode failure, the loop corrected the SINR estimate, and the correction achieved nothing except a lower data rate that then had to be un-learned.
The general form of this failure is worth naming, because you will meet it repeatedly. The outer loop converts any persistent non-radio fault into an apparent coverage problem. A DMRS misconfiguration, a codebook mismatch and a PDCCH coverage hole all present as a UE that is being scheduled well below its reported CQI — which is exactly what a genuine reporting bias also presents as. The discriminator is BLER: if the first-transmission BLER is at target, the loop is working and the CQI is genuinely optimistic. If BLER is far above target at every MCS the loop tries, the loop is not the problem and lowering the MCS further will not help.
11.3 Reset at handover: carry it or discard it
At handover the UE arrives at a new cell with a new gNB, and possibly a different vendor's scheduler, a different CSI-RS configuration and a different interference environment. What happens to the offset? There is no specification, so both answers exist in the field, and both are defensible.
| Policy | What it costs | What it buys | Where it shows up |
|---|---|---|---|
| Discard — start the new cell at 0 dB, or at a per-cell default | The UE re-learns from scratch. If the true bias is −3 dB, the first 6–12 transport blocks in the target cell fail, and if the bias is positive the UE runs pessimistically for 50–100 TTIs. | No stale state. A pathological offset cannot follow the UE across the network. | A reproducible BLER spike in the first few TTIs after every handover completion, and a matching MCS dip |
| Carry — transfer the offset in the handover preparation, or keep it in the UE context | A bias that was specific to the source cell's interference or beam geometry arrives in a cell where it is wrong, and a poisoned offset propagates. | Instant convergence when the bias really is a property of the UE — which, for a chipset-specific reporting bias, it is. | A UE that is inexplicably conservative in a cell where every other UE is fine, traceable back to the cell it came from |
Table 15. The reset question at handover. There is no correct answer, but there is a wrong one for any given deployment — and you can tell which policy your gNB implements from the first twenty TTIs after a handover. See the companion 22 Handover Overview.
11.4 Staleness after DRX sleep, and after any silence
A UE in a long DRX cycle may not be scheduled for hundreds of milliseconds. The offset does not decay — there is nothing to decay it — so it is exactly as it was when the UE went to sleep. Meanwhile the UE may have moved tens of metres, the beam may have changed, and the interference environment certainly has. The first transport block after drx-onDurationTimer starts is scheduled using a channel estimate that is at best one report old and an offset that is one DRX cycle old.
Symptom in a log: a BLER spike concentrated in the first one or two TTIs of every DRX Active Time, absent for the rest of it. Correlate against drx-LongCycleStartOffset and it is unmistakable. Some schedulers respond by scheduling the first block after a DRX wake-up at a deliberately conservative MCS — a probe — and using its outcome to re-seed the loop. If you see the first block of every Active Time at a fixed low MCS regardless of CQI, that is what you are looking at, and it is a feature. The companion 11 DRX document owns the timers.
11.5 Scope: per UE, per beam, or per carrier
The offset corrects a bias, and the bias is not a single scalar property of the UE. Part of it is the UE's receiver — genuinely per UE. Part of it is the interference and the multipath geometry — per beam. Part of it is the frequency-dependent behaviour of the UE's front end — per carrier. Whichever scope an implementation chooses, something breaks:
| Scope | What happens on a beam switch | What happens on SCell activation | Verdict |
|---|---|---|---|
| One offset per UE | The offset learned on beam 3 is applied to beam 7. If beam 7 has a strong interferer that beam 3 did not, the loop has to re-learn several dB, and it does so through failed transport blocks. | The PCell's offset is applied to a carrier 400 MHz away with different propagation and a different neighbour set. | Cheapest, and adequate for a static UE in a lightly loaded cell. Visibly wrong under mobility. |
| One offset per UE per beam / per TCI state | Instant — the right offset is already there. | Still wrong for the new carrier. | The right answer for FR2 and for FR1 with many beams. Costs state and means each beam converges independently, so rarely-used beams stay un-converged. |
| One offset per UE per carrier / per serving cell | Wrong, unless combined with per-beam scope. | Each carrier learns its own bias. | Necessary with carrier aggregation. See the companion 29 Carrier Aggregation. |
Table 16. Offset scoping. The failure mode is always the same — an offset learned in one context is applied in another — and the fix is always the same trade: more state, slower convergence per context.
11.6 Clamping, and why the range matters more than it looks
Every implementation clamps the offset. Without a clamp, a UE with a genuine non-radio fault would drive the offset to negative infinity and never come back. With a clamp, the excursion is bounded — but the clamp range is also the worst-case recovery time, because a loop pinned at the clamp has to climb all the way back at step_up per ACK.
| Clamp range | Worst-case excursion | Worst-case recovery, saturated UE | Worst-case recovery, 2 TB/s UE | Risk |
|---|---|---|---|---|
| ±3 dB | 3 dB below equilibrium | 54 ACKs = 27 ms | 27 s | A UE with a genuine 5 dB bias never converges; BLER stays above target permanently |
| ±6 dB | 6 dB | 108 ACKs = 54 ms | 54 s | A reasonable compromise, and the most common choice |
| −8 … +4 dB | 8 dB | 144 ACKs = 72 ms | 72 s | Covers almost any real bias, at the price of a two-minute recovery from a poisoning event on an idle-ish UE |
| Unbounded | Unbounded | Unbounded | Unbounded | One non-radio fault suppresses the UE indefinitely. Do not. |
Table 17. Clamp range against recovery time. The clamp is not a safety net you can make arbitrarily generous — its width is the duration of your worst pathology.
12. The Uplink Inner Loop: SRS and PUSCH DMRS
The uplink inner loop has a structural advantage the downlink cannot match: the gNB measures the channel it is going to use, at full precision, with no reporting delay. It has two sources for that measurement, and they are complementary rather than redundant.
| SRS-based sounding | PUSCH DMRS-based estimation | |
|---|---|---|
| What it is | A dedicated uplink reference signal the UE transmits on a schedule the gNB configures, covering a bandwidth the gNB chooses | The demodulation reference signal embedded in every PUSCH the UE transmits, measured as a by-product of demodulating it |
| Frequency coverage | Whatever freqDomainPosition, freqDomainShift, c-SRS and b-SRS configure — commonly the whole BWP, optionally hopped across it | Only the PRBs the UE was actually granted |
| Time coverage | Periodic at periodicityAndOffset-p, from every slot (sl1) to every 2560 slots; or aperiodic on a DCI trigger | Only when the UE transmits. A UE with no uplink data provides nothing. |
| What it can measure that the other cannot | The channel on PRBs the UE is not currently using, which is what the scheduler needs before it can widen the allocation. Also the per-antenna channel for codebook selection and, with usage = antennaSwitching, the downlink channel by reciprocity. | The actual received SINR including the actual interference in the actual resource, at the actual transmit power the UE used |
Table 18. The two uplink measurement sources. The practical arrangement in most schedulers is DMRS for the SINR that drives the MCS and SRS for everything that requires knowing about resources the UE is not using: bandwidth decisions, precoder selection and rank.
SRS-Config sits in BWP-UplinkDedicated and contains resource sets, each with a usage that declares what the gNB intends to do with the measurements. The usage value is not cosmetic — it determines which resources the UE must transmit on and how the gNB is permitted to interpret them.
| Field | Values | Typical | Effect on link adaptation |
|---|---|---|---|
usage | beamManagement / codebook / nonCodebook / antennaSwitching | codebook | codebook gives the gNB the per-port channel it needs to pick a precoder and a rank for PUSCH. antennaSwitching is the reciprocity path that lets the gNB estimate the downlink channel from uplink sounding — in a TDD cell that is a second, independent input to downlink link adaptation. |
periodicityAndOffset-p | sl1, sl2, sl4, sl5, sl8, sl10, sl16, sl20, sl32, sl40, sl64, sl80, sl160, sl320, sl640, sl1280, sl2560 | sl20 (10 ms at 30 kHz) | The age of the SRS-derived channel estimate. sl2560 is 1.28 s at 30 kHz — useful only for a stationary UE |
resourceMapping | nrofSymbols 1/2/4, repetitionFactor 1/2/4, startPosition 0…5 | 1 symbol, no repetition | Repetition raises the SRS SNR for a power-limited UE — without it, a cell-edge UE's SRS may be too weak to measure, and the scheduler ends up relying on DMRS alone |
Table 19. SRS-Config fields that matter to uplink link adaptation TS 38.331. The repetitionFactor and SRS power-control rows are the ones that bite at the cell edge, where the SRS is often the weakest thing the gNB has to work with.
Uplink link adaptation ought to be easy — the measurement is fresh, unquantised and made by the receiver that will use it. It is nevertheless the harder of the two, for one reason: the gNB is adapting to a transmitter whose power it is also controlling. In the downlink, the transmit power is essentially constant and the SINR the UE reports is a property of the channel. In the uplink, the SINR the gNB measures is a property of the channel and of the power-control loop's current state and of the transport format the scheduler just chose (§15). Measuring it accurately is not the problem; knowing what it will be next time is.
13. Uplink MCS Selection and the Bandwidth Trade
In the downlink the scheduler chooses an allocation and then a rate for it. In the uplink it chooses both at once, and they interact through power. A UE has one total transmit power budget, P_CMAX, typically 23 dBm for a power class 3 device. Spreading a transmission over twice as many PRBs at the same power spectral density needs 3 dB more total power; if the UE has not got 3 dB more power, spreading it halves the power spectral density and therefore the received SINR per resource element.
The PUSCH power formula makes this explicit TS 38.213 cl. 7.1.1. For PUSCH transmission occasion i:
P_PUSCH(i) = min { P_CMAX ,
P_O_PUSCH(j) + 10*log10( 2^mu * M_RB(i) )
+ alpha(j) * PL(q_d)
+ Delta_TF(i)
+ f(i,l) } [dBm]
P_O_PUSCH(j) nominal + UE-specific component, from
p0-NominalWithGrant and P0-PUSCH-AlphaSet -> p0
M_RB(i) allocated PRBs -- the bandwidth term
alpha(j) fractional pathloss compensation factor,
P0-PUSCH-AlphaSet -> alpha, one of
alpha0 alpha04 alpha05 alpha06 alpha07 alpha08
alpha09 alpha1
PL(q_d) downlink pathloss estimate on pathlossReferenceRS
Delta_TF(i) transport-format term -- see the calculation below;
zero if PUSCH-PowerControl -> deltaMCS is absent
f(i,l) the closed-loop accumulator driven by TPC commandsListing 2. The two terms that couple power control to link adaptation are M_RB — the scheduler's bandwidth decision — and Delta_TF — the scheduler's MCS decision. Neither of them is under the power loop's control, and both of them change the power the UE will use.
The bandwidth-versus-MCS trade, worked for a power-limited UE. 30 kHz SCS, 14-symbol PUSCH, 1 layer, one front-loaded DMRS symbol with one CDM group without data (N_DMRS = 6 RE/PRB), N_oh = 0. N_RE′ = 14 × 12 − 6 = 162, capped at 156, so N_RE = 156 × M_RB.
Option A — 50 PRB at MCS 16 (Q_m = 4, R = 658/1024 = 0.6426):
N_RE = 7800; N_info = 7800 × 0.6426 × 4 = 20 048 bits
n = ⌊log₂(20 024)⌋ − 5 = 9, N_info′ = 512 × 39 = 19 968
C = ⌈19 992/8424⌉ = 3, TBS = 24 × 833 − 24 = 19 968 bits
Option B — 100 PRB at MCS 8 (Q_m = 2, R = 602/1024 = 0.5879):
N_RE = 15 600; N_info = 15 600 × 0.5879 × 2 = 18 342 bits
n = ⌊log₂(18 318)⌋ − 5 = 9, N_info′ = 512 × 36 = 18 432
C = ⌈18 456/8424⌉ = 3, TBS = 24 × 769 − 24 = 18 432 bits
Comparable payloads — B is 92 % of A. Now the power. The bandwidth term 10·log₁₀(2¹ · M_RB):
A: 10·log₁₀(100) = 20.0 dB
B: 10·log₁₀(200) = 23.0 dB
And the transport-format term, Δ_TF = 10·log₁₀((2^(BPRE·K_s) − 1)·β_offset) with K_s = 1.25 when deltaMCS is enabled and β_offset = 1 for data-only PUSCH, where BPRE = (TBS + 24)/N_RE:
A: BPRE = 19 992/7800 = 2.563 → 2^3.204 − 1 = 8.21 → 9.14 dB
B: BPRE = 18 456/15 600 = 1.183 → 2^1.479 − 1 = 1.788 → 2.53 dB
Sum of the two scheduler-controlled terms:
A: 20.0 + 9.14 = 29.14 dB
B: 23.0 + 2.53 = 25.53 dB
Option B needs 3.6 dB less power for 92 % of the payload. The narrow-and-fast option is not the power-efficient one — it costs more power because Δ_TF grows faster with spectral efficiency than the bandwidth term grows with PRBs. What the wide option costs is spectrum: twice as many PRBs, which the scheduler cannot give to anyone else.
That result is worth internalising because it runs against intuition. The reflex is to concentrate a power-limited UE's transmission into a few PRBs to raise the power spectral density. For a UE that is actually at P_CMAX that reflex is correct — the min() in the formula has already clipped the power, so narrowing the allocation genuinely does raise the PSD. But for a UE with headroom, the calculation above is the relevant one, and the wide-and-slow allocation is cheaper in power and only slightly worse in rate. The scheduler's real decision is therefore a cell decision: spend PRBs to save a UE's power, or spend a UE's power to save PRBs.
| Situation | How the scheduler should read it | What it should do with MCS and PRBs |
|---|---|---|
| PHR reports large positive headroom | The UE is nowhere near P_CMAX; the min() is not binding | Free choice. Optimise for cell spectral efficiency: fewer PRBs, higher MCS, more UEs multiplexed. |
| PHR reports negative headroom | The UE has already been asked for more than it can give and is transmitting below the commanded power | The grant is invalid in power terms. Narrow it. Every MCS decision made before narrowing it was made against a false premise. |
| MCS pinned at 0 with BLER still above target | There is nowhere further to go in the MCS domain | The remaining levers are bandwidth reduction, PUSCH repetition (pusch-AggregationFactor or repetition type B), and transform precoding for the PAPR gain |
Table 20. Reading the power headroom report as a link-adaptation input. The companion 10 BSR, PHR and SR document owns the PHR formats and the reporting triggers; what matters here is that a PHR is the only direct evidence the scheduler gets about whether its own power arithmetic is still valid.
msg3-DeltaPreamble and the Msg3 grant are a special case worth knowing about. The very first PUSCH a UE sends in a cell is Msg3, scheduled by the RAR with no CSI, no SRS, no DMRS history and no outer loop — the gNB has one preamble detection and nothing else. Msg3 is therefore always sent at a fixed, deliberately conservative MCS. If you see Msg3 failing while subsequent PUSCH is fine, that is not a link adaptation problem; see the companion 03 Random Access.
14. The Uplink Outer Loop and the Ambiguous CRC
The uplink outer loop is structurally identical to the downlink one: one offset per UE, moved up by a small step and down by a large step, with the ratio setting the target BLER. The difference is in the input. There is no HARQ-ACK in the uplink — the gNB does not send one, because it does not need to; it either schedules a retransmission for that HARQ process or it toggles the NDI and moves on. The companion 05 HARQ document covers how the UE infers the outcome from the next grant.
So the uplink outer loop's ground truth is the gNB's own CRC check on the received PUSCH. That sounds better than a reported bit, and in one sense it is: it is local, immediate and cannot be lost. But it is ambiguous in a way the downlink HARQ-ACK is not.
| What the gNB observes | What it could mean | What the outer loop does about it | Is that the right response? |
|---|---|---|---|
| Energy detected on the expected PUSCH resource, CRC fails | A genuine decode failure — the SINR was below what the MCS needed | Lowers the offset by step_down | Yes. This is the case the loop exists for. |
| Nothing detected on the expected PUSCH resource | The UE never received the grant, because it missed the PDCCH | Lowers the offset by step_down, if the implementation treats no-energy as a failure | No. The data channel's rate has nothing to do with the control channel's coverage. Fix the CORESET, not the MCS. |
| Nothing detected on the expected PUSCH resource, shared spectrum | The UE received the grant but its listen-before-talk check failed, so it did not transmit at all | Lowers the offset by step_down | No. LBT failure is a channel-occupancy problem. Lowering the MCS makes the transmission longer, which makes the next LBT no easier and the collision window wider. |
| CRC fails on every PUSCH from this UE at every MCS | A configuration fault — wrong DMRS, wrong transform precoding setting, wrong sequenceId, or an uplink timing error | Drives the offset to the clamp and holds it | No. And it will also mask the real fault, because the symptom becomes "low MCS" rather than "broken uplink". |
| Partial: some code blocks decode, the transport block does not | Frequency-selective interference across the allocation | Lowers the offset, which lowers the MCS across the whole allocation | Partly. The better response is a narrower allocation placed away from the interference, which is a scheduler decision the outer loop cannot reach. |
Table 21. Five things a failed PUSCH CRC can mean, and what a naive outer loop does with each. Only the first row is a link-adaptation event. The loop corrects for all five identically because it cannot distinguish them.
This is the uplink's signature trap. A missed grant, an LBT failure and a decode failure are the same observation to the outer loop: expected data did not arrive correctly. All three drive the MCS down. In the LBT case that is actively counterproductive — a lower MCS means a longer transmission, which occupies the channel for longer and does nothing whatsoever about the contention that caused the failure. On NR-U, check lbt-FailureRecoveryConfig and the consistent-LBT-failure counters before concluding anything from an uplink MCS distribution. A well-implemented ULLA excludes both cases: it requires detected energy on the resource before counting a CRC failure, and it excludes occasions the UE reported as LBT-failed.
15. Link Adaptation Against Power Control
Uplink power control is a closed loop. Uplink link adaptation is a closed loop. They respond to the same channel, they act on the same transmission, and they run at similar rates. Two feedback loops with overlapping authority over one actuator is a recipe for trouble, and the reason it usually works anyway is a clean separation of concerns that is worth stating precisely.
| Uplink power control | Uplink link adaptation | |
|---|---|---|
| Specified in | TS 38.213 cl. 7.1 (PUSCH), 7.2 (PUCCH), 7.3 (SRS) | Nowhere, for the outer loop; TS 38.214 cl. 6.1.4 for the MCS tables |
| Controlled variable | The received power spectral density at the gNB | The first-transmission block error rate |
| Target | P_O_PUSCH, offset by α·PL — i.e. a received SINR operating point set by planning | A BLER, typically 10 %, at whatever SINR actually arrives |
| Actuator | The f(i,l) accumulator, driven by 2-bit TPC commands in DCI 0_0/0_1 or a TPC MAC CE | The MCS field of the grant |
| Feedback signal | The gNB's measured received power against its target | The PUSCH CRC result |
| Saturates at | P_CMAX — after which the loop is open and its commands do nothing | MCS 0 at the bottom, MCS 28 at the top |
| Time constant | A few TPC commands — 1 to 3 dB per command, so a few TTIs | Tens to hundreds of TTIs |
Table 22. The separation of concerns. Power control decides how loud; link adaptation decides how fast. Because they control different variables and the power loop is roughly an order of magnitude faster, they normally compose rather than fight.
| TPC field value | Accumulated δ_PUSCH | Absolute δ_PUSCH |
|---|---|---|
| 0 | −1 dB | −4 dB |
| 1 | 0 dB | −1 dB |
| 2 | +1 dB | +1 dB |
| 3 | +3 dB | +4 dB |
Table 23. The 2-bit TPC command for scheduled PUSCH TS 38.213 Table 7.1.1-1. Accumulated mode is the default; absolute mode is selected by tpc-Accumulation being absent from PUSCH-PowerControl — note the inversion, the field's presence disables accumulation. In accumulated mode f(i,l) is a running sum, which means it can wind up exactly as the OLLA offset can.
15.1 The coupling through Δ_TF
The two loops are not fully independent, and the coupling is the Δ_TF term in §13. When deltaMCS is configured, the UE's transmit power depends on the spectral efficiency of the transport format the scheduler chose. Raise the MCS and the UE transmits harder; lower the MCS and it transmits more softly. The intent is sound — power should track the SINR the chosen rate actually requires, so that a UE sending QPSK does not waste power it does not need — but the consequence is that an outer-loop correction changes the very SINR against which the next correction will be measured.
15.2 What breaks when the power loop saturates
Here is the failure the section title promised. A UE at the cell edge reaches P_CMAX. The gNB's power loop measures a received PSD below target and issues +3 dB TPC commands. In accumulated mode, f(i,l) grows by 3 dB per command. The UE cannot comply — the min() in the power formula clips at P_CMAX — so the received PSD does not move, so the gNB issues more +3 dB commands, and the accumulator winds up: 6 dB, 12 dB, 30 dB of commanded power that has no effect whatsoever.
Meanwhile the link adaptation loop sees CRC failures, because the received SINR really is too low for the MCS. It lowers the MCS. That helps — genuinely — and the loop converges to a rate that works. So far so good. Two things then go wrong:
- The accumulator discharges into a smaller allocation. The UE moves, or its buffer shrinks, and the scheduler gives it 10 PRBs instead of 100. The bandwidth term drops by 10 dB, so P_CMAX is no longer binding and the wound-up
f(i,l)suddenly applies. The UE transmits 10 or 20 dB louder than intended, into a cell where the scheduler has planned its interference budget on the assumption that it would not. Neighbour cells see an interference spike from a UE that was, a moment ago, at the cell edge and inaudible. - Link adaptation keeps correcting a variable it does not control. While the power loop is saturated, the received SINR is fixed by P_CMAX, the path loss and the allocation width — nothing the MCS decision does changes it. The outer loop is then a perfectly good controller with its actuator connected to the wrong variable: it can reduce the rate to fit the SINR, which is correct, but it will keep reducing it in response to failures caused by anything else, and it will never recover upwards because nothing upward ever succeeds.
The fix for the first problem is anti-windup on the power loop: stop accumulating f(i,l) once the PHR indicates zero or negative headroom. Every competent implementation does this, and it is the direct analogue of clamping the OLLA offset. The fix for the second is to make the link adaptation loop aware of the power loop's saturation, so that a power-limited UE's failures are attributed to power rather than to rate. The observable in both cases is the same: a PHR reporting near-zero or negative headroom. A PHR with negative headroom invalidates every assumption both loops are making, and in a log it should be read as loudly as an RLF.
16. Choosing a Target BLER
The target BLER is the one parameter of the outer loop that has an engineering justification rather than a tuning history, and the justification is worth working through because it is the same argument that decides between eMBB and URLLC configurations.
A higher target means a higher MCS and more bits per transmission, but more failures, each costing a HARQ round trip and a retransmission that occupies resources without delivering new data. A lower target means fewer failures but a lower rate on every transmission, including the vast majority that would have succeeded anyway. There is an optimum, and it depends on how much margin a decade of BLER reduction costs.
Effective throughput at two targets, worked. Same geometry as §7: 30 kHz SCS, 100 PRB, 12 symbols, 1 layer, 13 200 RE per slot.
At a 10 % target the loop settles on MCS 18 — spectral efficiency 2.7305, TBS 35 856 bits. Nine transport blocks in ten succeed first time:
delivered = 35 856 × 0.90 = 32 270 bits/slot = 64.5 Mbit/s
At a 1 % target the loop needs about 2 dB more margin on a fading channel (commonly 1.5–2.5 dB; 2 dB assumed here). At 3 dB per bit/RE that is 0.67 bits/RE less, so 2.73 − 0.67 = 2.06 bits/RE, and the nearest MCS at or below that is MCS 13 — 16QAM, R = 490/1024, SE 1.9141, TBS 25 104 bits. Ninety-nine in a hundred succeed:
delivered = 25 104 × 0.99 = 24 853 bits/slot = 49.7 Mbit/s
The 1 % target costs 23 % of the throughput. And the resource cost of the retransmissions it avoids is small: at a 10 % target, one slot in ten carries a retransmission instead of new data, so the resource overhead of running at 10 % is about 10 % — already accounted for in the 0.90 factor — while the rate penalty of avoiding it is 30 %.
The latency side. At 30 kHz with k1 = 4 and a retransmission scheduled 4 slots after the feedback, a HARQ round trip is about 8 slots = 4 ms. At a 10 % target, the mean added delay is 0.1 × 4 = 0.4 ms and the 99th percentile is about 4 ms (a single retransmission). For a 5 ms eMBB budget that is fine. For a 1 ms URLLC budget it is fatal — which is the whole argument.
| Service | Target first-tx BLER | How it is achieved | Why that target | Cost |
|---|---|---|---|---|
| eMBB — the default | 10⁻¹ | CQI table 1 or 2, MCS table 1 or 2, step_up/step_down = 1/9 | HARQ recovers cheaply, the retransmission latency is affordable, and the model above peaks here | A 4 ms tail on one transmission in ten |
| URLLC | 10⁻⁵ (residual, after HARQ) with a first-tx target often 10⁻³ to 10⁻⁴ | CQI table 3 and MCS table 3, or an MCS-C-RNTI to select the low-SE table per grant; PDSCH/PUSCH repetition; a fixed conservative offset rather than an ACK-counting loop (§10) | There is no time for a retransmission inside a 1 ms budget, so the first transmission must succeed | Up to two thirds of the rate. This is the price of the reliability target and it is not negotiable. |
Table 24. Target BLER by service. Note that URLLC's famous 10⁻⁵ is a residual target after HARQ and repetition, not a first-transmission target — conflating the two leads to absurdly conservative configurations.
MCS-C-RNTI is the mechanism that makes mixed traffic workable. It is a second C-RNTI assigned alongside the ordinary one, and its only function is that any DCI whose CRC it scrambles selects the low-SE MCS table TS 38.214 cl. 5.1.3.1. A scheduler can therefore switch a single UE between a 10⁻¹ target and a 10⁻⁵ target per grant, with no reconfiguration and no RRC signalling. In a log, two different RNTIs scheduling the same UE in adjacent slots is not an error — it is the reliability switch being thrown. A sensible implementation keeps a separate outer-loop offset per table, because the bias that is correct at a 10 % target is not the bias that is correct at 10⁻⁵.
17. Rank Adaptation: When Fewer Layers Win
Rank is part of the same decision. The reported RI says how many layers the UE believes the channel can support, and the scheduler is free to use fewer — or, in some implementations, more. Rank multiplies the transport block size directly: N_info is proportional to the layer count υ, so rank 2 at the same MCS is twice the TBS. That makes higher rank look unconditionally good, and it is not, for two reasons that both come down to physics.
First, the transmit power is split. Two layers at the same total power means each layer gets 3 dB less than a single layer would have had. Second, the layers interfere with each other. On a well-conditioned channel — rich scattering, well-separated eigenvalues — the receiver separates them almost perfectly and the residual leakage is a few tenths of a decibel. On a correlated channel — line of sight, a narrow angular spread, a UE holding two antennas 8 mm apart — the eigenvalues are far apart, the separation is poor, and the leakage is several decibels.
Rank 1 against rank 2, worked at 12 dB. A single-layer channel measured at 12 dB SINR. On this document's 3 dB-per-bit/RE scale that supports 3.9023 bits/RE at a 10 % target — CQI 12, or MCS 22.
Rank 2 on a well-conditioned channel. Power splits: 12 − 3 = 9 dB per layer. Inter-layer leakage after MMSE separation, assume 0.3 dB: 8.7 dB per layer → 2.4063 bits/RE each (CQI 9) → total 4.813 bits/RE.
Rank 2 wins by 23 %.
Rank 2 on a correlated channel. Power splits to 9 dB; leakage on a channel with a high condition number, assume 2 dB: 7.0 dB per layer → 1.9141 bits/RE each (CQI 8) → total 3.828 bits/RE.
Rank 1 wins by 2 %, on rate alone.
And rate alone understates it. For rank 1 to 4 the downlink carries one codeword across all the layers TS 38.214 cl. 5.1.3.2, so a single transport-block CRC covers both layers and the weaker layer sets the error rate for the whole block. On the correlated channel the two layers are not equal — one is well above 7 dB and the other well below — so the effective SINR the codeword experiences is closer to the weaker layer than the average, and the realised BLER exceeds the target. Rank 1 wins clearly once that is accounted for.
| Situation | Reported RI | What a good scheduler does | Why |
|---|---|---|---|
| Rich scattering, high SINR | 2, 3 or 4 | Use the reported rank | The UE's rank estimate is the whole point of the RI field and it has the channel matrix |
| Line of sight, high SINR, closely spaced UE antennas | Often still 2, because the UE reports the rank that maximises its own estimated throughput without knowing about the codeword structure | Override down to 1 and raise the MCS instead | One codeword over two badly-separated layers fails on the weak one; one codeword on one strong layer does not |
| Rank reported as 2, BLER above target at every MCS the loop tries | 2 | Override down to 1 and let the loop re-converge | This is the classic signature of rank overestimation: the loop cannot fix it because the problem is not the rate, it is the layer count |
| High mobility | Whatever was reported 12 ms ago | Bias towards lower rank | The precoder is stale as well as the CQI, and a stale precoder on two layers leaks far more than a stale precoder on one |
Table 25. Rank decisions. Row four is the one to memorise: persistent high BLER that the outer loop cannot cure is a rank problem far more often than it is a rate problem.
Rank can be bounded by configuration as well as by decision. maxRank in PUSCH-Config caps the uplink rank; ri-Restriction inside CodebookConfig is a bitmap that forbids the UE from reporting particular rank values at all, which is the cleanest way to stop rank overestimation at source; and maxNrofCodeWordsScheduledByDCI in PDSCH-Config decides whether the DCI even has a second MCS field. Setting ri-Restriction to allow only rank 1 on a cell where every UE is line-of-sight is a legitimate and effective configuration, and it removes an entire class of failure.
Rank override is invisible in the obvious places. The reported RI is in the CSI report; the used rank is in the DCI antenna port field, which is not the same field and often not in the same log line. A UE reporting RI 2 and being scheduled on one layer looks, in a throughput counter, exactly like a UE with half the channel capacity it has. When you are chasing a factor-of-two throughput shortfall, compare the reported RI against the antenna port field of the grants — not against the throughput.
18. Parameter and Configuration Reference
The parameters split cleanly into two groups, and the split is the most important thing about this table. Everything in the first table is in TS 38.331, has a defined range, appears in an RRC message and can be checked against a specification. Everything in the second table exists only in a vendor's configuration database.
| IE / field | Where it lives | Range | Typical | Effect |
|---|---|---|---|---|
cqi-Table | CSI-ReportConfig | table1 / table2 / table3 | table2 | Which CQI table the reported index means, and therefore the target BLER the report is made against (§6) |
mcs-Table | PDSCH-Config, PUSCH-Config | qam256 / qam64LowSE (absent = the 64QAM table) | qam256 | Which MCS table the mcs field of the DCI indexes. Must be consistent with the UE's reported capability. |
mcs-TableTransformPrecoder | PUSCH-Config | qam256 / qam64LowSE | absent | The MCS table for transform-precoded (DFT-s-OFDM) PUSCH, which is a separate selection from the CP-OFDM one |
reportSlotConfig | CSI-ReportConfig → periodic | slots4 … slots320 | slots20 | Report period in slots. The dominant term in the CSI age budget (§8). |
reportSlotOffset | CSI-ReportConfig → aperiodic | 0 … 32 | 0 … 4 | Slot offset from the triggering DCI to the report |
ri-Restriction | CodebookConfig → codebookType | BIT STRING, one bit per rank | all allowed | Forbids the UE from reporting specific ranks — the cleanest guard against rank overestimation (§17) |
maxRank | PUSCH-Config | 1 … 4 | 1 or 2 | Caps the uplink layer count |
periodicityAndOffset-p | SRS-Resource → resourceType → periodic | sl1 … sl2560 | sl20 | SRS period, and therefore the age of the uplink channel estimate (§12) |
repetitionFactor | SRS-Resource → resourceMapping | n1 / n2 / n4 | n1 | SRS repetition; raises the sounding SNR for a power-limited UE |
p0, alpha | P0-PUSCH-AlphaSet | p0 −16 … 15 (dB, relative); alpha0 … alpha1 | alpha08 | The received-SINR operating point uplink power control aims at (§15) |
tpc-Accumulation | PUSCH-PowerControl | disabled, or absent | absent (i.e. accumulation enabled) | Whether f(i,l) accumulates. Note the inverted sense of the field. |
deltaMCS | PUSCH-PowerControl | enabled, or absent | varies | Whether transmit power tracks the transport format via Δ_TF (§15.1) |
pusch-AggregationFactor | PUSCH-Config | n1 / n2 / n4 / n8 | n1 | Slot-level repetition — the lever that remains once MCS 0 is reached |
codeBlockGroupTransmission | PDSCH-ServingCellConfig | maxCodeBlockGroupsPerTransportBlock 2/4/6/8 | absent | Changes what a NACK means, and therefore the outer loop's effective step size (§9.2) |
Table 26. The 3GPP-specified parameters that shape link adaptation. Note that not one of them configures the adaptation decision; they configure its inputs, its output alphabet and its constraints.
| Vendor parameter (names vary) | Typical range | Common default | What it does | Symptom if wrong |
|---|---|---|---|---|
| Outer-loop step down | 0.1 … 1.0 dB | 0.5 dB | The offset decrement on a NACK | Too large: MCS oscillation and a wide sawtooth. Too small: slow escape from an optimistic state. |
| Outer-loop step up | step_down · p/(1−p) | 0.056 dB | The offset increment on an ACK. Setting this independently of step_down silently changes the target BLER. | Too small: minutes-long recovery (§10). Too large: BLER above target. |
| Offset clamp, lower | −3 … −10 dB | −6 dB | Bounds the pathological excursion | Too wide: long outages after poisoning. Too tight: a genuinely biased UE never converges. |
| Handover reset policy | discard / carry / carry a fraction | varies by vendor | What happens to the offset at handover (§11.3) | A BLER spike after every handover, or a poisoned offset following the UE across the network |
| DTX handling | count as NACK / ignore / reduced step | count as NACK | Whether a missing HARQ-ACK drives the offset down | PDCCH coverage problems presenting as low MCS (§11.2) |
| CQI-to-SINR mapping table | a per-vendor table or curve | proprietary | Step 2 of the chain in §7 | MCS that does not track CQI with the slope you expect |
| Rank override policy | trust RI / cap by condition number / cap by BLER history | varies | Whether the reported RI is used as-is (§17) | Persistent high BLER that no MCS reduction cures |
Table 27. None of these is specified by 3GPP. They have no IE names, no defined ranges and no standardised counters. The names in the left column are descriptive, not normative — every vendor calls them something different, and some expose only a subset. This table is the answer to "why do these two gNBs behave differently on identical radio?"
19. Failure Modes and What Each One Means
| What fails | Who detects it | What the network does | Log signature and what it points at |
|---|---|---|---|
| OLLA windup — the offset freezes at a bad value because the UE stopped being scheduled | Nobody. There is no counter for it and no alarm. | The next burst is scheduled at the frozen offset and, if the burst is short, never recovers | MCS at the start of each downlink burst correlating with how the previous burst ended rather than with the current CQI. Compare the first and last MCS of consecutive bursts. §11.1. |
| OLLA poisoned by non-SINR NACKs — a DMRS fault, an interference burst on PDSCH only, pre-emption, or a codebook mismatch | Nobody directly. Visible only as the combination of high BLER and a healthy CQI. | Drives the offset to the clamp and holds it; recovery takes 100+ ACKs after the cause has gone | First-transmission BLER far above target at every MCS the loop tries, including MCS 0, while reported CQI stays healthy. That combination rules link adaptation out as the cause. §11.2. |
CQI table mismatch — the UE is configured with table2, the scheduler assumes table1 (or vice versa) | Nobody. Every counter looks healthy. | Runs at roughly 65 % of achievable throughput indefinitely, or drives BLER far above target in the other direction | BLER an order of magnitude below target with the offset pinned at the upper clamp — the loop trying to push up and running out of range. Read cqi-Table out of the RRC reconfiguration and compare it against the scheduler's assumption. §6. |
CSI reports absent — CSI-ReportConfig released, PUCCH resource collision, reportQuantity = cri-RSRP only, or the UE not reporting | The gNB, if it counts CSI report receptions. Many do not alarm on it. | The inner loop falls back to a vendor default, usually a fixed low MCS | MCS constant regardless of radio conditions, BLER near zero, throughput about a third of expected, RSRP excellent. Count decoded CSI reports per second and compare against reportSlotConfig. §4. |
MCS table configured that the UE does not support — mcs-Table = qam256 on a UE without 256QAM PDSCH capability | The UE, by failing to decode; or the RRC layer, if the gNB checks capability before configuring | Everything above the 64QAM range fails; the outer loop drives down until the scheduled MCS happens to fall in the range the UE can actually demodulate | BLER catastrophic above a specific MCS index and fine below it — a hard cliff, not a gradient. Cross-check pdsch-256QAM-FR1 in the UE capability against mcs-Table. See the companion 26 UE Capability. |
| Rank overestimation on a correlated channel — RI 2 reported and used on a line-of-sight channel with poorly separated antennas | Nobody. The RI is not wrong; it is an estimate the UE is entitled to make. | One codeword over two badly separated layers fails on the weaker layer; the outer loop lowers the MCS, which does not help | High BLER that no MCS reduction cures, RI 2 in the CSI report, antenna port field showing 2 layers. Compare reported RI against the DCI antenna port field. §17. |
| Uplink outer loop corrupted by missed grants — DTX on PUSCH counted as a CRC failure | Nobody. Both look like "expected data did not arrive". | Lowers the uplink MCS in response to a downlink control channel problem, achieving nothing | Uplink CRC failures with no received energy and no DMRS correlation peak on the granted resource. If the log carries a received-SINR field alongside the CRC result, use it. §14. |
Power control at P_CMAX while link adaptation keeps adapting — f(i,l) winds up, the outer loop corrects a variable it does not control | The PHR reports zero or negative headroom. That is the only evidence. | The MCS converges correctly to the achievable rate; the accumulator then discharges into a narrower allocation and the UE transmits 10–20 dB too loud | Interference spikes from high-pathloss UEs correlating with a drop in their M_RB. Read PHR as an alarm-level field. §15.2. |
| Target BLER set for eMBB on a URLLC bearer — step ratio 1/9 on a flow that needs 10⁻⁵ | The application, in the form of missed deadlines. No radio counter flags it. | One transport block in ten needs a 4 ms HARQ round trip inside a 1 ms budget | Latency 99th percentile pinned near one HARQ RTT, first-tx BLER sitting neatly at 10 %, cqi-Table = table1 or table2 on a bearer with a 5QI that implies URLLC. Check the table selection against the 5QI. §16. |
Table 28. Nine ways link adaptation fails. Note how many rows say "nobody" in the second column — the outer loop has no standardised counters, so almost everything here is diagnosed from the relationship between quantities rather than from any single one. §24 turns that into a procedure.
There is one discriminator that resolves most of this table, and it is worth writing on the wall: compare first-transmission BLER against the target.
At target — the loop is working. If throughput is still wrong, the problem is upstream: the CQI, the table, the rank, the power.
Far above target at every MCS — the loop is being poisoned. Something other than SINR is destroying transport blocks. Stop looking at link adaptation.
Far below target — the loop is being prevented from pushing up. Look for a clamp, a table mismatch, or an inner loop running on nothing.
20. Configuration Reference (ASN.1)
All extracts are abridged from TS 38.331 — fields not relevant to link adaptation are elided with ..., and version-tagged extension groups are shown only where they matter here.
CSI-ReportConfig ::= SEQUENCE {
reportConfigId CSI-ReportConfigId,
carrier ServCellIndex OPTIONAL,
resourcesForChannelMeasurement CSI-ResourceConfigId,
csi-IM-ResourcesForInterference CSI-ResourceConfigId OPTIONAL,
nzp-CSI-RS-ResourcesForInterference CSI-ResourceConfigId OPTIONAL,
reportConfigType CHOICE {
periodic SEQUENCE {
reportSlotConfig CSI-ReportPeriodicityAndOffset,
pucch-CSI-ResourceList SEQUENCE (SIZE (1..maxNrofBWPs))
OF PUCCH-CSI-Resource
},
semiPersistentOnPUCCH SEQUENCE { ... },
semiPersistentOnPUSCH SEQUENCE {
reportSlotConfig ENUMERATED {sl5, sl10, sl20,
sl40, sl80, sl160,
sl320},
reportSlotOffsetList SEQUENCE (SIZE (1..16))
OF INTEGER(0..32),
p0alpha P0-PUSCH-AlphaSetId
},
aperiodic SEQUENCE {
reportSlotOffsetList SEQUENCE (SIZE (1..16))
OF INTEGER(0..32)
}
},
reportQuantity CHOICE {
none NULL,
cri-RI-PMI-CQI NULL,
cri-RI-i1 NULL,
cri-RI-i1-CQI SEQUENCE {
pdsch-BundleSizeForCSI ENUMERATED {n2, n4} OPTIONAL
},
cri-RI-CQI NULL,
cri-RSRP NULL,
ssb-Index-RSRP NULL,
cri-RI-LI-PMI-CQI NULL
},
reportFreqConfiguration SEQUENCE {
cqi-FormatIndicator ENUMERATED {widebandCQI,
subbandCQI} OPTIONAL,
pmi-FormatIndicator ENUMERATED {widebandPMI,
subbandPMI} OPTIONAL,
csi-ReportingBand CHOICE { ... } OPTIONAL
} OPTIONAL,
timeRestrictionForChannelMeasurements
ENUMERATED {configured, notConfigured},
timeRestrictionForInterferenceMeasurements
ENUMERATED {configured, notConfigured},
codebookConfig CodebookConfig OPTIONAL,
dummy ENUMERATED {n1, n2} OPTIONAL,
groupBasedBeamReporting CHOICE { ... },
cqi-Table ENUMERATED {table1, table2,
table3, spare1} OPTIONAL,
subbandSize ENUMERATED {value1, value2},
non-PMI-PortIndication SEQUENCE (SIZE (1..maxNrofSingle
Panel-PortLayerCombinations))
OF PortIndexFor8Ranks OPTIONAL,
...
}Listing 3. CSI-ReportConfig, abridged. cqi-Table is a single ENUMERATED near the bottom, easy to miss in a decoded reconfiguration, and it silently determines the meaning of every CQI value the UE will ever report (§6). The spare1 member is where table4 for 1024QAM was later grafted in — see §23.
PDSCH-Config ::= SEQUENCE {
dataScramblingIdentityPDSCH INTEGER (0..1023) OPTIONAL,
dmrs-DownlinkForPDSCH-MappingTypeA SetupRelease {DMRS-DownlinkConfig}
OPTIONAL,
...
mcs-Table ENUMERATED {qam256,
qam64LowSE} OPTIONAL,
maxNrofCodeWordsScheduledByDCI ENUMERATED {n1, n2} OPTIONAL,
prb-BundlingType CHOICE { ... },
...
}
PUSCH-Config ::= SEQUENCE {
dataScramblingIdentityPUSCH INTEGER (0..1023) OPTIONAL,
txConfig ENUMERATED {codebook,
nonCodebook} OPTIONAL,
...
pusch-PowerControl PUSCH-PowerControl OPTIONAL,
frequencyHopping ENUMERATED {intraSlot,
interSlot} OPTIONAL,
mcs-Table ENUMERATED {qam256,
qam64LowSE} OPTIONAL,
mcs-TableTransformPrecoder ENUMERATED {qam256,
qam64LowSE} OPTIONAL,
transformPrecoder ENUMERATED {enabled,
disabled} OPTIONAL,
codebookSubset ENUMERATED {fullyAndPartialAndNon
Coherent, partialAndNonCoherent,
nonCoherent} OPTIONAL,
maxRank INTEGER (1..4) OPTIONAL,
pusch-AggregationFactor ENUMERATED {n2, n4, n8} OPTIONAL,
...
}Listing 4. The MCS table selections, downlink and uplink. Note that absence of mcs-Table selects the 64QAM table — the commonest configuration mistake here is assuming the field defaults to 256QAM because the UE supports it. Note also that PUSCH has two independent table selections, one for CP-OFDM and one for transform-precoded waveforms.
SRS-Config ::= SEQUENCE {
srs-ResourceSetToReleaseList SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets))
OF SRS-ResourceSetId OPTIONAL,
srs-ResourceSetToAddModList SEQUENCE (SIZE(1..maxNrofSRS-ResourceSets))
OF SRS-ResourceSet OPTIONAL,
srs-ResourceToAddModList SEQUENCE (SIZE(1..maxNrofSRS-Resources))
OF SRS-Resource OPTIONAL,
tpc-Accumulation ENUMERATED {disabled} OPTIONAL,
...
}
SRS-ResourceSet ::= SEQUENCE {
srs-ResourceSetId SRS-ResourceSetId,
srs-ResourceIdList SEQUENCE (SIZE(1..maxNrofSRS-Resources
PerSet)) OF SRS-ResourceId OPTIONAL,
resourceType CHOICE {
aperiodic SEQUENCE {
aperiodicSRS-ResourceTrigger INTEGER (1..maxNrofSRS-Triggers-1),
csi-RS NZP-CSI-RS-ResourceId OPTIONAL,
slotOffset INTEGER (1..32) OPTIONAL,
...
},
semi-persistent SEQUENCE {
associatedCSI-RS NZP-CSI-RS-ResourceId OPTIONAL,
...
},
periodic SEQUENCE {
associatedCSI-RS NZP-CSI-RS-ResourceId OPTIONAL,
...
}
},
usage ENUMERATED {beamManagement, codebook,
nonCodebook,
antennaSwitching},
alpha Alpha OPTIONAL,
p0 INTEGER (-202..24) OPTIONAL,
pathlossReferenceRS CHOICE { ssb-Index, csi-RS-Index } OPTIONAL,
srs-PowerControlAdjustmentStates
ENUMERATED {sameAsFci2,
separateClosedLoop} OPTIONAL,
...
}
SRS-Resource ::= SEQUENCE {
srs-ResourceId SRS-ResourceId,
nrofSRS-Ports ENUMERATED {port1, ports2, ports4},
transmissionComb CHOICE { n2 SEQUENCE {...},
n4 SEQUENCE {...} },
resourceMapping SEQUENCE {
startPosition INTEGER (0..5),
nrofSymbols ENUMERATED {n1, n2, n4},
repetitionFactor ENUMERATED {n1, n2, n4}
},
freqHopping SEQUENCE {
c-SRS INTEGER (0..63), b-SRS INTEGER (0..3),
b-hop INTEGER (0..3) },
resourceType CHOICE {
aperiodic SEQUENCE { ... },
semi-persistent SEQUENCE {
periodicityAndOffset-sp SRS-PeriodicityAndOffset, ... },
periodic SEQUENCE {
periodicityAndOffset-p SRS-PeriodicityAndOffset, ... }
},
sequenceId INTEGER (0..1023),
spatialRelationInfo SRS-SpatialRelationInfo OPTIONAL,
...
}Listing 5. SRS-Config and its children, abridged. usage and periodicityAndOffset-p are the two fields that decide what the uplink inner loop has to work with; repetitionFactor and the alpha/p0 pair decide whether it can work with it at the cell edge (§12).
PUSCH-PowerControl ::= SEQUENCE {
tpc-Accumulation ENUMERATED {disabled} OPTIONAL,
msg3-Alpha Alpha OPTIONAL,
p0-NominalWithoutGrant INTEGER (-202..24) OPTIONAL,
p0-AlphaSets SEQUENCE (SIZE (1..maxNrofP0-PUSCH-AlphaSets))
OF P0-PUSCH-AlphaSet OPTIONAL,
pathlossReferenceRSToAddModList
SEQUENCE (SIZE (1..maxNrofPUSCH-PathlossRef
erenceRSs)) OF PUSCH-PathlossReferenceRS
OPTIONAL,
twoPUSCH-PC-AdjustmentStates ENUMERATED {twoStates} OPTIONAL,
deltaMCS ENUMERATED {enabled} OPTIONAL,
sri-PUSCH-MappingToAddModList SEQUENCE (SIZE (1..maxNrofSRI-PUSCH-Mappings))
OF SRI-PUSCH-PowerControl OPTIONAL,
...
}
P0-PUSCH-AlphaSet ::= SEQUENCE {
p0-PUSCH-AlphaSetId P0-PUSCH-AlphaSetId,
p0 INTEGER (-16..15) OPTIONAL,
alpha Alpha OPTIONAL
}
Alpha ::= ENUMERATED {alpha0, alpha04, alpha05, alpha06,
alpha07, alpha08, alpha09, alpha1}Listing 6. PUSCH-PowerControl — the other loop. deltaMCS present couples transmit power to the transport format via Δ_TF and therefore couples the two loops (§15.1); tpc-Accumulation present disables accumulation, which is the reverse of what the name suggests and a genuine source of misreading in decoded configurations.
21. Two More Worked Calculations
21.1 Subband CQI overhead against the gain it buys
Given: a 100 MHz BWP at 30 kHz SCS = 273 PRB. subbandSize = value1, which for a BWP of 145–275 PRB is 32 PRB TS 38.214 Table 5.2.1.4-2, so the reporting band holds ⌈273/32⌉ = 9 subbands. cqi-FormatIndicator = subbandCQI.
Payload. Wideband CQI 4 bits, plus a 2-bit differential per subband encoding the offset from the wideband value TS 38.214 Table 5.2.2.1-1:
4 + 9 × 2 = 22 bits for the CQI part alone, against 4 bits for a wideband-only report.
With CRI 1, RI 1 and PMI 5 bits the report goes from 11 bits to 29 bits — past what a PUCCH format 2 with a small resource comfortably carries, so this is a format-3 or semi-persistent-on-PUSCH report.
What it buys. The 2-bit differential covers offsets of −1, 0, +1 and ≥ +2 relative to the wideband value, so the reportable dynamic range across subbands is about three CQI steps — roughly 5 dB in the 64QAM region. If the channel's frequency selectivity across the BWP is smaller than that, subband reporting is telling the scheduler nothing it could not have guessed. If it is much larger than that, the differential saturates and subband reporting understates the good subbands.
Conclusion. Subband CQI is worth its payload when the delay spread puts the coherence bandwidth below the subband size and the selectivity within the ±2-step differential range. At 32 PRB per subband and 30 kHz that is a coherence bandwidth below about 11.5 MHz, i.e. a delay spread above roughly 90 ns — true indoors and in urban macro, marginal in rural line-of-sight. It is not a setting to enable everywhere by default.
21.2 What the outer loop's dither costs
Given: the loop of §9 at equilibrium, target 10 %, step_down 0.5 dB, step_up 0.0556 dB, a UE scheduled every slot at 30 kHz.
The dither. At equilibrium, NACKs arrive on average every 1/0.1 = 10 TTIs. Between NACKs the offset climbs by 9 × 0.0556 = 0.50 dB, then drops by 0.50 dB. So the offset executes a sawtooth of amplitude about 0.5 dB peak-to-peak if the MCS boundaries were continuous.
They are not. Adjacent MCS indices in the 64QAM region of table 1 are 0.16 to 0.31 bits/RE apart, which on the 3 dB-per-bit scale is 0.5 to 0.9 dB. The offset therefore has to move roughly one MCS step's worth before the decision changes at all, so in the intervals between boundary crossings the achieved BLER is not 10 % — it is whatever the current MCS gives, typically 3 % to 15 %.
The cost. Averaged over the sawtooth, the loop spends part of its time one MCS step below the optimum. One MCS step in this region is about 6 % of the TBS (35 856 → 33 552 bits from MCS 18 to 17). If it spends half its time a step low, the mean loss is about 3 %.
Why you cannot tune it away. Halving step_down to 0.25 dB halves the sawtooth, but it also doubles the time to escape an optimistic excursion — from 12 NACKs to 24 for a 6 dB error. The 3 % is the price of a loop that can still react. Tuning it below about 0.2 dB buys a fraction of a percent and costs a doubling of every recovery time in §10.
22. Illustrative Message Traces
Illustrative trace. Field names and encodings follow 3GPP; the values are constructed for this document and are not a capture from any deployed or lab network.
One UE throughout, C-RNTI 0x4C1A, one 100 MHz FR1 TDD cell at 30 kHz SCS, 273 PRB bandwidth part, cqi-Table = table1, mcs-Table absent. Slot numbering is (frame, slot).
22.1 A periodic CSI report, decoded field by field
10:42:17.4820 PHY UCI-decode rnti=0x4C1A ch=PUCCH fmt=2 (412,14)
pucch-Resource ......... id 3, 1 PRB @ PRB 136, 2 symbols (12..13)
uci-Bits ............... 11 crc = PASS
dtx-detected ........... false -- energy present, not a DTX read
csi-ReportConfigId ..... 0
reportQuantity ......... cri-RI-PMI-CQI
decoded:
CRI .................. 0 -- NZP-CSI-RS resource 0 of 2
RI ................... 1 -- rank 1; maxRank = 2
wideband-CQI ......... 11 -- cqi-Table = table1
PMI i1,1 ............. 5 -- Type I single-panel, 4 ports
PMI i1,3 ............. 0
PMI i2 ............... 2
interpreted:
modulation ........... 64QAM, R = 567/1024, 3.3223 bits/RE
equiv-SINR ........... 14.1 dB -- vendor CQI-to-SINR mapping
csi-age-at-decode .... 5 slots = 2.5 ms
next-report-expected . (413,14) -- reportSlotConfig = slots20Listing 7. A healthy periodic report. Two fields deserve attention: dtx-detected distinguishes a decoded report from an absent one, and csi-age-at-decode is the quantity §8 budgets — a gNB that logs it saves you the arithmetic.
22.2 The inner loop, from that report to a transport block
10:42:17.4835 MAC dl-sched rnti=0x4C1A (412,16) csi-record ............. CQI 11, RI 1, PMI(5,0,2), age 4 slots equiv-sinr-from-cqi .... 14.10 dB olla-offset ............ -1.50 dB -- converged; 218 updates so far effective-sinr ......... 12.60 dB mcs-table .............. qam64 (mcs-Table absent) mcs-selected ........... 18 -- Qm 6, R 466/1024 = 0.4551 predicted-bler ......... 0.104 -- against a 0.100 target allocation ............. 100 PRB (PRB 86..185), 12 symbols (2..13) layers ................. 1 -- antennaPorts field = 2 dmrs ................... type1, 1 symbol, 2 CDM groups w/o data n-re-per-prb ........... 132 -- 144 - 12 - 0 n-re ................... 13200 -- min(156,132) x 100 n-info ................. 36042 n-info-quantised ....... 35840 -- n = 10, 1024 x 35 code-blocks ............ 5 -- ceil(35864/8424) tbs .................... 35856 bits = 4482 bytes harq-process ........... 3 ndi=1 (toggled) rv=0 k1 ..................... 4 -- HARQ-ACK expected (412,20)
Listing 8. The same chain as §7, as a scheduler would log it. Every intermediate value is present, which is what makes this kind of trace worth asking for: olla-offset and effective-sinr are the two fields that exist nowhere in 3GPP and explain everything.
22.3 The outer loop moving, over eight TTIs
-- one row per first transmission; retransmissions excluded from the loop
slot CQI equiv olla eff MCS TBS HARQ olla'
SINR offset SINR after
------------------------------------------------------------------
(412,16) 11 14.10 -1.50 12.60 18 35856 ACK -1.444
(412,18) 11 14.10 -1.444 12.66 18 35856 ACK -1.389
(412,20) 11 14.10 -1.389 12.71 18 35856 NACK -1.889
(412,22) 11 14.10 -1.889 12.21 18 35856 ACK -1.833
(413,0) 11 14.10 -1.833 12.27 18 35856 ACK -1.777
(413,2) 11 14.10 -1.777 12.32 18 35856 ACK -1.722
(413,14) 12 15.80 -1.722 14.08 20 44040 ACK -1.666
(413,16) 12 15.80 -1.666 14.13 20 44040 NACK -2.166
-- the CQI changes only at (413,14), when the next report lands: six of
-- these eight decisions used the same 4-bit number.Listing 9. Eight consecutive outer-loop updates. Three things to see: the offset moves 0.056 dB on an ACK and 0.5 dB on a NACK, exactly as §9 specifies; the MCS does not change at all across the first six rows because the offset dither has not crossed an MCS boundary; and the reported CQI is constant for 20 slots, which is the staleness of §8 made concrete.
22.4 An uplink MCS decision from an SRS measurement
10:42:19.0110 PHY srs-meas rnti=0x4C1A (415,8)
srs-ResourceId ......... 1 usage=codebook ports=2
periodicity ............ sl20 offset 8 -- 10 ms
symbols ................ 1 (symbol 13) repetitionFactor n1
measured:
per-port-SNR ......... p0 18.4 dB, p1 17.9 dB
wideband-SINR ........ 15.2 dB -- after interference estimate
condition-number ..... 4.1 dB -- well conditioned
10:42:19.0125 MAC ul-sched rnti=0x4C1A (415,11)
sinr-source ............ pusch-dmrs (415,6) -- fresher than the SRS
dmrs-sinr .............. 14.6 dB
srs-sinr ............... 15.2 dB age 3 slots
sinr-used .............. 14.6 dB
ulla-offset ............ -0.90 dB -- separate from the DL offset
effective-sinr ......... 13.70 dB
phr-last ............... +9 dB @ (415,2) -- headroom available
rank-selected .......... 2 -- condition number 4.1 dB
mcs-selected ........... 13 -- Qm 4, R 490/1024, per layer
allocation ............. 60 PRB (PRB 20..79), 14 symbols
tbs .................... 30216 bits per layer
delta-tf-estimate ...... +4.9 dB -- deltaMCS enabled
predicted-tx-power ..... 14.2 dBm of 23.0 dBm -- 8.8 dB headroom
tpc-command ............ 1 (0 dB) -- power loop satisfiedListing 10. The uplink decision. Note that the scheduler uses the DMRS SINR rather than the SRS SINR because it is fresher, and uses the SRS for the rank decision because it covers both ports and the whole band. Note also predicted-tx-power: the scheduler checked that its own MCS choice would not push the UE into P_CMAX before committing to it.
22.5 A stuck offset suppressing throughput
-- Failure case. An interference burst at 10:43:02.1 lasted 30 ms. -- These lines are 200 ms after it ended. 10:43:02.340 MAC olla-clamp rnti=0x4C1A offset=-6.00 dB CLAMPED_LOW updates-since-clamp ... 0 cause-hint ............ 14 consecutive first-tx NACK 10:43:02.560 MAC dl-sched rnti=0x4C1A (452,4) csi-record ............ CQI 12, RI 1 age 6 slots equiv-sinr-from-cqi ... 15.80 dB -- the report says the link is fine olla-offset ........... -5.61 dB -- climbing at 0.056 dB per ACK effective-sinr ........ 10.19 dB mcs-selected .......... 14 -- should be 22 for this CQI predicted-bler ........ 0.004 -- 25x below the 0.100 target tbs ................... 28680 bits -- vs 51216 at MCS 22 measured-bler-100tti .. 0.006 updates-since-clamp ... 7 -- 7 ACKs, 0.39 dB recovered -- Diagnosis. CQI healthy, BLER 25x BELOW target, MCS 8 steps below what -- the CQI implies, offset recently clamped. This is not a radio problem -- and not a reporting problem: it is an integrator that was driven to -- its clamp by a transient and is now recovering at 0.056 dB per ACK. -- Remaining recovery: (6.00 - 0.39 - 1.50) / 0.056 = 73 more ACKs. -- At this UE's 40 ms traffic period that is about 2.9 seconds.
Listing 11. The signature of a stuck offset, and the reason §19 puts BLER against target at the top of the diagnostic tree. BLER far below target is not good news — it is capacity being thrown away, and here it names the mechanism precisely.
22.6 An uplink CRC failure that was never a link event
10:43:11.0080 PHY pusch-rx rnti=0x4C1A (461,7) granted-by ............ DCI 0_1 in (461,3), CORESET 1, AL 2 expected-prb .......... 20..79 symbols 0..13 dmrs-correlation ...... 0.02 -- no peak; nothing was sent rx-power .............. -119.4 dBm -- noise floor estimated-sinr ........ -24.8 dB crc ................... FAIL classification ........ NO_ENERGY_DETECTED ulla-update ........... SUPPRESSED -- excluded from the outer loop attributed-to ......... pdcch-miss (grant not received) 10:43:11.0085 MAC pdcch-stats rnti=0x4C1A window=1000 slots dci-0_1-transmitted ... 214 pusch-with-energy ..... 197 pusch-no-energy ....... 17 -- 7.9 % of grants not acted on coreset-1-load ........ 78 % of candidates blocked recommendation ........ raise aggregation level / review CORESET load -- A gNB that did NOT classify this would have applied 17 step_down -- events over 1000 slots -- 8.5 dB of spurious correction -- to a UE -- whose uplink channel was never the problem.
Listing 12. What correct handling looks like. The classification and ulla-update = SUPPRESSED lines are the ones that matter: the gNB checked for received energy before believing its own CRC result. Compare the last three lines of the listing with §14 and with the companion 31 PDCCH Blind Decoding.
23. Release Deltas: Rel-15 to Rel-18
| Release | Change | Why it was added | Effect on link adaptation |
|---|---|---|---|
| Rel-15 | The whole framework: three CQI tables, three MCS tables, CSI-ReportConfig with four report types, Type I and Type II codebooks, SRS-Config with four usages, MCS-C-RNTI | NR needed a CSI framework flexible enough for FR1 and FR2 and for eMBB and URLLC in the same cell | Everything in §4 to §17 except where noted below |
| Rel-16 | Enhanced Type II codebook and Type II port-selection codebook | Better PMI resolution for MU-MIMO with acceptable feedback overhead | Better precoding, therefore higher achievable SINR at the same transmit power, therefore a higher MCS for the same CQI. Larger reports, which push periodic reporting onto PUSCH. |
| Rel-16 | NR-U — operation in shared spectrum with listen-before-talk, lbt-FailureRecoveryConfig, consistent LBT failure detection | 5 GHz and 6 GHz unlicensed access | Introduces a failure that is indistinguishable from a decode failure to the uplink outer loop (§14). A ULLA implementation written before Rel-16 will mis-handle it. |
| Rel-16 | PUSCH repetition type B, and enhanced PDSCH/PUSCH aggregation | URLLC coverage without dropping to absurdly low code rates | Extends the adaptation range below MCS 0 — repetition becomes a continuation of the MCS ladder rather than a separate mechanism (§13) |
| Rel-17 | 1024QAM for FR1 downlink: a fourth MCS table and a fourth CQI table, selected by Rel-17 extensions to mcs-Table and cqi-Table | Fixed-wireless-access deployments with very high SINR were capped by 256QAM | Extends the top of the range to about 9.2 bits/RE. Requires far more accurate CSI and a much tighter outer loop, because at 1024QAM a 0.5 dB error costs a whole MCS step. Not useful below roughly 30 dB SINR. |
| Rel-17 | Unified TCI framework, and CSI enhancements for high-speed-train single-frequency-network deployments | FR2 beam management overhead, and Doppler on high-speed rail | Makes per-beam outer-loop scoping (§11.5) both easier to implement and more necessary, since a TCI state change is now a first-class event |
| Rel-17 | NTN — non-terrestrial networks with round-trip times of tens of milliseconds | Satellite access | The outer loop's feedback delay becomes comparable to its time constant, which changes it from an integrator into a delayed integrator — a fundamentally less stable control problem. HARQ feedback can be disabled entirely (see the companion 05 HARQ), in which case the outer loop has no input at all. |
| Rel-18 | Time-domain channel property reporting and Doppler-domain CSI, for medium and high UE velocities | The §8 staleness problem, addressed at the source: report the channel's rate of change so the gNB can extrapolate rather than extrapolating blindly | The first change in the whole series that attacks the organising problem of this document directly. A gNB that knows the Doppler can size its own fading margin instead of leaving the outer loop to discover it. |
| Rel-18 | Up to eight-port uplink SRS and eight-layer uplink MIMO | Uplink-heavy use cases and CPE devices with more antennas | Extends uplink rank adaptation from a 1–4 decision to a 1–8 decision, with the same correlated-channel trap (§17) and more ways to fall into it |
| Rel-18 | AI/ML for the air interface, including CSI compression and CSI prediction as study and work items | Feedback overhead and staleness are both fundamentally information-theoretic problems that learned encoders may address better than codebooks | Prospective. If CSI prediction lands, the inner loop stops being a lookup of a stale measurement and becomes an estimate of the channel at transmission time — which would reduce, though not remove, what the outer loop has to correct |
Table 29. A decade of link adaptation changes. The pattern is worth noting: Rel-15 to Rel-17 mostly extended the range — higher modulation, more layers, more codebooks — while Rel-18 is the first to attack the staleness that makes the outer loop necessary in the first place.
Two of these releases change what a log means rather than what the network does. Rel-17's fourth CQI and MCS tables mean that "CQI 11" now has four possible interpretations rather than three, and a log analysis tool written against Rel-15 will silently mis-read a 1024QAM cell. Rel-16's LBT failures mean that an uplink CRC failure count is no longer a radio statistic on shared spectrum. Both are the kind of change that breaks a dashboard without breaking a network.
24. Reading Link Adaptation in Logs: A Checklist
In order. Each step either resolves the problem or narrows it, and the order matters — steps 1 and 2 rule out whole classes of cause before you spend time on the rest.
- Get the first-transmission BLER and compare it against the target. Not the residual BLER, not the retransmission rate — the fraction of first transmissions that failed. If it is at target, the outer loop is working and the cause is upstream of it: go to step 4. If it is far above target, go to step 3. If it is far below target, go to step 5.
- Confirm the CQI table. Read
cqi-Tableout of the lastRRCReconfigurationfor this UE and confirm the scheduler is using the same one. Then readmcs-TablefromPDSCH-Configand confirm the UE's capability supports it. This takes two minutes and eliminates two of the eleven failure modes in §19. - BLER far above target: find out whether MCS reduction is helping at all. Plot BLER against scheduled MCS. If BLER falls as MCS falls, the loop is working and is simply still converging — check how long ago the condition started. If BLER stays high at every MCS including the lowest the loop reached, the failures are not caused by rate: suspect DMRS configuration, rank overestimation, a HARQ codebook mismatch, or pre-emption. Stop looking at link adaptation.
- BLER at target but throughput low: check the rank. Compare the reported RI in the CSI report against the layer count in the DCI antenna port field. A UE reporting RI 2 and being scheduled on one layer has half the throughput and a perfectly healthy BLER. If the rank is being overridden, find out why — usually a condition-number or BLER-history policy, and usually correct.
- BLER far below target: find the offset and find out whether it is clamped. If the gNB logs the outer-loop offset, read it. If it does not, measure it: plot scheduled MCS against reported CQI and read the vertical displacement from the expected mapping — in the 64QAM region of table 1 the expected slope is 2 MCS indices per CQI index, through (CQI 10, MCS 18). An offset at its clamp with healthy CQI and near-zero BLER is the stuck-offset signature of §22.5.
- Count CSI reports against the configuration. Decoded reports per second should equal 1000 / (
reportSlotConfig× slot duration). Zero means the inner loop is running on a vendor default and the MCS will be flat regardless of radio conditions. - Correlate every MCS excursion against non-radio events before radio ones. Handover completions, TCI state changes, SCell activations, DRX Active Time starts, BWP switches. An MCS dip that lines up with one of these is an offset-scope or offset-reset problem (§11.3, §11.4, §11.5), not a coverage problem — and it will not appear in any radio counter.
- In the uplink, read the PHR first. Zero or negative headroom invalidates every assumption both loops are making (§15.2). Only once headroom is confirmed positive is it worth looking at the uplink MCS as a link-adaptation question.
- In the uplink, separate missed grants from decode failures. For every failed PUSCH, look for received energy and a DMRS correlation peak on the granted resource. No energy means the grant was never acted on: that is a PDCCH problem or, on shared spectrum, an LBT problem. If those occasions are being fed into the outer loop, the uplink MCS distribution is meaningless (§14, §22.6).
- Only now look at the radio. If the loops are converged, the tables agree, the reports are arriving, the rank is right and the power loop has headroom, then a low MCS is the correct answer to a poor channel, and the problem is coverage, interference or beam selection. Hand it to the companion 20 Measurements and Events.
25. Glossary
| Term | Expansion | Meaning in this document |
|---|---|---|
| BLER | Block Error Rate | Unless qualified, the first-transmission transport block error rate — the quantity the outer loop controls. Not the residual error rate after HARQ. |
| CQI | Channel Quality Indicator | A 4-bit index into one of three (Rel-17: four) tables, meaning "the highest rate I could decode at this table's target error rate". A statement about the UE's receiver expressed as a rate, not a measurement of SINR. |
| DLLA / ULLA | Downlink / Uplink Link Adaptation | The whole two-loop mechanism in one direction |
| Δ_TF | Transport-format power term | The component of PUSCH transmit power that depends on the spectral efficiency of the chosen transport format — the coupling between the power loop and the rate loop (§15.1) |
| ILLA | Inner Loop Link Adaptation | The feedforward mapping from a channel estimate to an MCS. Fast, memoryless, partly specified. |
| MCS | Modulation and Coding Scheme | A 5-bit index into one of three (Rel-17: four) tables giving a modulation order Q_m and a target code rate R |
| MCS-C-RNTI | MCS-specific C-RNTI | A second C-RNTI whose only function is to select the low-SE MCS table for the DCIs it scrambles — the per-grant reliability switch |
| OLLA | Outer Loop Link Adaptation | The feedback integrator that corrects the inner loop's systematic bias using HARQ or CRC outcomes. Slow, stateful, specified nowhere. |
| Offset / Δ_olla | — | The outer loop's single state variable per UE, in dB, added to the inner loop's SINR estimate before the MCS lookup |
| PMI | Precoding Matrix Indicator | Which precoder from the configured codebook the UE recommends |
| Poisoning | — | Any situation where transport blocks fail for a reason other than SINR, causing the outer loop to lower the rate to no effect (§11.2) |
| RI | Rank Indicator | How many spatial layers the UE believes the channel supports |
| SRS | Sounding Reference Signal | The uplink reference signal the gNB measures to learn the uplink channel on resources the UE is not currently using |
| step_up / step_down | — | The outer loop's asymmetric increments. Their ratio encodes the target BLER; their magnitude sets the convergence time. |
| TBS | Transport Block Size | Derived, never signalled, from the allocation, the MCS, the layer count and the overhead assumptions TS 38.214 cl. 5.1.3.2 |
| Windup | — | An integrator holding a value it can no longer correct because its input has stopped. Applies to both the OLLA offset and the power loop's f(i,l) accumulator. |
26. References
- 3GPP TS 38.214 — Physical layer procedures for data. The central reference for this topic. Clause 5.1.3 for the PDSCH MCS tables and the modulation-order and target-code-rate determination; clause 5.1.3.2 for the transport block size procedure; clause 5.2 for the whole CSI reporting framework, with 5.2.2.1 for the CQI tables and the definition of what a CQI value means; clause 5.4 for the CSI computation delay requirements Z and Z′; clause 6.1.4 for the PUSCH MCS and transport block size determination.
- 3GPP TS 38.213 — Physical layer procedures for control. Clause 7 for uplink power control: 7.1 PUSCH, 7.2 PUCCH, 7.3 SRS, including the P_PUSCH formula, the Δ_TF definition, the TPC command mapping in Table 7.1.1-1 and the accumulation rules.
- 3GPP TS 38.212 — Multiplexing and channel coding. Clause 6.3 for uplink control information encoding, including the CSI part 1 / part 2 split and the CSI field ordering; clause 7.3.1.1.2 for DCI format 0_1 and 7.3.1.2.2 for DCI format 1_1, where the
mcs,ndi,rv, antenna port and TPC fields and their widths are defined. - 3GPP TS 38.331 — RRC protocol specification.
CSI-MeasConfig,CSI-ReportConfig,CSI-ResourceConfig,NZP-CSI-RS-Resource,CodebookConfig,PDSCH-Config,PUSCH-Config,PUSCH-PowerControl,P0-PUSCH-AlphaSetandSRS-Configwith all its children. - 3GPP TS 38.211 — Physical channels and modulation. CSI-RS sequence generation and mapping, and SRS — what the two loops are actually measuring.
- 3GPP TS 38.321 — MAC protocol specification. The
SP CSI reporting on PUCCH Activation/DeactivationMAC CE, the TPC MAC CE, and the HARQ entity whose feedback the outer loop consumes. - 3GPP TS 38.306 — UE radio access capabilities. Where
pdsch-256QAM-FR1, the 1024QAM capabilities and the CSI processing-unit limits live — the constraints that decide whether a configuration is legal for a given UE.
On the outer loop and 3GPP. It is worth being unambiguous, because the search for a clause reference wastes a lot of people's time: outer-loop link adaptation is not specified in any 3GPP specification. There is no clause describing it, no IE configuring it, no ASN.1 carrying it, no performance-measurement counter reporting it and no conformance test exercising it. The step sizes, the target BLER, the clamp range, the offset's scope, its behaviour at handover and at DRX wake-up, and whether DTX counts as a NACK are all implementation-defined. Everything in §9, §10, §11 and the second table of §18 describes what implementations do, drawn from the control problem's own arithmetic and from what is observable in logs — not what any specification requires. Where this document gives a number for one of these, it is marked as typical or as an assumption, and it should be checked against the specific product in front of you.
Companion documents in this set
- 02 Radio Frame Structure — slots, symbols and numerology, which every latency figure in §8 and every convergence time in §10 is counted in.
- 03 Random Access — the Msg3 grant, the one PUSCH scheduled with no CSI, no SRS and no outer loop at all (§13).
- 05 HARQ — the retransmission machinery whose ACK/NACK is the outer loop's only input, the reserved MCS indices 29–31, the HARQ-ACK codebooks whose mis-decoding poisons the loop (§11.2), the transport block size procedure this document reuses unchanged, and disabled HARQ feedback on NTN, which removes the outer loop's input entirely.
- 08 Scheduling — the DCI formats field by field, the TDRA tables, SPS and configured grants, and the survey-level treatment of link adaptation that this document expands.
- 10 BSR, PHR and SR — the power headroom report, which §13 and §15 treat as the single most informative uplink field, and its reporting triggers.
- 11 DRX — the Active Time rules and
drx-onDurationTimer, which set how stale an offset becomes during sleep (§11.4). - 20 Measurements and Events — CSI-RS as a measurement resource, and the beam selection that decides which channel the CQI describes.
- 22 Handover Overview — the mobility event at which the offset is either carried or discarded, and the source of the post-handover BLER spike in §11.3.
- 26 UE Capability —
pdsch-256QAM-FR1, the 1024QAM capabilities and the CSI processing-unit limits, which decide whether themcs-Tableandcqi-Tableyou configured are legal for this UE (§19). - 29 Carrier Aggregation — SCell activation, and why an offset scoped per UE rather than per carrier is wrong the moment a second carrier appears (§11.5).
- 31 PDCCH Blind Decoding — why a missed PDCCH produces silence rather than a detectable error, which is exactly what makes DTX ambiguous to both outer loops (§11.2, §14).
- 33 DMRS — the reference signal whose misconfiguration destroys transport blocks at every MCS, and the canonical example of the outer loop converting a reference-signal fault into an apparent coverage problem (§11.2, §19).
- 35 Physical Channels — where PDSCH, PUSCH, PUCCH and PDCCH sit in the resource grid, the PUCCH formats that carry CSI reports and HARQ-ACK, and the waveform choice that gives PUSCH two independent MCS table selections (§20).