>
Home5G NRRRC — Radio Resource ControlMeasurements & Events
🗂️ RRC — Radio Resource ControlIntermediate

Measurements & Events (measConfig → measReport) in 5G NR

How RRCReconfiguration configures measId / reportConfigId / measObjectId, and how events A1–A6 trigger a MeasurementReport.

📚 3GPP-basedTS 38.331

A UE never decides its own handover. It measures, it filters, it reports — and the network decides. This page is the machinery that tells the UE what to measure, when a measurement is worth reporting, and how that report crosses the air interface. All of it lives in one RRC IE, measConfig, delivered inside an RRCReconfiguration (TS 38.331), and it all ends in one message going the other way: the MeasurementReport.

Introduction

Measurements and mobility are the connected-mode counterpart of cell reselection. Where an idle UE moves itself, a UE in RRC_CONNECTED is told where to go: it gathers radio-quality evidence about the serving cell and its neighbours, sends that evidence upward when a configured condition is met, and the gNB decides whether — and to which cell — to hand it over. The entire configuration is pushed down in a single RRC IE, measConfig, and the entire result comes back in a single RRC message, MeasurementReport.

This procedure runs continuously throughout a connection and underpins almost every mobility feature: intra- and inter-frequency handover, inter-RAT fallback to LTE, carrier-aggregation SCell management, and the newer conditional handover (CHO) and NTN triggers. Get it wrong and you see the classic connected-mode failures — late handovers and dropped calls at cell edge, ping-pong between two cells, or handovers to a cell that is strong but congested. Get the filtering, hysteresis and timers right and mobility is invisible to the user.

Because the flow is a chain — configure objects, bind them to report configs, filter the samples, evaluate the event, transmit the report — a mobility problem almost always localises to one link. Knowing whether the UE was never configured to measure a layer, could not measure it (no gap), filtered too heavily, never satisfied the event, or sent a report the gNB ignored, turns a vague "handover failure" into a specific, testable hypothesis. The stage-by-stage debugging boxes below follow that chain.

Why measurements & mobility exist

💡

In plain words: think of the UE as a scout with a radio, and the gNB as the commander. The scout does not decide where the unit moves — it just watches the terrain and radios back "the ridge to the east is now clearly better than where we are, and it has stayed that way for a full minute." The commander decides whether to move. The events (A1–A6, B1/B2) are the pre-agreed phrases the scout is allowed to say; hysteresis and timeToTrigger are the "be sure before you call it in" discipline that stops false alarms.

Concretely, a handover decision needs evidence, but streaming every raw radio sample uplink would be wasteful and noisy. So the network configures the UE to report only when something meaningful happens: a neighbour becomes clearly better than the serving cell, the serving cell drops below a floor, an inter-RAT layer becomes viable. The UE smooths the inherently jittery radio, checks the configured condition, and only if the condition holds long enough does it spend uplink resources on a MeasurementReport. This keeps signalling low, suppresses ping-pong, and still gives the gNB timely, trustworthy evidence to act on.

What

A framework in RRC where the gNB configures measurement objects, report configurations, and the links between them, and the UE returns MeasurementReports when a configured condition is satisfied.

Why

Mobility decisions need radio-quality evidence about the serving cell and its neighbours. Rather than stream raw samples, the UE reports only when something meaningful happens — a neighbour gets strong, the serving cell gets weak.

How

Three add/mod lists inside measConfig: measObjectToAddModList, reportConfigToAddModList, and measIdToAddModList. Filtering is set by quantityConfig; measurement gaps by measGapConfig.

The measurement framework

Think of the framework as three lists and one glue field. The gNB hands the UE a list of things to watch (measurement objects), a list of conditions that make a measurement worth sending (report configurations), and a list of pairings that bind one object to one report config (measurement identities). Once those are in place the UE runs a small state machine per pairing: sample the radio, smooth the samples, check the condition, and if it holds long enough, send a report.

🎯

