>
Home5G NRPHY — Physical LayerLink Adaptation (OLLA/ILLA)
📶 PHY — Physical LayerAdvanced

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.

📚 3GPP-basedTS 38.214TS 38.213

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
  1. 01Why Fixed Modulation and Coding Cannot Work
  2. 02The Asymmetry That Shapes Everything
  3. 03Two Loops, Two Time Scales
  4. 04CSI Reporting: CQI, PMI, RI, CRI and LI
  5. 05CSI-ReportConfig: What, When, and On What
  6. 06The Three CQI Tables
  7. 07From CQI to MCS to Transport Block Size
  8. 08The Age of the Estimate: a Latency Budget
  9. 09The Downlink Outer Loop: What OLLA Actually Is
  10. 10The Step Ratio and the Convergence Time
  11. 11OLLA Pathologies: Where the Field Problems Live
  12. 12The Uplink Inner Loop: SRS and PUSCH DMRS
  13. 13Uplink MCS Selection and the Bandwidth Trade
  14. 14The Uplink Outer Loop and the Ambiguous CRC
  15. 15Link Adaptation Against Power Control
  16. 16Choosing a Target BLER
  17. 17Rank Adaptation: When Fewer Layers Win
  18. 18Parameter and Configuration Reference
  19. 19Failure Modes and What Each One Means
  20. 20Configuration Reference (ASN.1)
  21. 21Two More Worked Calculations
  22. 22Illustrative Message Traces
  23. 23Release Deltas: Rel-15 to Rel-18
  24. 24Reading Link Adaptation in Logs: A Checklist
  25. 25Glossary
  26. 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 atSpectral efficiencyFirst-tx BLER at 5 dB SINRRate at 20 dB SINR, against what was possibleNet effect
MCS 4 (QPSK, R = 308/1024)0.6016 bits/REwell under 1 %0.60 of 5.55 bits/RE — 11 % of capacityThe 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 capacityThe mid-cell works. The edge cannot decode at all and burns HARQ retransmissions until RLC gives up.
Adapted per grant0.2344 … 5.5547 bits/REheld at the target, ≈ 10 %essentially all of itEvery 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.

💡
Key point

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.

Figure 1. Read this figure top down and every difference in the rest of the document follows. The uplink estimate is roughly an order of magnitude fresher and is not quantised at all — and yet uplink link adaptation is generally harder to get right, because the gNB is adapting to a transmitter whose power it is also controlling (§15).
ConsequenceDownlinkUplink
Estimate freshness at decision time3–13 ms typical with reportSlotConfig = slots20; see §80.5–2 ms from periodic SRS, and effectively zero from the DMRS of the PUSCH just received
Estimate resolution4-bit CQI, ~1.5–1.8 dB per step in the 64QAM regionWhatever the gNB's own estimator produces — commonly 0.1 dB internal resolution
What the outer loop's ground truth isA HARQ-ACK bit the UE reports on PUCCH or multiplexed in PUSCHThe gNB's own CRC check on the received PUSCH
How the ground truth can lieA missed PDCCH means no HARQ-ACK at all; DTX read as NACKA missed grant, an LBT failure on shared spectrum, or a genuine decode failure are indistinguishable
Coupled control loopsNone with overlapping authority — downlink power is not per-UE closed-loopUplink 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.

Figure 2. The control structure, both directions on one diagram. Note which boxes are 3GPP and which are not: the tables at the top and bottom of the inner loop are specified to the last decimal place, and the arrow that closes the outer loop is specified nowhere.
Inner loop (ILLA)Outer loop (OLLA)
What it isA feedforward table lookupA feedback integrator with one state variable per UE
InputCQI report (DL) or SRS/DMRS SINR estimate (UL), plus the outer loop's offsetHARQ-ACK (DL) or PUSCH CRC result (UL)
Update rateEvery grant — up to once per slotEvery HARQ feedback — also up to once per slot
Effective time constantNone; it has no memoryTens to hundreds of TTIs, set by the step size
What it cannot doNotice that it is wrongReact to a fast fade — it is far too slow, and that is by design
Specified in 3GPP?The tables yes, the mapping between them noNot at all — not the rule, not the steps, not the state, not the reset behaviour
Typical symptom when it breaksMCS that does not track the reported CQI at allMCS 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.

