Repository navigation
ran-controller: observe-only A3 (CHO) handover evaluation - #528
Conversation
…s' filtered reports For every UE, the CU runs src/cell/Handover.h A3Trigger each plan round: the serving DU's own filtered measurement (F1 v2 meas) against every other DU's heard one. An absent or stale neighbour is no evidence. It logs ran.ho_candidate when the condition has held for the time-to-trigger, and ran.ho_clear when it stops. Nothing is pushed to the DUs. RAN_HO, RAN_A3_METRIC (rssi|snr), RAN_A3_OFFSET_DB, RAN_A3_HYST_DB, RAN_A3_TTT_MS. The effective time-to-trigger rounds up to RAN_PLAN_MS. Rig (testrig m5_ran_coord.sh, two cells, stationary UEs, 90 s): - the default 3 dB offset gives 0 candidates; - at -2 dB, exactly one: cell 1's UE toward cell 2 (serving -54.5, target -54.0 dBm, held 1.0 s), with no clear or repeat; cell 2's UE (about 15 dB stronger at its own DU) none. The M5 verdict passes in both runs. Refs #260 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR Summary by QodoEvaluate A3 handover candidates without initiating handovers
AI Description
Diagram
High-Level Assessment
Files changed (1)
|
Code Review by Qodo
1.
|
- Only the configured RAN_DUS: a report from any other DU id creates no state, and the A3 loop walks only configured DUs. - F1 v3: every measurement carries its sample age at report time (MeasTable -> fill_report_measurements). A value counts only until its age plus the report's age passes RAN_MEAS_STALE_MS. - A UE with no valid serving measurement in a round has its A3 timers dropped and any logged candidate cleared (why: no_serving), so a gap cannot carry an old time-to-trigger. - A direct switch between targets logs the clear (why: retarget) before the new candidate. - selftests: ages through the producer and the wire, v3 framing. Rig (m5_ran_coord.sh, 90 s each): - default: 0 candidates; - offset -2 dB: one candidate (cell 1's UE toward cell 2), then a no_serving clear when the UEs stopped. The M5 verdict passes in both. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Qodo review addressed in 66bbbf0:
Rig: default thresholds gave 0 candidates. At -2 dB there was one candidate, then a no_serving clear at the end. The M5 verdict passes in both runs. |
|
Qodo findings addressed (see the reply above) |
…itional fallback (#530) Part of M6 (#260), epic #254. Step 2b: handover execution on top of #528 (observe-only A3) and #529 (command and IE). Full write-up in `docs/cell-m6.md`. - **CU (`RAN_HO_EXECUTE=1`):** - an A3 candidate becomes a `HoCommand`: the target's free id and position, the switch instant and the TSF delta; - **two-phase:** sent to the target first, to the source on the target's ack, and a cancel goes to the target if the source refuses; - `ran.ho_done` when the target reports the UE. - **Target DU:** pre-admits the UE under the commanded id and publishes its grant for the switch instant. - **Source DU:** airs the tagged IE until the switch, then releases the UE only if it stopped uplinking there; otherwise it keeps it. - **UE:** at the instant it shifts its clock (`TsfSync::ShiftRemote`, new, selftested), takes its target identity and runs the target's buffered map. **Conditional:** with no target grant within 10 superframes it returns to the source. - **Fix:** a DU with no UE now paces its main loop. Its superframe count, and so its reported network superframe, used to run away. Rig (two PTP cells; the crossing is forced with `RAN_A3_OFFSET_DB=-2`, as the UEs are stationary): | | normal | target DU killed right after staging | |---|---|---| | switch | source sf 143 → target sf 137 (= the target's effective sf) | source sf 172 | | target hears the UE | in slot from the first superframe after: 1115/1117 | — | | outcome | committed; source released at +21; `ran.ho_done` +5 | UE fell back at +10; source kept it, 1092/1092 in slot | ctest 92/92. Not yet: DAPS (duplicated uplink plus `SeqDedup`), an emulated moving crossing, and different-channel handover. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Part of M6 (#260), epic #254. Step 1 of the integration: the CU evaluates handovers but does not act on them.
ran-controllerruns the mergedA3Triggerfor every UE. It compares the serving DU's filtered measurement (F1 v2meas) against every other DU's filteredheardmeasurement of that UE. An absent or stale neighbour counts as no evidence.ran.ho_candidatewhen the A3 condition has held for the time-to-trigger,ran.ho_clearwhen it stops. Nothing is pushed to the DUs.RAN_HO,RAN_A3_METRIC(rssi|snr),RAN_A3_OFFSET_DB,RAN_A3_HYST_DB,RAN_A3_TTT_MS. The effective time-to-trigger rounds up toRAN_PLAN_MS.Rig (testrig
m5_ran_coord.sh, two cells, stationary UEs, 90 s each):At −2 dB, cell 2's UE, which is about 15 dB stronger at its own DU, produced no candidate. The M5 coordination verdict passes in both runs.
Next steps (separate PRs): grant migration over F1, then same-channel DAPS with dedup.
🤖 Generated with Claude Code