The core idea: a measObject (what to measure) plus a reportConfig (when to report) become a single measurement identity through a measId. Everything on this page hangs off that one binding — the object names a frequency layer, the report config names a trigger, and the measId welds them together and is echoed back in the report so the gNB knows exactly which pairing fired.

measConfig — the container

One IE holds the whole configuration. Everything the UE knows about what to measure and when to report arrives inside measConfig, and it is almost always sent as a delta — add these, modify those, remove the rest.

Each list comes in a matched pair: a ...ToAddModList that adds new entries or overwrites existing ones by id, and a ...ToRemoveList that deletes them by id. That delta structure is why mobility reconfiguration is cheap: to swap a neighbour layer the gNB removes one measObjectId and adds another, without resending the rest. The measConfig itself is carried in an RRCReconfiguration and acknowledged by RRCReconfigurationComplete. The components below make up a complete measConfig.

Component IERoleKey fields
measObjectToAddModListWhat to measure — one entry per frequency layermeasObjectId, MeasObjectNR (ssbFrequency, SMTC, refFreqCSI-RS)
reportConfigToAddModListWhen to report — the trigger and reporting cadencereportConfigId, reportType, event, timeToTrigger
measIdToAddModListThe glue — binds one object to one report configmeasId, measObjectId, reportConfigId
quantityConfigL3 filtering applied before events are evaluatedfilterCoefficient per quantity (RSRP/RSRQ/SINR)
measGapConfigGaps so the UE can retune to measure other frequenciesgapFR1/gapFR2/gapUE, gap length & period
s-MeasureConfigServing-cell quality below which neighbour measurement startsssb-RSRP / csi-RSRP threshold

A subtle but important field is s-MeasureConfig: it lets the UE skip neighbour measurement entirely while the serving cell is comfortably strong, waking the neighbour search only once serving quality drops below the configured threshold. It is a pure battery optimisation — there is no point burning cycles hunting neighbours when the current cell is excellent.

📘

Spec note: measGapConfig matters whenever the UE cannot measure a target while serving traffic — typically an inter-frequency or inter-RAT layer that needs an RF retune. Intra-frequency measurement usually needs no gap because the target shares the serving carrier's bandwidth. Gaps are configured per-UE (gapUE) or per-frequency-range (gapFR1, gapFR2).

✅ Debugging steps

  • Confirm the UE received the RRCReconfiguration carrying measConfig and returned RRCReconfigurationComplete — an un-acked reconfig means the UE is measuring on the old (or no) config.
  • Decode the three add/mod lists and check the target neighbour layer actually has a measObjectId present after the delta is applied.
  • Verify s-MeasureConfig: if it is set high, the UE may legitimately not be measuring neighbours yet because the serving cell is strong.
  • For inter-frequency/inter-RAT targets, confirm a matching measGapConfig exists — without a gap the UE physically cannot retune to measure them.

⚠ Common causes of failure

  • RRCReconfiguration lost or rejected, so the intended measConfig never took effect.
  • Delta mis-applied: the object for the target layer was removed but not re-added, so nothing watches it.
  • s-MeasureConfig set so high the UE never starts neighbour measurement before the serving cell fails.
  • Missing measurement gap for an inter-frequency/inter-RAT object, so the object exists but is never measured.

measObjectNR — what to measure

Every measurement starts with an object that names a piece of spectrum and the reference signal to measure on it. In NR that object is a MeasObjectNR, and it answers "which frequency, and using which reference signal?"

Inside measObjectToAddModList each entry is a MeasObjectNR tagged with a measObjectId. Its anchor field is ssbFrequency — the SSB centre frequency (the Global Synchronization Channel Number, or GSCN raster point) where the UE searches for cells — paired with ssbSubcarrierSpacing. A single object describes one frequency layer: reuse the serving carrier's frequency for intra-frequency measurement, or set a different ssbFrequency for an inter-frequency layer. That is the entire distinction between intra- and inter-frequency measurement — same object frequency versus different object frequency.