💡
Key point

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.

QuantityExpands toWhat it answersSize on the wireWho uses it
CRICSI-RS Resource IndicatorWhich of the CSI-RS resources in the set gave the best channel — in practice, which beam⌈log₂(K)⌉ bits for K resources in the setBeam selection, before the MCS decision is even meaningful
RIRank IndicatorHow many spatial layers this channel can usefully carry1–3 bits, bounded by maxRank / ri-RestrictionLayer count in the DCI antenna port field; multiplies the TBS
PMIPrecoding Matrix IndicatorWhich precoder from the configured codebook the gNB should useCodebook-dependent; 5 bits for Type I single-panel, 4 ports, rank 1–2The precoder, not the MCS. A wrong PMI shows up as a low CQI next report.
CQIChannel Quality IndicatorWhich table entry the UE believes it could decode at the table's target error rate4 bits per CQI value; one wideband value, plus differentials per subband if configuredThe inner loop. This is the number the MCS comes from.
LILayer IndicatorWhich of the reported layers is the strongest⌈log₂(RI)⌉ bitsPT-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.

💡
Key point

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 valueFields reportedUsed for
noneNothingA report configuration used only to trigger measurement, or a placeholder
cri-RI-PMI-CQICRI, RI, PMI, wideband and optionally subband CQIThe workhorse for closed-loop MIMO PDSCH scheduling
cri-RI-i1-CQICRI, RI, i1, CQIAs above, with a rate estimate
cri-RI-CQICRI, RI, CQI — no PMINon-codebook or reciprocity-based precoding, where the gNB derives the precoder from SRS instead
cri-RSRP, ssb-Index-RSRPBeam identity and RSRPBeam management, not link adaptation — see the companion 20 Measurements and Events
cri-RI-LI-PMI-CQIAll fivePDSCH 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.

⚠️
Common pitfall

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.

FieldValuesTypicalWhat it decides
reportConfigId0 … 470, 1, 2The handle used by the aperiodic trigger states and by csi-ReportConfigToReleaseList
resourcesForChannelMeasurementA CSI-ResourceConfigIdthe serving-beam NZP setWhich CSI-RS the CQI describes. Get this wrong and the report is about a different beam.
csi-IM-ResourcesForInterferenceA CSI-ResourceConfigId, or absentconfigured in loaded networksWhether the reported CQI includes measured inter-cell interference or only thermal noise. Omitting it makes every CQI optimistic under load.
reportConfigTypeperiodic / semiPersistentOnPUCCH / semiPersistentOnPUSCH / aperiodicperiodicWhen the report happens and on which channel. See the next table.
reportQuantitySee §4cri-RI-PMI-CQIWhich of the five quantities are in the payload
reportFreqConfigurationcqi-FormatIndicator widebandCQI/subbandCQI; csi-ReportingBand bitmapwidebandCQIOne CQI for the whole BWP, or one per subband. Subband reporting is what makes frequency-selective scheduling possible.
timeRestrictionForChannelMeasurementsconfigured / notConfigurednotConfiguredconfigured forbids the UE from averaging across CSI-RS occasions — it must use only the most recent one. Fresher, noisier.
cqi-Tabletable1 / table2 / table3table2 for eMBBWhich CQI table the reported index refers to. See §6.
codebookConfigType I single/multi-panel, Type II, port selectionType I single-panelThe 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.

reportConfigTypeCarried onTriggered byLatencyWhen you want it
periodicPUCCHNothing — it just happens, every reportSlotConfig slots at reportSlotOffsetFixed, and up to a full period staleSteady-state connected-mode operation. Cheap, predictable, always there.
semiPersistentOnPUSCHPUSCHActivated by DCI 0_1 with an SP-CSI-RNTI-scrambled CRCAs periodic once activeLarger payloads — Type II codebooks, many subbands — that will not fit in a PUCCH
aperiodicPUSCHOne CSI request field in DCI 0_1, selecting a trigger stateBounded by the CSI computation time Z / Z′ TS 38.214 cl. 5.4Immediately 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.

