Repository navigation
cell M6: DAPS across channels, downlink — the target answers each visit with its DL PDUs - #559
Conversation
…it with its DL PDUs Before the switch the UE takes DL only from its serving cell, so a dead source left it without DL from the kill to the switch, though the target held every PDU. - The target DU answers a visit copy from a UE staged for an inter-frequency arrival, before the switch. The answer carries the queued DL PDUs no answer has offered yet (up to 8), in a broadcast-addressed frame (build_visit_dl_frame: no ACK, no CW escalation) tagged with the target cell's key. - The UE, still on the target's channel, takes the PDUs into its stream (cell.ue.visit_dl). The queue keeps everything, and after the switch the not-yet-offered PDUs go first. - Answers are sent by their own thread; sending from the RX callback froze the DU on the rig. - Only across channels: on one channel an answer would land in the source cell's schedule. Rig (testrig m6_daps_xch.sh, 2 runs per case, all pass): longest DL silence with the source killed 205 ms with answers, 1395-1498 ms without. Uplink results unchanged. Same-channel DL DAPS, the three-cell crossing with a failure and the handovers pass. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR Summary by QodoDeliver target downlink PDUs during inter-frequency DAPS visits
AI Description
Diagram
High-Level Assessment
Files changed (4)
|
Code Review by Qodo
1.
|
…, a deadline per answer, lost answers inside the reorder window - An answer that is not queued (queue full), expires or fails gives its PDUs back: the offered mark rolls back unless a later answer moved it. - Each queued answer carries a 3 ms deadline (the UE's hold on the target's channel); a stale one is dropped, not aired after the UE left. - After the switch, pick_dl_pdus (cell.h) sends not-yet-offered PDUs first, but an offered PDU goes before it would fall 24 sns behind the fresh ones. A lost answer's PDU then reaches the UE inside its 32-sn reorder window. Selftests cover the orderings and a lost answer with a 60-PDU backlog. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
…rigger stated (A3 defaults, static UEs) Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Review addressed:
Rig on this code: |
|
Applying skip-qodo-gate: Qodo posted its review as a PR comment (5 findings, two of them requirement gaps), not a review with threads, so the gate sees no review. All 5 are addressed and re-verified on the rig (m6_daps_xch.sh 4 runs per case, same-channel m6_daps_dl). All 28 builds pass (macOS gcc was cancelled by the runner once; it passed on re-run). |
Part of #260 (M6), epic #254. Follows #559. The DL gap across an inter-frequency DAPS switch was ~2 superframes (225 ms) after #559's review fixes; the first uplink after the switch fired at the source's phase. This PR removes the switch's own share of the interruption. **UE (`examples/cell`)** - The loop's pass after a switch is scheduled for the target's first superframe (+1 ms), not a full period later (the uplink beacon used to restart past that superframe's UL slot). - The TSF is placed for the target's UL slot right after the switch's retune (`cell.ue.switch_place`). Kestrel's first beacon after a TSF step fires at the old phase (predicted 57.2 ms, observed 57.3); if that stale TBTT would land in the target's active span, the restart is held past it. **DU (`examples/cell`)** - `pick_dl_pdus`: a DL frame leads with not-yet-offered PDUs; an offered PDU goes only just before the fresh one that would push it out of the UE's reorder window while still recoverable. #559's rule (offered first once 24 sns behind) let a large offered backlog lead after the switch. Selftests: plain order, offered/fresh split, the lost-answer backlog, a 40-PDU backlog with a fresh PDU in the first frame. **CU (`tools/ran-controller`)** - The target identity's UL slot is seeded from the handover command, so the first uplinks after the switch map to the right network superframe. - A UE's A3 is frozen while its handover is in flight: with a slow conclusion the DUs' stale evidence cleared the candidate and raised a second one for the same move (seen on the rig in `m6_three_ho`; no second command). **Rig** (testrig `m6_daps_xch.sh`, 4 runs per case, cells on 149/157): | case | UL lost before the switch | DL gap across the switch | |---|---|---| | source dies, visits | 0 (all 4) | 122–123 ms | | source dies, no visits | 10–16 | 1498–2010 ms | | source alive, visits | 0 (all 4) | 123 ms | Re-run on the final build (2 per case): 122–123 ms in 3 of 4 visit runs, 225 in one, where the first frame after the switch had no new PDU yet (100 ms PDU cadence); details in `docs/cell-m6.md`. Regression on the rig: measgap, three_ho (3 runs), crossing3, handover, daps, daps_dl, crossing all pass. 🤖 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). 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)build_visit_dl_frame,cell.h) is broadcast-addressed: no ACK, no CW escalation.parse_visit_dl_frame;cell.ue.visit_dl).Rig (testrig
scripts/m6_daps_xch.sh, 2 runs per case, all pass)🤖 Generated with Claude Code