Two more parts shape how the object is measured:

  • SMTC — the smtc1 (and optional smtc2) SSB Measurement Timing Configuration. It is a window (periodicity plus offset plus duration) that tells the UE when the target cell's SSB burst occurs, so the UE only opens its receiver during that window instead of listening continuously. SMTC periodicity ranges from 5 ms to 160 ms and is the single biggest lever on inter-frequency measurement effort.
  • refFreqCSI-RS — the reference point for CSI-RS based measurement, used when the object also carries a csi-rs-ResourceConfigMobility. This lets the UE measure on CSI-RS resources rather than (or in addition to) SSB, giving finer beam resolution.

The object can also carry a cell list — cellsToAddModList for cell-specific offsets (Ocn), plus blackCellsToAddModList (never report) and whiteCellsToAddModList (only report these). For the mobility mental model, keep it short: one measObjectId equals one frequency layer to be watched, with an SMTC that says when to look.

🎯

Say it in one line: ssbFrequency + SMTC = the "where and when" of a frequency layer; refFreqCSI-RS switches the measurement basis from SSB to CSI-RS. Everything else in MeasObjectNR is offsets and filter cell lists.

✅ Debugging steps

  • Confirm the object's ssbFrequency/ssbSubcarrierSpacing actually match the target cell's SSB — a wrong GSCN means the UE looks where there is no SSB.
  • Check the SMTC (periodicity/offset/duration) lines up with the target's SSB burst; a misaligned window means the UE opens its receiver when nothing is transmitted.
  • If the target should be measured on CSI-RS, verify refFreqCSI-RS and csi-rs-ResourceConfigMobility are present.
  • Check blackCellsToAddModList is not silently suppressing the expected neighbour (and, if a whitelist is used, that the neighbour is on it).

⚠ Common causes of failure

  • ssbFrequency off the actual raster point, so the UE never finds the target cell's SSB.
  • SMTC offset/period misaligned with the neighbour's SSB timing, so measurements read noise instead of the SSB.
  • Target PCI on the blackCellsToAddModList (or absent from a configured whitelist), so it is never reported.
  • CSI-RS measurement configured without a valid refFreqCSI-RS, so the beam-level measurement basis is undefined.

reportConfig, measId and gaps — when to report

An object tells the UE where to look. A report config tells it what makes a measurement worth sending, and how often to keep sending once it starts. The measId then binds the two into a live measurement.

Each entry in reportConfigToAddModList is a ReportConfigNR identified by a reportConfigId. Its reportType is one of two things:

  • periodical — report on a timer regardless of any condition. Used for logging, SON, and continuous serving-cell tracking.
  • eventTriggered — report only when an event condition (A1–A6, B1/B2) fires. This is the mobility workhorse.

An event-triggered config carries a triggerQuantityrsrp, rsrq, or sinr — which selects the quantity the condition is evaluated on, together with three timing/margin controls that appear on every event:

  • hysteresis — a margin (in 0.5 dB steps) added to the entering condition and subtracted from the leaving condition, so the UE does not flip-flop around the boundary.
  • timeToTrigger — the condition must stay true continuously for this long (0 ms up to 5120 ms) before the UE actually reports.
  • reportOnLeave — send an extra report when the leaving condition later becomes satisfied.

Independent of the event, the config sets the reporting cadence: reportInterval and reportAmount (how often and how many times to repeat once triggered), reportQuantityCell / reportQuantityRsIndexes (which of RSRP/RSRQ/SINR to include, at cell and beam level), maxReportCells (how many neighbours to list), and rsType (whether reported quantities derive from ssb or csi-rs).

Now the glue. Each entry in measIdToAddModList is a MeasIdToAddMod carrying exactly three things: a measId, a measObjectId, and a reportConfigId. It says "watch this object with this report config, and call the pairing this measId." The same object can be reused under several measIds (A3 and A5 on the same neighbour layer); the same report config can be reused across frequency layers. The measId is also what the UE echoes back in measResults, so the gNB always knows which pairing fired.

