Connect a Radio
The radio behind a cell is declared on the NRCell: the backend and its fields, the node
and the radio on it, the clock source. What each backend makes Racora generate is
How Radios Are Driven; every field, with its default and range,
is in the NRCell API.
Declare the Backend
Set spec.radioBackend.ruType and the matching block; only that block is read.
A USRP B210 over UHD:
radioBackend:
ruType: uhd
uhd:
deviceArgs: "type=b200,serial=35912B3,num_recv_frames=64,num_send_frames=64"
srate: 23.04 # MHz; carries a 10 or 20 MHz cell
otwFormat: sc12 # halves USB bandwidth on a B210
txGain: 80 # dB
rxGain: 40 # dB
clockSource: gpsdo # internal (default) | external | gpsdo
deviceArgs names the radio (the serial matters on a node with several) and deepens the
USB transfer queues a B210 needs at this rate; ruType: uhd alone is a valid declaration
for a lone B210 on a lone radio node.
A virtual radio with a UE simulator you deploy:
radioBackend:
ruType: zmq
zmq:
txPort: "tcp://0.0.0.0:2000" # where the DU transmits; a Service exposes 2000
rxPort: "tcp://<ue-simulator>.user-equipment.svc.cluster.local:2001" # where it receives
Point rxPort at your simulator's Service; the default host is in the user-equipment
namespace (the controller's ueNamespace value).
No radio at all, what the example cell uses:
radioBackend:
ruType: dummy
An O-RU over Open Fronthaul is the ofh backend, set up in
An O-RU over Open Fronthaul.
Pick the Node and the Radio
Three things decide placement, all on the NRCell:
ruType: uhdadds theracora.io/rf-ready=truenode selector,ruType: ofhtheracora.io/fronthaul-ready=trueone; virtual cells get none. What a node must be to carry that label is the Requirements page and the node contract; how a node gets there is Add a Radio Node.duAssignment.nodeNameadds akubernetes.io/hostnameselector for that node, merged withrf-ready, so the scheduler still checks that the node is RF-ready and has a free radio.radioBackend.uhd.deviceArgspicks the radio on the node:type=b200takes whichever USRP the device plugin attached;serial=…makes it a specific one, which matters as soon as a node holds two.
Put the radio on USB 3; at 23.04 MS/s a B210 on USB 2 has not got the bandwidth. After a DU crash the radio can be left with a stale USB claim: delete the DU pod to clear it.
Set the Clock Source and Check GPS Lock
For a single cell leave clockSource: internal (the default). For cells between which
UEs hand over, set clockSource: gpsdo on every cell (a GPSDO on every radio; external
works the same way with a shared reference) and wait for GPS lock before you expect
mobility.
With clockSource: gpsdo and no lock, the DU does not start: it logs
Could not lock reference GPS time source and restarts until the antenna sees enough
satellites. To check lock, read the radio's GPS sensors on the radio node itself (the
provisioner installs UHD there) while the DU is waiting between restarts and so does not
hold the radio:
uhd_usrp_probe --args "type=b200,serial=<serial>" --sensor /mboards/0/sensors/gps_locked
uhd_usrp_probe --args "type=b200,serial=<serial>" --sensor /mboards/0/sensors/gps_servo
gps_locked reads true once the GPSDO has lock; gps_servo is the GPSDO's own status
line. For a single cell, switching the cell to internal brings it up without GPS.
An O-RU over Open Fronthaul
An O-RAN split 7.2 O-RU hangs off a fronthaul port of a racora.io/fronthaul-ready node
(Add a Radio Node); the cell names that node in duAssignment.nodeName. The
ofh block carries what is particular to this radio, and a radio profile what its model
fixes. A 4T2R O-RU on the n78 carrier at 3750 MHz:
spec:
dlArfcn: 650000 # 3750 MHz, used for both directions (TDD)
band: 78
channelBandwidthMHz: 10
commonScs: 30
duAssignment:
nodeName: <node cabled to the O-RU>
radioBackend:
ruType: ofh
ofh:
profileRef: oru-profile
ruMacAddr: "<the O-RU's fronthaul MAC>"
vlanTagCp: 5 # one VLAN for C- and U-plane
vlanTagUp: 5
dlPortId: [0, 1, 2, 3] # one eAxC id per antenna: 4 DL
ulPortId: [0, 1] # 2 UL
prachPortId: [4, 5]
txGain: 20 # dB, the O-RU's carrier gain
The profile is a ConfigMap next to the cell, its profile.yaml the same keys; take the
values from the radio's vendor documentation and its M-plane delay profile:
apiVersion: v1
kind: ConfigMap
metadata:
name: oru-profile
namespace: racora-system
data:
profile.yaml: |
t1aMaxCpDl: <µs> # the DU's transmission and reception windows
t1aMinCpDl: <µs>
ta4Max: <µs>
ta4Min: <µs>
isPrachCpEnabled: true
enableDlStaticComprHdr: false # what the radio expects on the wire
enableUlStaticComprHdr: false
iqScaling: <scaling>
mplane:
compressionType: STATIC
tddCarrierBinding: true
The node's fronthaul interface, the DU's MAC address and the F1-U address are filled in on
the node when the DU starts; set ofh.networkInterface only to use another port than the
one node provisioning selected.
Let the DU pod hold the M-plane session. Put the O-RU's NETCONF account in a Secret in
the DU namespace and name its endpoint on the cell; the pod's mplane sidecar then
provisions the O-RU and starts the DU only once its carriers are active:
kubectl -n distributed-unit create secret generic oru-account \
--from-literal=username=<account> --from-file=password=<file holding the password>
ofh:
mplane:
netconfHost: <the O-RU's management address> # or callHome: true
credentialsSecret: oru-account
kubectl -n racora-system get nrcell <cell> then shows the turn-up in RadioReady and
DUReady; the sidecar's log (kubectl -n distributed-unit logs <du pod> -c mplane) is the
M-plane conversation in short.
To take the cell down, delete it: the pod goes, and with it the DU and then the M-plane
session. To restart it, delete its DU pod (kubectl -n distributed-unit delete pod -l app=du-<cell>): the replacement provisions the O-RU again before its DU starts, which is
also what the controller does by itself when the DU container restarts. Keep one M-plane
client per O-RU: while the cell holds the session, run no other client against the O-RU.
Or provision the O-RU yourself, before the cell exists. Without an endpoint the O-RU
must already carry the cell's configuration, with its carriers active and its sync state
LOCKED, when the DU starts. Render its provisioning file from the manifests with the
controller's own code:
{ cat oru-profile.yaml; echo ---; cat cell.yaml; } | \
kubectl -n racora-system exec -i deploy/racora-controller -- \
python -m racora_controller.ofh_config > ru.yml
The --- keeps the two manifests separate YAML documents; concatenated without it, a file
that does not start with its own --- merges into the one before it.
Replace @FRONTHAUL_MAC@ in ru.yml with the MAC address of the node's fronthaul port,
provision the O-RU with an M-plane client, for example the OCUDU O1 adapter
(--profile ru --ru_supervise --ru_provision_config ru.yml), and apply the cell once the
carriers are READY. The controller writes the same file into the cell's ConfigMap
(ru.yml in nrcell-du-<cell>-config). Take it down in the reverse order: delete the
cell, then end the M-plane session; to restart the cell, take it down and bring it up again.
The Node Must Reach the O-RU
The DU of a cell with an M-plane address requests one healthy O-RU
(racora.io/oru) from the node, as the fronthaul agent advertises it, so it only
schedules where the O-RU answers. Before applying the cell, or when its pod stays
Pending, look at what the node sees:
kubectl describe node <node> | grep -A1 'racora.io/oru' # Capacity: the O-RUs named; Allocatable: the ones answering
kubectl -n racora-system logs ds/fronthaul-agent # the reason, one line per change
The agent also hands the O-RU its address: a request from the cell's ruMacAddr gets
netconfHost, so that address must be an IPv4 address in the fronthaul port's subnet,
and nothing else on the segment may serve DHCP. Allocatable 0 with the O-RU powered
and no DHCP line in the agent's log means it never asked, or asked from another MAC
(an UnknownORU Event on the node names it); netconf-closed after a DHCP line is an
O-RU still booting. The agent, the condition RadioReady=False/Unreachable and what the
controller does when a running cell loses its O-RU are on
The Fronthaul Agent.
Tune the Physical Layer
Beyond the radio block, spec.ssb, pdcch, pdsch, pusch and prach are passed through
verbatim into the DU's cell configuration, so any physical-layer parameter the shipped gNB
accepts can be set per cell. Their meaning is the stack's, documented at
docs.ocudu.org; the API server does not validate them.