Skip to main content

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

Pick the Node and the Radio​

Three things decide placement, all on the NRCell:

  1. ruType: uhd adds the racora.io/rf-ready=true node selector; 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.
  2. duAssignment.nodeName adds a kubernetes.io/hostname selector for that node, merged with rf-ready, so the scheduler still checks that the node is RF-ready and has a free radio.
  3. radioBackend.uhd.deviceArgs picks the radio on the node: type=b200 takes 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.

Tune the Physical Layer​

Beyond the radio block, spec.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.