🎯

The measId is the identity: object (what) + report config (when) → one measurement (a measId). Remove the measId and the pairing goes dormant even if the object and report config still exist.

✅ Debugging steps

  • Confirm a measId actually binds the target object to a report config — an orphaned object or report config produces no reports.
  • Check the event's triggerQuantity matches the quantity you expect the decision on (RSRP vs RSRQ vs SINR).
  • Verify hysteresis and timeToTrigger are sane for the scenario — a long TTT plus heavy filtering makes reporting sluggish.
  • For inter-frequency/inter-RAT report configs, re-check that a measurement gap covers the object so the event can ever be evaluated.

⚠ Common causes of failure

  • No measId linking the object and report config, so a perfectly good object is never evaluated.
  • timeToTrigger too long for the mobility speed, so the report arrives after the link has already failed (late handover).
  • timeToTrigger/hysteresis too small, so ping-pong reports flood uplink and cause back-and-forth handovers.
  • Event trigger quantity mismatched to intent (e.g. triggering on RSRP where interference, not coverage, is the problem).

The events: A1–A6 and B1/B2

Events are the language mobility speaks. Each one is a small inequality comparing a measured quantity against a threshold or against the serving cell, wrapped in hysteresis and timeToTrigger. Get the six intra/inter-frequency A-events and the two inter-RAT B-events straight and you can read almost any operator's mobility policy.

EventEntering conditionTypical use
A1Serving becomes better than a thresholdStop / cancel ongoing gap-based measurement (serving recovered)
A2Serving becomes worse than a thresholdStart neighbour measurement; trigger to look for an escape cell
A3Neighbour becomes better than serving by an offsetIntra-frequency / equal-priority inter-frequency handover — the workhorse
A4Neighbour becomes better than a thresholdLoad balancing; blind/coverage-triggered handover to a target layer
A5Serving worse than Thresh1 AND neighbour better than Thresh2Coverage handover — move only once the serving cell is genuinely failing
A6Neighbour becomes better than a SCell by an offsetCarrier aggregation — SCell change within the same frequency
B1Inter-RAT neighbour (e.g. E-UTRA) better than a thresholdRedirect / handover toward another RAT (LTE)
B2Serving worse than Thresh1 AND inter-RAT neighbour better than Thresh2Inter-RAT coverage fallback (e.g. NR → LTE at the edge)

A3 is the workhorse for intra-frequency handover, and it is the one worth writing out. Its entering condition (called the A3 enter or condition 1) compares the neighbour to the serving cell after applying all offsets and hysteresis:

Mn + Ofn + OcnHys > Mp + Ofp + Ocp + Off

Where Mn is the neighbour's L3-filtered measurement, Ofn/Ocn are the frequency-specific and cell-specific offsets for the neighbour, Hys is the hysteresis, Mp is the serving (primary) cell measurement, Ofp/Ocp are the serving-side offsets, and Off is the event's a3-Offset. In plain terms: the neighbour must beat the serving cell by more than the offset plus hysteresis, and stay that way for timeToTrigger. The corresponding leaving condition simply flips the inequality and reverses the sign of hysteresis (add Hys instead of subtract), which is exactly what gives A3 its ping-pong-resistant deadband.

Every event has this enter/leave pair. For a threshold event like A2 the enter condition is Mp + Hys < Thresh and the leave condition is Mp − Hys > Thresh — the same hysteresis band, applied to a fixed threshold instead of to the serving cell. The dual-threshold events (A5, B2) require both sub-conditions to hold simultaneously for the whole timeToTrigger.

🎯

A3 vs A5: A3 is relative — neighbour versus serving, so it fires even when both are strong. A5 is absolute on both sides — serving must drop below Thresh1 and a neighbour must rise above Thresh2. Use A3 for normal equal-coverage mobility; use A5 (or A2→A4/A5) when handover should happen only once the serving cell is genuinely weak.

