Skip to main content

Hand Over Between Two Cells

This tutorial builds the smallest network that moves a UE: two cells on two radios, frame aligned by GPS, declared as neighbors of each other. It continues from Attach a Phone to a Live Cell.

Before You Begin​

  • The phone and its Subscriber from the previous tutorial. If you cleaned up, apply subscriber-1.yaml again; the cell is declared below.
  • Two USRP B210s, each on its own rf-ready node, each with a GPSDO and a GPS antenna that sees the sky. Handover needs frame-aligned cells, and on USRPs that means GPS lock on both radios (How Mobility Reaches the CU-CP).

1. Declare Two GPS-Locked Cells​

The same carrier on both radios, clockSource: gpsdo on both. Save this as two-cells.yaml with your serials and node names:

apiVersion: racora.io/v1alpha1
kind: NRCell
metadata:
name: nr-n78-b210
namespace: racora-system
spec:
band: 78
dlArfcn: 632628
channelBandwidthMHz: 10
commonScs: 30
plmn: "90170"
tac: 7
pci: 1
radioBackend:
ruType: uhd
uhd:
deviceArgs: "type=b200,serial=<serial A>,num_recv_frames=64,num_send_frames=64"
clockSource: gpsdo
otwFormat: sc12
srate: 23.04
txGain: 80
rxGain: 40
duAssignment:
nodeName: <node A>
---
apiVersion: racora.io/v1alpha1
kind: NRCell
metadata:
name: nr-n78-b210-node1
namespace: racora-system
spec:
band: 78
dlArfcn: 632628
channelBandwidthMHz: 10
commonScs: 30
plmn: "90170"
tac: 7
pci: 2
radioBackend:
ruType: uhd
uhd:
deviceArgs: "type=b200,serial=<serial B>,num_recv_frames=64,num_send_frames=64"
clockSource: gpsdo
otwFormat: sc12
srate: 23.04
txGain: 80
rxGain: 40
duAssignment:
nodeName: <node B>
kubectl apply -f two-cells.yaml
kubectl get pods -n distributed-unit -w

Each DU restarts, logging Could not lock reference GPS time source, until its antenna sees enough satellites; then both stay Running. Checking lock directly is Connect a Radio. Both cells declare distinct PCIs, so the Intelligence Plane has nothing to retune while the phone is moving; a retune would lock the cell and roll its DU.

2. Make Them Neighbors​

Relations are directional, so declare both:

kubectl patch nrcell nr-n78-b210 -n racora-system --type=merge \
-p '{"spec":{"neighbors":[{"nrCellRef":"nr-n78-b210-node1","reportConfigs":[2]}]}}'
kubectl patch nrcell nr-n78-b210-node1 -n racora-system --type=merge \
-p '{"spec":{"neighbors":[{"nrCellRef":"nr-n78-b210","reportConfigs":[2]}]}}'

The controller applies the relations to the running CU-CP without restarting anything. With the built-in report configuration, a UE hands over when the neighbor measures 3 dB stronger than its serving cell for 100 ms. CU-IP's automatic neighbor relations would also find this pair from what UEs measure; declaring them makes the tutorial deterministic.

3. Move the Phone​

Bring the phone up near the radio of nr-n78-b210 so it attaches there, then carry it toward the radio of nr-n78-b210-node1. Watch the CU-CP narrate it:

kubectl exec -n centralized-unit deploy/cu-cp -c cu-cp -- tail -F /tmp/cu_cp.log | grep -i handover

A handover reads, in order: Trigger intra-CU (inter-DU) handover from source_du=A to target_du=B, then "Intra CU Handover Routine" finished successfully, then "Intra CU Handover Target Routine" finished successfully. The RAN Mobility dashboard in Grafana counts them (Read Logs and Traces).

4. Lock a Cell​

With the phone now on nr-n78-b210-node1, lock the other cell:

kubectl patch nrcell nr-n78-b210 -n racora-system --type=merge -p '{"spec":{"adminState":"Locked"}}'

A lock is a graceful stop: the CU-CP releases any UE on the cell and the cell leaves the air. Carry the phone back toward its radio and it stays on nr-n78-b210-node1, because there is no cell to hand over to. A handover commanded toward a locked cell through the runtime commands is refused, with Ignoring Handover Request. Cause: Target cell with pci=N is administratively deactivated in the CU-CP log (Runtime Commands). Unlock it again:

kubectl patch nrcell nr-n78-b210 -n racora-system --type=merge -p '{"spec":{"adminState":"Unlocked"}}'

5. Clean Up​

kubectl delete -f two-cells.yaml
kubectl delete -f subscriber-1.yaml

What's Next​