🧮
Worked calculation

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.

Figure 3. The three tables plotted against reported index. Table 2 reaches 7.4063 bits/RE because it includes 256QAM; table 3 stops at 4.5234 and starts at 0.0586 because it is a low-spectral-efficiency table built for a 10⁻⁵ residual error target rather than 10⁻¹.
cqi-Table valueTS 38.214 tableHighest modulationEfficiency range (bits/RE)Target block error rateConfigured for
table1Table 5.2.2.1-264QAM0.1523 … 5.554710⁻¹The Rel-15 baseline; still the default in many deployments
table2Table 5.2.2.1-3256QAM0.1523 … 7.406310⁻¹eMBB on a UE that supports 256QAM PDSCH and mcs-Table = qam256
table3Table 5.2.2.1-464QAM0.0586 … 4.523410⁻⁵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 indexModulationCode rate × 1024Efficiency (bits/RE)
0out of range
1QPSK780.1523
2QPSK1200.2344
3QPSK1930.3770
4QPSK3080.6016
5QPSK4490.8770
6QPSK6021.1758
716QAM3781.4766
816QAM4901.9141
916QAM6162.4063
1064QAM4662.7305
1164QAM5673.3223
1264QAM6663.9023
1364QAM7724.5234
1464QAM8735.1152
1564QAM9485.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.

⚠️
Common pitfall

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.

⚠️
Common pitfall

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.

Figure 4. Five steps, of which two are 3GPP table lookups and three are the vendor's. Steps 3 and 4 are where two schedulers given the same report legitimately diverge.
🧮
Worked calculation

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.

🧮
Worked calculation

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.

MCSQ_mR × 1024Efficiency (bits/RE)TBS in this geometryCode blocks
1344901.914125 104 bits3
1546162.406331 752 bits4
1646582.570333 816 bits5
1764382.566433 552 bits4
1864662.730535 856 bits5
2065673.322344 040 bits6
2467724.523459 976 bits8
2869485.554773 704 bits9

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.

Figure 5. The end of the chain: the whole inner-loop decision leaves the gNB as a five-bit field. Note what sits two bits away from it — the TPC command, which is the other closed loop's actuator (§15). The two loops are neighbours in the same DCI.

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.

Figure 6. Periodic wideband CQI at 30 kHz SCS with 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.
StageSlots at 30 kHzmsWhat sets it
Channel measured on the CSI-RS occasion0 (reference instant)0NZP-CSI-RS-Resource periodicity and offset
UE filters and computes CQI, RI, PMI31.5The 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 slot10.5reportSlotConfig and reportSlotOffset
PUCCH transmitted, gNB decodes the UCI10.5PUCCH format and the gNB's own decode pipeline
gNB writes the CSI record; scheduler can use it10.5Implementation
Earliest PDSCH using this report63.0The 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 landsup to 25up to 12.5reportSlotConfig. 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.

🧮
Worked calculation

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 msevery 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.

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.

Figure 8. The outer loop finding its equilibrium, from a simulation of the exact update rule in §9 against the SINR-to-BLER model below. The offset starts at the +3 dB clamp — a UE whose offset was carried over from a cell where the bias was positive — and has to travel 6 dB. Watch the shape of the second panel: steep downward edges, ramps nine times shallower, and an average that converges even though the instantaneous value never settles.
📘
Spec detail

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.

🧮
Worked calculation

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 errorDirectionUpdates neededAt 2000 TTI/s (saturated)At 25 TTI/s (40 ms bursts)At 2 TTI/s (keep-alive)
2 dBtoo optimistic4 NACKs2 ms0.16 s2 s
6 dBtoo optimistic12 NACKs6 ms0.5 s6 s
6 dBtoo pessimistic114 ACKs57 ms4.6 s57 s
11 dB (clamp to clamp)too pessimistic208 ACKs104 ms8.3 s104 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.

💡
Key point

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.