The figure below shows the A3 entering condition unfolding over time — the neighbour rising past the effective threshold, the hysteresis and offset that build that threshold, and the timeToTrigger window that must elapse before the report is sent.

RSRP time → serving Mₚ (roughly flat) effective threshold = Mₚ + Off + Hys neighbour Mₙ rising Off + Hys condition first true timeToTrigger TTT expires → report
Figure 1. Event A3 entering condition over time. The neighbour must exceed the serving cell plus a3-Offset plus hysteresis, and remain above that effective threshold for the full timeToTrigger before a MeasurementReport is sent.

✅ Debugging steps

  • For a late/missed handover, reconstruct the fired event's inequality from logged Mn/Mp and the configured offsets/hysteresis, and confirm whether the condition was ever met.
  • Check timeToTrigger against how long the condition actually held — a condition that met but did not persist for TTT sends no report.
  • Confirm the right event is configured for the intent (A3 for equal-coverage, A5 for edge-only, A4 for load/blind, B1/B2 for inter-RAT).
  • Verify per-cell (Ocn) and per-frequency (Ofn) offsets are not biasing the target out of contention.

⚠ Common causes of failure

  • a3-Offset/hysteresis too large, so a genuinely better neighbour never crosses the effective threshold (late handover, edge drop).
  • timeToTrigger too long for the fading rate, so the report is sent after radio-link failure.
  • Wrong event type for the goal (e.g. A3 where the serving cell should have to be weak first, causing early handovers).
  • Cell/frequency offsets mis-signed, suppressing the correct target or promoting a poor one.

Every event's enter and leave conditions

A3 is written out above; here is the same enter/leave treatment for all of them (TS 38.331). Two rules make the whole table readable: every enter condition subtracts Hys from the "rising" side (so the trigger must be clearly met), and every leave condition flips the inequality and adds Hys — that opposite-sign hysteresis is the deadband that stops ping-pong. Every condition must also hold for the whole timeToTrigger before it fires.

🔑

Symbols: Ms/Mp = serving (SpCell) measurement, Mn = neighbour, Ofn/Ofp = frequency-specific offsets, Ocn/Ocp/Ocs = cell-specific offsets, Hys = hysteresis, Off = the event offset (e.g. a3-Offset), Thresh/Thresh1/Thresh2 = configured thresholds. All measurements are L3-filtered.

EventMeaningEnter (condition 1)Leave (condition 2)
A1Serving > threshold (cancel gaps)Ms − Hys > ThreshMs + Hys < Thresh
A2Serving < threshold (start looking)Ms + Hys < ThreshMs − Hys > Thresh
A3Neighbour > serving + offsetMn + Ofn + Ocn − Hys > Mp + Ofp + Ocp + OffMn + Ofn + Ocn + Hys < Mp + Ofp + Ocp + Off
A4Neighbour > threshold (load/blind)Mn + Ofn + Ocn − Hys > ThreshMn + Ofn + Ocn + Hys < Thresh
A5Serving < Thresh1 AND neighbour > Thresh2Mp + Hys < Thresh1 and Mn + Ofn + Ocn − Hys > Thresh2Mp − Hys > Thresh1 or Mn + Ofn + Ocn + Hys < Thresh2
A6Neighbour > SCell + offset (CA)Mn + Ocn − Hys > Ms + Ocs + OffMn + Ocn + Hys < Ms + Ocs + Off
B1Inter-RAT neighbour > thresholdMn + Ofn + Ocn − Hys > ThreshMn + Ofn + Ocn + Hys < Thresh
B2Serving < Thresh1 AND inter-RAT neighbour > Thresh2Mp + Hys < Thresh1 and Mn + Ofn + Ocn − Hys > Thresh2Mp − Hys > Thresh1 or Mn + Ofn + Ocn + Hys < Thresh2

