Repository navigation
cell M6: DAPS — same-channel uplink duplication + CU dedup - #531
Conversation
…eduplicated at the CU The common sequence: a UE uplink is its hardware beacon, stamped with the UE's egress TSF, so seq = round(egress / P) (ul_seq_from_egress) is the same in every DU's copy. No UE change. DU: every own-UE uplink is forwarded to the CU (F1 v5 Type::Uplink) whenever F1 is on. With CELL_DAPS=1 (default), during a handover it also forwards the UE's other identity: the target forwards the source id staged with the command, until the switch + the release window; the source forwards the target id after the switch. CU: src/cell/Handover.h UlStreams delivers each uplink once per UE stream over SeqDedup. The handover command aliases the target identity onto the source's stream, so both identities dedup together and the stream runs on across the switch. It logs ran.ul_gap for every delivery gap and ran.ul summaries. The cell_m6 ctest covers a simulated crossing (two 20 %-lossy paths, the identity flip, the source dying), the alias, the common seq, and the F1 Uplink round trip. Rig (testrig scripts/m6_daps.sh, 3 runs per mode, the source DU killed the moment its UE accepts the handover, about 2 s before the switch): uplinks missing from the crossing UE's stream are 0/0/1 (min/median/max) with DAPS and 13/14/14 without. ctest 92/92. docs/cell-m6.md. Refs #260 Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
PR Summary by QodoAdd same-channel DAPS uplink forwarding and CU deduplication
AI Description
Diagram
High-Level Assessment
Files changed (6)
|
Code Review by Qodo
1.
|
- The common seq is the reception instant on the shared PTP timebase (ul_seq_from_network_time), not the UE beacon's untagged timestamp: a forged or replayed frame cannot move the CU's dedup window. - A copy heard under the other identity is forwarded only if its tag verifies. - F1 socket non-blocking (forwarding runs on the radio RX path); send drops counted (cell.f1.summary). - CU: gaps are confirmed after a 32-seq reorder window, one event per run, and a late copy recovers silently. Only identities a configured DU reports, or an active handover aliased, create state. Idle streams retire after 60 s. Provisional aliases are dropped on failure, timeout or cancel, and source identities after retirement. The summary resolves stats by stream id. - UlStreams: stats_of_stream, stream_id, forget_stream; forget drops a stream with its last identity. Selftests extended. Rig (scripts/m6_daps.sh, 3 runs each): missing 0/0/0 with DAPS vs 14/15/18 without; a single grouped no-DAPS gap per run. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
|
Qodo review addressed in 4ff81f3:
The 'persistent UE stream ID' item is an alternative-approach note: the alias created at command time already gives one stream across both identities. Rig re-run on the final code: missing 0/0/0 with DAPS vs 14/15/18 without (3 runs each). ctest 92/92. |
|
All 6 Qodo findings resolved. The remaining item, 'Use a persistent UE stream ID in F1', is Qodo's alternative-approach note, not a finding: the command-time alias already gives the UE one stream across its identities. Merging under skip-qodo-gate. |
Part of M6 (#260), epic #254: **different-channel handover**, on top of #530 and #531. - **Command and IE:** `HoCommand.target_channel` comes from the target DU's report, and `HoIe.channel` travels with it (IE v2, F1 v6). - **UE:** at the switch it shifts its clock by the TSF delta (no target beacon needed first), takes the target identity, and **stops its uplink beacon → retunes (`FastRetune`) → restarts it on the new channel**. It logs the retune time and the first serving beacon after it. - **Deadline to return:** with no target grant within `CELL_HO_FALLBACK_SF`, the UE retunes back to the source, which still holds it. This bounds the deaf time. - **Trigger:** with one radio per DU, no DU hears a UE across channels, so the A3 has no neighbour value to compare. **UE measurement gaps are the next step.** Until then, `RAN_HO_FORCE=SRC:UE:TGT@SECONDS` forces one handover through the normal execution path. Rig (testrig `scripts/m6_handover.sh --channel2 157`: ch149 → ch157, 5 normal + 3 runs with the target killed after staging; all pass): | KPI | min | median | max | |---|---|---|---| | `FastRetune` (8852CE, 11 retunes) | 23.5 ms | 23.6 ms | 23.7 ms | | first target beacon after the retune | 37 ms | 52 ms | 101 ms | | interruption (sf, first in-slot uplink at the target) | 2 | 2 | 3 | | target crash: fallback / source uplink missed (sf) | 10 / 10 | 10 / 10 | 10 / 10 | The switch lands on the target's effective superframe in every run: clock continuity holds across the channel change. ctest 92/92. Details in `docs/cell-m6.md`. 🤖 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. On top of #530 (handover execution).
seq = round(egress / P)is identical in every DU's copy. The UE needs no change.Uplink). WithCELL_DAPS=1(the default), during a handover it also forwards the UE's other identity: the target forwards the source identity before the switch, and the source forwards the target identity after.UlStreamsdelivers each uplink once per UE stream overSeqDedup. The handover command aliases the target identity onto the source's stream, so the stream runs on across the switch. It logsran.ul_gapfor every delivery gap andran.ulsummaries.cell_m6ctest: a simulated crossing (two lossy paths, the identity flip, the source dying), the alias, the common seq, and the F1 round trip.Rig (testrig
scripts/m6_daps.sh, 3 runs per mode; the source DU is killed the moment its UE accepts the handover, about 2 s before the switch):ctest 92/92. Still open: downlink duplication, the different-channel (sequential) DAPS variant, and security pre-staging.
🤖 Generated with Claude Code