Figure 9. A 30 ms interference burst that lands on the PDSCH resource elements but not on the CSI-RS or CSI-IM resources. The reported CQI never moves — the top panel's grey trace is unaffected throughout — so the inner loop sees a healthy channel from beginning to end. Only the outer loop reacts, it winds down to its clamp in fifteen updates, and then needs 269 TTIs to climb back. Cause: 30 ms. Consequence: 134 ms.
What actually caused the NACKWhy the outer loop cannot tellWhat it does insteadLog signature
A DMRS configuration mismatch — wrong dmrs-AdditionalPosition, wrong number of CDM groups without data, wrong dmrs-TypeThe channel estimate at the UE is wrong, so demodulation fails regardless of SINRDrives the offset to the clamp and holds it there for as long as the misconfiguration lastsBLER 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 9CSI-RS and CSI-IM occupy different resource elements and may be in different slotsWinds down during the burst, then climbs back at 0.056 dB per ACK for several hundred TTIsAn offset excursion that begins and ends long after the interference counter spike
A missed PDCCH — the UE never saw the grantThere is no HARQ-ACK, and DTX is commonly counted as a NACKAttributes a control channel coverage problem to the data channel, and lowers the data rate, which does nothing about itNACK 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 PDSCHThe pre-empted resource elements are simply noise to the decoderLowers the offset for a UE whose channel is fineOffset 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.

⚠️
Common pitfall

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.

PolicyWhat it costsWhat it buysWhere it shows up
Discard — start the new cell at 0 dB, or at a per-cell defaultThe 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 contextA 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:

ScopeWhat happens on a beam switchWhat happens on SCell activationVerdict
One offset per UEThe 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 stateInstant — 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 cellWrong, 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 rangeWorst-case excursionWorst-case recovery, saturated UEWorst-case recovery, 2 TB/s UERisk
±3 dB3 dB below equilibrium54 ACKs = 27 ms27 sA UE with a genuine 5 dB bias never converges; BLER stays above target permanently
±6 dB6 dB108 ACKs = 54 ms54 sA reasonable compromise, and the most common choice
−8 … +4 dB8 dB144 ACKs = 72 ms72 sCovers almost any real bias, at the price of a two-minute recovery from a poisoning event on an idle-ish UE
UnboundedUnboundedUnboundedUnboundedOne 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.

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.

Figure 10. Delivered spectral efficiency against target BLER, under a model where each decade of BLER reduction costs 2 dB of extra margin and 3 dB buys one bit per resource element. The blue bars are what the loop settles on; the orange bars are what is actually delivered once failed first transmissions are discounted. The maximum of the orange curve falls at a target of 9.6 %.
🧮
Worked calculation

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.