Notice the pattern families: threshold events (A1, A2, A4, B1) compare one measurement to a fixed Thresh; relative events (A3, A6) compare a neighbour to the serving/SCell plus an offset; and dual-threshold events (A5, B2) require a serving-degradation and a neighbour-quality condition to hold at once — that is why they are the "only hand over when the serving cell is genuinely failing" events. A6 uses only cell-specific offsets because the neighbour and the SCell are on the same frequency.

How these map to Conditional Handover (CHO) and NTN

CHO reuses the events

Conditional Handover wraps the same events as execution conditionscondEventA3, condEventA5 (or a pair combined) inside condTriggerConfig. The network prepares the target in advance; the UE itself evaluates the condition and executes the prepared handover when it is met, so a fading link no longer has to first deliver a report.

Why

At high speed or poor coverage the classic "measure → report → command" loop can be too slow — the report or command is lost. Pre-provisioning the target and letting the UE trigger locally makes mobility far more robust.

NTN adds location/time

For NTN, where whole cells move, Rel-17 adds location- and time-based execution conditions — condEventD1 (distance to a reference point) and condEventT1 (a time window) — because RSRP-based events alone are unreliable when the cell itself is sweeping.

🎯

One sentence: every A/B event is an enter/leave pair with a hysteresis deadband and a timeToTrigger dwell; CHO takes the same conditions and lets the UE act on them autonomously, and NTN extends them with distance (D1) and time (T1) triggers.

L1 versus L3 filtering, and the quantities

Raw radio is noisy. If the UE evaluated events on instantaneous samples it would trigger on every fading dip. Two stages of filtering — one in the physical layer, one in RRC — turn jittery samples into stable trigger inputs.

L1 filtering happens inside the physical layer, invisible to RRC: the UE averages many reference-signal samples over an implementation-defined window (roughly 200 ms of measurement period) to produce one physical-layer measurement per beam/cell. L3 filtering then sits on top, in RRC, and is fully standardised by quantityConfig. It is a first-order IIR (infinite impulse response) filter applied to each new L1 measurement:

Fn = (1 − a)·Fn−1 + a·Mn,  with  a = (1/2)k/4

Here Mn is the latest L1 measurement, Fn the updated filtered value, and k the filterCoefficient from quantityConfig (configured separately per quantity). A larger k gives a smaller a, meaning heavier smoothing and slower response; a smaller k tracks the radio more closely and reacts faster. Setting k = 0 makes a = 1 and disables L3 filtering entirely (Fn = Mn). The filtered Fn is precisely what feeds Mn and Mp in the event inequalities above, so quantityConfig and the event thresholds must be tuned together — heavy filtering plus a long timeToTrigger is doubly sluggish.

Three quantities can be measured, filtered, and reported. Which one the event evaluates on is set by triggerQuantity; the others may still be carried in the report for the gNB's benefit.

QuantityDefinition (intuition)Best at revealing
RSRPReference Signal Received Power — average power of the SS/PBCH (or CSI-RS) resource elements, in dBmAbsolute coverage / signal strength; the default trigger quantity
RSRQReference Signal Received Quality = N × RSRP / RSSI — power relative to total received powerLoad and interference; distinguishes a strong-but-congested cell
SINRSignal-to-Interference-plus-Noise Ratio of the reference signal, in dBAchievable link quality / throughput potential under interference

A common pattern: trigger on rsrp (coverage is what actually drops calls) but include rsrq and sinr in reportQuantityCell so the target-cell selection can favour a less loaded or cleaner neighbour among several that all clear the RSRP bar.

Beam-level versus cell-level and consolidation

In NR a cell is not one beam — it is a set of SSB beams sweeping the sector. So there are really two measurement layers: per-beam values, and a single per-cell value derived from them. Events fire on the cell-level value; the report can also carry the strongest beams.

The UE first measures each SSB beam of a cell separately (indexed by SSB index). It then consolidates those beam measurements into one cell-level quantity using two fields carried in the measurement object / report config:

  • nrofSS-BlocksToAverage — the maximum number of the strongest beams to linear-average into the cell value.
  • absThreshSS-BlocksConsolidation — an absolute threshold; only beams above it are eligible for the average.

