Repository navigation
cell M6: DAPS across channels — sequential uplink visits to the target's channel - #558
Conversation
…t's channel Uplink DAPS needs the target to hear the UE's source identity, which it cannot on another channel: with the source killed mid-handover, every uplink from the kill to the switch was lost. - UE (CELL_UE_DAPS_VISIT [1]): between accepting an inter-frequency handover and its switch, the UE visits the target's channel once a superframe, in its idle time after its uplink slot. It airs a copy of its uplink beacon clear of the target's active span, holds 4 ms (send_packet only queues), and retunes back. The loop runs every 30 ms in that window. - F1 v14: Uplink carries its reception instant (net_us). The CU maps a copy heard by another cell's DU to the UE's last own uplink slot (Handover.h ul_seq_for_copy) from the source's phase and the UE's position, so a visit dedups with the source's copy. - The switch clears the TSF step hold. A hold set before it counts in the source's superframes: a visit just before the switch left the uplink 40 ms off for 9 superframes, and the target's TA loop took ~12 s to settle. - DUs log each forwarded other-identity copy (cell.daps.fwd); the CU logs each mapped copy (ran.ul_copy). Rig (testrig m6_daps_xch.sh, 2 runs per case, all pass), source killed at the accept: - with visits, 0 uplinks lost before the switch (no visits: 13); - 2 lost at the switch itself; - without a fault, the visits cost the source nothing. The measgap, same-channel DAPS, handover and three-cell crossing runs pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR Summary by QodoEnable sequential uplink DAPS across channels
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
…target, late/overrun visits skipped, every copy mapped, short step hold - A visit instant is taken only if the whole send (queued through the 4 ms hold, with guards) is clear of the target's active span, across the superframe boundary. - A late wake skips the visit. An outbound retune that overran the instant skips the send and retunes back (cell.ue.daps_visit_skip). Both retunes feed gap_retune_us. - The CU maps every forwarded uplink, the serving DU's own copies included, from the identity's last-known slot (kept 30 s from the latest report naming it). Copies of one uplink share a seq even next to a network-superframe boundary, and a late copy after the source released the UE still maps. Selftest for the boundary case. - A visit holds TSF steps only through its own superframe (not four, which froze them for the whole window); held steps are counted. Rig (m6_daps_xch.sh 2 runs per case, all pass): with visits, 0 uplinks lost before the switch (15 without), no visit skipped. Regression: same-channel DAPS, the handovers, the two-cell crossing with a source failure, measgap and the three-cell crossing with one all pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review addressed in the latest commit:
Rig on this code:
|
|
Applying skip-qodo-gate: Qodo posted its review as a PR comment (5 findings), not a review with threads, so the gate sees no review. All 5 are fixed and re-verified on the rig (m6_daps_xch.sh 2 runs per case, plus same-channel DAPS, handovers, crossings with source failures, measgap); its high-level note recommends keeping the network-time mapping. All 28 builds pass. |
…it with its DL PDUs (#559) Part of #260 (epic #254). Follows #558 (sequential uplink visits): the downlink side across channels. ## Why Before the switch, the UE takes DL only from its serving cell. With the source dead, it had no DL from the kill to the switch (1.4–1.9 s), although the target held every PDU since the command (DAPS DL). ## What (`examples/cell`) - **The answer:** when the target DU hears a visit copy from a UE it staged for an inter-frequency arrival, before the switch, it answers at once with the queued DL PDUs no answer has offered yet (up to 8). - The frame (`build_visit_dl_frame`, `cell.h`) is broadcast-addressed: no ACK, no CW escalation. - It is tagged with the target cell's key, and the body names the UE's target identity. - **The UE:** while still holding the target's channel, it takes the PDUs into its DL stream (`parse_visit_dl_frame`; `cell.ue.visit_dl`). - **After the switch:** the queue keeps everything (nothing ACKs an answer), and the target sends not-yet-offered PDUs first, then the offered ones. Without that ordering, the newest PDU waited behind ~5 frames the UE already had (0.53–0.63 s). - **Sending:** answers go out from a dedicated thread. Sending from inside the RX callback froze the DU on the rig (the RX loop recycles the TX pages). - **Only across channels:** the target's neighbour list must put the source on another channel. On one channel the target hears every uplink, and an answer would land in the source cell's schedule. - **Selftests:** the visit DL frame (broadcast RA, target identity, key, not a unicast DL frame, PDU sn under the tag). ctest 95/95. ## Rig (testrig `scripts/m6_daps_xch.sh`, 2 runs per case, all pass) | longest DL silence at the UE | runs | |---|---| | source killed at the accept, visits answered | 205, 205 ms | | source killed, no visits | 1395, 1498 ms | | no fault, visits answered | 308, 205 ms | - **Answers:** 19–21 per handover; DL DAPS lost no PDU. - **Uplink:** unchanged (0 lost before the switch with visits). - **What remains:** the switch's own pause (~2 superframes). That is next. - **Regression:** same-channel DL DAPS (no answers sent, silence unchanged), the three-cell crossing with a source failure, plain and fault handovers. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Part of #260 (epic #254). Closes "DAPS across channels" for the uplink. The #557 crossing measured the gap: 12–14 uplinks lost from a source kill to the switch, because the target cannot hear the source identity on another channel.
What
examples/cell,CELL_UE_DAPS_VISIT[1]):send_packetonly queues; no TX report on Kestrel), and retunes back before its serving beacon;Handover.hul_seq_for_copy): the DU forwards the copy with its reception instant (Uplink.net_us). The CU maps a copy heard by another cell's DU to the UE's last own uplink slot, from the source phase and the UE's position in the reports, so a visit dedups with the source's copy. The UE needs no global clock.cell.daps.fwd(DU) andran.ul_copy(CU, raw vs mapped seq).Review fixes (later commit): the whole send clear of the target's span; late or overrun visits skipped and logged; the CU maps every copy (incl. the serving DU's) from the identity's last-known slot; a visit holds steps only through its own superframe.
Rig (testrig
scripts/m6_daps_xch.sh, 2 runs per case, all pass)🤖 Generated with Claude Code