ServiceTarget first-tx BLERHow it is achievedWhy that targetCost
eMBB — the default10⁻¹CQI table 1 or 2, MCS table 1 or 2, step_up/step_down = 1/9HARQ recovers cheaply, the retransmission latency is affordable, and the model above peaks hereA 4 ms tail on one transmission in ten
URLLC10⁻⁵ (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 succeedUp 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.

📘
Spec detail

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.

🧮
Worked calculation

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.

SituationReported RIWhat a good scheduler doesWhy
Rich scattering, high SINR2, 3 or 4Use the reported rankThe 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 antennasOften still 2, because the UE reports the rank that maximises its own estimated throughput without knowing about the codeword structureOverride down to 1 and raise the MCS insteadOne 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 tries2Override down to 1 and let the loop re-convergeThis 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 mobilityWhatever was reported 12 ms agoBias towards lower rankThe 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.

🔍
What you see in logs

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 / fieldWhere it livesRangeTypicalEffect
cqi-TableCSI-ReportConfigtable1 / table2 / table3table2Which CQI table the reported index means, and therefore the target BLER the report is made against (§6)
mcs-TablePDSCH-Config, PUSCH-Configqam256 / qam64LowSE (absent = the 64QAM table)qam256Which MCS table the mcs field of the DCI indexes. Must be consistent with the UE's reported capability.
mcs-TableTransformPrecoderPUSCH-Configqam256 / qam64LowSEabsentThe MCS table for transform-precoded (DFT-s-OFDM) PUSCH, which is a separate selection from the CP-OFDM one
reportSlotConfigCSI-ReportConfigperiodicslots4slots320slots20Report period in slots. The dominant term in the CSI age budget (§8).
reportSlotOffsetCSI-ReportConfigaperiodic0 … 320 … 4Slot offset from the triggering DCI to the report
ri-RestrictionCodebookConfigcodebookTypeBIT STRING, one bit per rankall allowedForbids the UE from reporting specific ranks — the cleanest guard against rank overestimation (§17)
maxRankPUSCH-Config1 … 41 or 2Caps the uplink layer count
periodicityAndOffset-pSRS-ResourceresourceTypeperiodicsl1sl2560sl20SRS period, and therefore the age of the uplink channel estimate (§12)
repetitionFactorSRS-ResourceresourceMappingn1 / n2 / n4n1SRS repetition; raises the sounding SNR for a power-limited UE
p0, alphaP0-PUSCH-AlphaSetp0 −16 … 15 (dB, relative); alpha0alpha1alpha08The received-SINR operating point uplink power control aims at (§15)
tpc-AccumulationPUSCH-PowerControldisabled, or absentabsent (i.e. accumulation enabled)Whether f(i,l) accumulates. Note the inverted sense of the field.
deltaMCSPUSCH-PowerControlenabled, or absentvariesWhether transmit power tracks the transport format via Δ_TF (§15.1)
pusch-AggregationFactorPUSCH-Confign1 / n2 / n4 / n8n1Slot-level repetition — the lever that remains once MCS 0 is reached
codeBlockGroupTransmissionPDSCH-ServingCellConfigmaxCodeBlockGroupsPerTransportBlock 2/4/6/8absentChanges 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 rangeCommon defaultWhat it doesSymptom if wrong
Outer-loop step down0.1 … 1.0 dB0.5 dBThe offset decrement on a NACKToo large: MCS oscillation and a wide sawtooth. Too small: slow escape from an optimistic state.
Outer-loop step upstep_down · p/(1−p)0.056 dBThe 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 dBBounds the pathological excursionToo wide: long outages after poisoning. Too tight: a genuinely biased UE never converges.
Handover reset policydiscard / carry / carry a fractionvaries by vendorWhat 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 handlingcount as NACK / ignore / reduced stepcount as NACKWhether a missing HARQ-ACK drives the offset downPDCCH coverage problems presenting as low MCS (§11.2)
CQI-to-SINR mapping tablea per-vendor table or curveproprietaryStep 2 of the chain in §7MCS that does not track CQI with the slope you expect
Rank override policytrust RI / cap by condition number / cap by BLER historyvariesWhether 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 failsWho detects itWhat the network doesLog signature and what it points at
OLLA windup — the offset freezes at a bad value because the UE stopped being scheduledNobody. 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 recoversMCS 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 mismatchNobody 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 goneFirst-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 directionBLER 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 absentCSI-ReportConfig released, PUCCH resource collision, reportQuantity = cri-RSRP only, or the UE not reportingThe 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 MCSMCS 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 supportmcs-Table = qam256 on a UE without 256QAM PDSCH capabilityThe UE, by failing to decode; or the RRC layer, if the gNB checks capability before configuringEverything above the 64QAM range fails; the outer loop drives down until the scheduled MCS happens to fall in the range the UE can actually demodulateBLER 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 antennasNobody. 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 helpHigh 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 failureNobody. Both look like "expected data did not arrive".Lowers the uplink MCS in response to a downlink control channel problem, achieving nothingUplink 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 adaptingf(i,l) winds up, the outer loop corrects a variable it does not controlThe 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 loudInterference 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 budgetLatency 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.

💡
Key point

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

🧮
Worked calculation

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

🧮
Worked calculation

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

🔍
ABOUT THESE 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 = slots20

Listing 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 satisfied

Listing 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

ReleaseChangeWhy it was addedEffect on link adaptation
Rel-15The 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-RNTINR needed a CSI framework flexible enough for FR1 and FR2 and for eMBB and URLLC in the same cellEverything in §4 to §17 except where noted below
Rel-16Enhanced Type II codebook and Type II port-selection codebookBetter PMI resolution for MU-MIMO with acceptable feedback overheadBetter 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-16NR-U — operation in shared spectrum with listen-before-talk, lbt-FailureRecoveryConfig, consistent LBT failure detection5 GHz and 6 GHz unlicensed accessIntroduces 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-16PUSCH repetition type B, and enhanced PDSCH/PUSCH aggregationURLLC coverage without dropping to absurdly low code ratesExtends the adaptation range below MCS 0 — repetition becomes a continuation of the MCS ladder rather than a separate mechanism (§13)
Rel-171024QAM for FR1 downlink: a fourth MCS table and a fourth CQI table, selected by Rel-17 extensions to mcs-Table and cqi-TableFixed-wireless-access deployments with very high SINR were capped by 256QAMExtends 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-17Unified TCI framework, and CSI enhancements for high-speed-train single-frequency-network deploymentsFR2 beam management overhead, and Doppler on high-speed railMakes 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-17NTN — non-terrestrial networks with round-trip times of tens of millisecondsSatellite accessThe 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-18Time-domain channel property reporting and Doppler-domain CSI, for medium and high UE velocitiesThe §8 staleness problem, addressed at the source: report the channel's rate of change so the gNB can extrapolate rather than extrapolating blindlyThe 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-18Up to eight-port uplink SRS and eight-layer uplink MIMOUplink-heavy use cases and CPE devices with more antennasExtends 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-18AI/ML for the air interface, including CSI compression and CSI prediction as study and work itemsFeedback overhead and staleness are both fundamentally information-theoretic problems that learned encoders may address better than codebooksProspective. 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.

🔄
Release delta

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.

25. Glossary

TermExpansionMeaning in this document
BLERBlock Error RateUnless qualified, the first-transmission transport block error rate — the quantity the outer loop controls. Not the residual error rate after HARQ.
CQIChannel Quality IndicatorA 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 / ULLADownlink / Uplink Link AdaptationThe whole two-loop mechanism in one direction
Δ_TFTransport-format power termThe 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)
ILLAInner Loop Link AdaptationThe feedforward mapping from a channel estimate to an MCS. Fast, memoryless, partly specified.
MCSModulation and Coding SchemeA 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-RNTIMCS-specific C-RNTIA second C-RNTI whose only function is to select the low-SE MCS table for the DCIs it scrambles — the per-grant reliability switch
OLLAOuter Loop Link AdaptationThe feedback integrator that corrects the inner loop's systematic bias using HARQ or CRC outcomes. Slow, stateful, specified nowhere.
Offset / Δ_ollaThe outer loop's single state variable per UE, in dB, added to the inner loop's SINR estimate before the MCS lookup
PMIPrecoding Matrix IndicatorWhich precoder from the configured codebook the UE recommends
PoisoningAny situation where transport blocks fail for a reason other than SINR, causing the outer loop to lower the rate to no effect (§11.2)
RIRank IndicatorHow many spatial layers the UE believes the channel supports
SRSSounding Reference SignalThe uplink reference signal the gNB measures to learn the uplink channel on resources the UE is not currently using
step_up / step_downThe outer loop's asymmetric increments. Their ratio encodes the target BLER; their magnitude sets the convergence time.
TBSTransport Block SizeDerived, never signalled, from the allocation, the MCS, the layer count and the overhead assumptions TS 38.214 cl. 5.1.3.2
WindupAn 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.214Physical 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.213Physical 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.212Multiplexing 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.331RRC protocol specification. CSI-MeasConfig, CSI-ReportConfig, CSI-ResourceConfig, NZP-CSI-RS-Resource, CodebookConfig, PDSCH-Config, PUSCH-Config, PUSCH-PowerControl, P0-PUSCH-AlphaSet and SRS-Config with all its children.
  • 3GPP TS 38.211Physical channels and modulation. CSI-RS sequence generation and mapping, and SRS — what the two loops are actually measuring.
  • 3GPP TS 38.321MAC protocol specification. The SP CSI reporting on PUCCH Activation/Deactivation MAC CE, the TPC MAC CE, and the HARQ entity whose feedback the outer loop consumes.
  • 3GPP TS 38.306UE 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.
📘
Spec detail

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 Capabilitypdsch-256QAM-FR1, the 1024QAM capabilities and the CSI processing-unit limits, which decide whether the mcs-Table and cqi-Table you 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).