The rule is: take the beams above absThreshSS-BlocksConsolidation, keep at most the nrofSS-BlocksToAverage strongest of them, and linear-average those to get the cell-level result. If no beam clears the threshold, the cell value is simply the single strongest beam. This is why a cell with one excellent beam and several weak ones is represented fairly — the weak beams do not drag its cell-level RSRP down, because they never qualified for the average.

LevelWhat it isControlled byUsed for
Beam (SSB index)Per-SSB RSRP/RSRQ/SINRreportQuantityRsIndexes, maxNrofRS-IndexesToReportBeam management, target-beam selection at the new cell
CellConsolidated single value per cellnrofSS-BlocksToAverage, absThreshSS-BlocksConsolidationEvent evaluation (A1–A6, B1/B2) and handover decision

When the event finally fires, the resulting MeasurementReport carries a measResults IE containing: the measId that fired, a measResultServingMOList for the serving cell(s), and measResultNeighCells listing each reported neighbour by physCellId with its consolidated measResult and, if configured, its best beam indexes. The neighbour count is capped by maxReportCells, and after the first report the UE keeps sending every reportInterval up to reportAmount times — so the gNB receives a short stream to act on, not a single snapshot.

📎

Note: beam-level reporting is what lets the target gNB pick the right receive beam immediately after handover, avoiding a fresh beam sweep — the source cell effectively hands over a beam shortlist inside the report.

Reading a MeasurementReport in the logs

The whole framework converges on one uplink RRC message. A trace shows the event firing after its dwell, then the MeasurementReport itself — the measId that fired, the serving-cell result, and the neighbour list with consolidated per-cell values and best beams. Read it against your measConfig and the event inequality, and the mismatch is your root cause.

Representative UE log (event A3 handover) — illustrative, values vary by vendor/build:

MeasEval: measId=3 (obj=1 n78, repCfg=2 eventA3), triggerQuantity=rsrp Mn(PCI 210)=-92 dBm Mp(PCI 101)=-101 dBm a3-Offset=3 Hys=2 Ofn=0 Ocn=0 enter: Mn - Hys (-94) > Mp + Off (-98) ? YES -> timeToTrigger(320 ms) started t=320 ms condition still true -> FIRE RRC UL-DCCH / MeasurementReport measResults measId : 3 measResultServingMOList[0]: servCellId=0, PCI=101, ssb-RSRP=-101, ssb-RSRQ=-14, ssb-SINR=3 measResultNeighCells: measResultListNR [0] physCellId=210, ssb-RSRP=-92, ssb-RSRQ=-11, ssb-SINR=8 rsIndexResults: ssbIndex=2 (-92), ssbIndex=5 (-96) --> gNB acts: RRCReconfiguration with reconfigurationWithSync (handover to PCI 210)
FieldMeaningExampleCheck
measIdWhich object+reportConfig pairing fired.3Maps back to the object (n78) and event (A3); a missing expected measId means the pairing was never configured.
triggerQuantityQuantity the event was evaluated on.rsrpConfirm it matches intent; a coverage drop should trigger on RSRP, interference on RSRQ/SINR.
Mn / MpL3-filtered neighbour and serving measurements entering the inequality.-92 / -101Recompute the A3 enter condition with the configured offsets and Hys.
timeToTriggerDwell the condition must hold before firing.320 msIf the link failed before TTT elapsed, the report is late — shorten TTT or offsets.
measResultServingMOListServing-cell consolidated RSRP/RSRQ/SINR.PCI 101, -101 dBmSanity-check the serving cell was genuinely weak at the trigger instant.
measResultListNR / rsIndexResultsNeighbour cell(s) by physCellId plus best SSB beams.PCI 210, ssbIndex 2/5Best-beam list lets the target pick a receive beam; capped by maxReportCells/maxNrofRS-IndexesToReport.

LTE ↔ NR

🔀

LTE ↔ NR: the measurement framework is structurally the same — LTE (TS 36.331) also uses measurement objects, report configs, meas IDs, an L3 IIR filter with a filterCoefficient, and events with hysteresis and timeToTrigger, all delivered in measConfig and answered by MeasurementReport. The A1–A5 and B1/B2 events carry the same meanings. NR adds several things LTE lacks: A6 (SCell change for CA existed in LTE too, but NR generalises it), the whole beam layer — per-SSB-index measurement and the nrofSS-BlocksToAverage/absThreshSS-BlocksConsolidation consolidation into a cell value — the SMTC window and SSB-vs-CSI-RS measurement basis (rsType), and Rel-16/17 Conditional Handover (condTriggerConfig) plus NTN's D1/T1 location- and time-based conditions. The measured quantities also shift from LTE's RSRP/RSRQ on CRS to NR's SS-RSRP/SS-RSRQ/SS-SINR on the SSB (or CSI-RS).

Summary

Measurements and mobility are one chain, and root-causing a handover problem means finding the broken link: measConfig → measObject → measId binding → L3 filtering → event evaluation → MeasurementReport → gNB decision. If the UE never reports, check that the object exists after the delta, that a measId binds it, that a gap covers an inter-frequency/inter-RAT target, and that the SMTC aligns with the neighbour's SSB. If it reports too late or too early, look at hysteresis, timeToTrigger, the event offsets, and the L3 filterCoefficient — heavy filtering and a long TTT compound into sluggishness. If it reports the wrong target, check per-cell/per-frequency offsets and blacklists.

The mental model to keep: an object says what and when to look (frequency + SMTC), a report config says what makes it worth sending (event + hysteresis + TTT + cadence), the measId welds them and is echoed in the report, and every event is an enter/leave pair whose opposite-sign hysteresis is the ping-pong deadband. Above it all, the UE only ever proposes — the gNB disposes, by answering a MeasurementReport with an RRCReconfiguration carrying reconfigurationWithSync.

Q&A

Q&A Quick Q&A

Q. What links a measurement object and a report config?

A. A MeasIdToAddMod entry: it binds one measObjectId and one reportConfigId under a single measId. That measId is the measurement identity the UE echoes back in the report.

Q. What do timeToTrigger and hysteresis each do?

A. Both suppress ping-pong. hysteresis is a level margin added to the entering condition and removed from the leaving condition, creating a deadband around the boundary; timeToTrigger is a time margin requiring the condition to hold continuously before a report is sent, filtering out momentary spikes.

Q. A3 versus A5 — when do you use each?

A. A3 is relative (neighbour beats serving by a3-Offset) and is the default for equal-coverage intra-frequency handover. A5 is dual-absolute (serving below Thresh1 AND neighbour above Thresh2), used when handover should happen only once the serving cell is genuinely weak.

Q. What is the difference between L1 and L3 filtering?

A. L1 filtering is physical-layer sample averaging, implementation-defined and not signalled. L3 filtering is the standardised first-order IIR in RRC, controlled by quantityConfig's filterCoefficient (k); its output feeds the event conditions.

Q. How is a cell-level RSRP built from beams?

A. Take the SSB beams above absThreshSS-BlocksConsolidation, keep at most the nrofSS-BlocksToAverage strongest, and linear-average them. If none clear the threshold, use the single strongest beam.

Q. When is a measurement gap needed?

A. When the UE must retune its RF away from the serving carrier to measure a target — typically inter-frequency or inter-RAT layers. measGapConfig schedules those gaps; intra-frequency measurement usually needs none.

Where this leads next

You now have the trigger engine: measConfig configures objects and reports, the measId glues them, filtering and consolidation clean the inputs, and a MeasurementReport delivers the evidence. Next, follow that report into the decision — see Handover Overview for how the gNB acts on it, Xn Handover for the inter-gNB signalling that follows, and RRC Procedures for where measConfig sits among the other RRC messages.

Handover OverviewXn HandoverRRC Procedures