# Ocudu

> Monitor, operate, and query a live Ocudu 5G gNB (base station) via its WebSocket metrics interface and YAML configuration files. Use when the user asks about connected UEs, signal quality, CQI, MCS, throughput, bitrate, HARQ failures, scheduler performance, cell metrics, AMF connectivity, NGAP status, RRC connections, RLC statistics, executor load, CPU usage, memory, power consumption, Open Fronthaul timing, gNB configuration, cell parameters, logging, PCAPs, slicing, or any runtime state of the Ocudu RAN. Also use when the user asks to start, stop, or restart the gNB or UE, check if components are running, run ping or connectivity tests, or troubleshoot the 5G network stack.

- Skill: `ngkore/ocudu` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add ngkore/ocudu`
- Raw SKILL.md: https://api.skillmd.com/api/skills/ngkore/ocudu/raw
- Safety review: pending (external: skill-scanner PASS, skillspector CAUTION)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: ngkore (https://skillmd.com/u/ngkore)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/ngkore/ocudu

---


# Ocudu RAN Monitor and Operate

Monitor, operate, and query a running Ocudu 5G RAN stack. Collect real-time JSON metrics over WebSocket, read YAML configuration files, start/stop components, and run connectivity tests.

## Prerequisites

1. **Ocudu gNB must be running** with remote control enabled in its config:
   ```yaml
   metrics:
     enable_json: true
   remote_control:
     enabled: true
     bind_addr: 0.0.0.0   # or 127.0.0.1 for localhost-only
     port: 8001
   ```
2. **Python 3.8+** with the `websocket-client` package:
   ```bash
   pip install websocket-client
   ```

## Environment

This is how the 5G stack is deployed:

| Component | Runs on | Details |
|-----------|---------|--------|
| **Ocudu gNB** | Host (bare metal) | Binary at `~/ocudu/build/apps/gnb/gnb`, config at `~/gnb_zmq.yaml` |
| **srsUE** | Host (bare metal) | Binary at `~/srsRAN_4G/build/srsue/src/srsue`, config at `~/ue_zmq.conf` |
| **Open5GS 5GC** | Docker | Container name: `open5gs_5gc`, manages AMF/SMF/UPF |
| **RF transport** | ZMQ | Simulated radio link between gNB and UE via ZMQ sockets |

### Checking if components are running

```bash
# Check gNB (runs on host, not Docker)
ps aux | grep gnb | grep -v grep

# Check UE (runs on host, not Docker)
ps aux | grep srsue | grep -v grep

# Check 5G Core (runs on Docker)
docker ps | grep open5gs

# Check gNB logs
tail -30 /tmp/gnb.log
```

**Important**: The gNB and UE are **NOT** Docker containers. Do not use `docker` commands to manage them. They are native host processes.

### Starting components

Start in this order: 5GC first → gNB → UE.

```bash
# 1. Start 5G Core (if not running)
cd ~/ocudu/docker && docker compose up -d 5gc

# 2. Start gNB (requires sudo, runs in foreground)
cd ~/ocudu/build/apps/gnb && sudo ./gnb -c ~/gnb_zmq.yaml

# 3. Start UE (requires sudo, runs in foreground, in a separate terminal)
cd ~/srsRAN_4G/build/srsue/src && sudo ./srsue ~/ue_zmq.conf
```

**Note**: Both gNB and UE run in the **foreground** as interactive processes. They occupy their terminal session.

### Stopping components

Stop in reverse order: UE first → gNB → 5GC.

**Method 1 — Interactive (if you have terminal access)**:
Type `q` and press Enter in the running gNB or UE terminal session to gracefully shut down.

**Method 2 — Programmatic (recommended for AI agent)**:
```bash
# Stop gNB via WebSocket quit command (clean shutdown via SIGTERM)
python3 -c "import websocket, json; ws = websocket.create_connection('ws://127.0.0.1:8001'); ws.send(json.dumps({'cmd': 'quit'})); ws.close()"

# Stop UE via signal (graceful shutdown)
sudo kill -SIGTERM $(pgrep -f srsue)

# Stop 5G Core
cd ~/ocudu/docker && docker compose down
```

**Warning**: Always confirm with the user before stopping any component. Stopping the gNB disconnects all UEs.

### Connectivity testing

The UE's network interface runs inside a **network namespace** called `ue1`. To send traffic through the UE:

```bash
# Ping the UPF gateway (tests UE → gNB → 5GC path)
sudo ip netns exec ue1 ping 10.45.1.1

# Ping an external host (requires NAT on 5GC)
sudo ip netns exec ue1 ping 8.8.8.8

# Check UE's IP address and routes
sudo ip netns exec ue1 ip addr show
sudo ip netns exec ue1 ip route show
```

**Important**: Do NOT use regular `ping` from the host — it bypasses the RAN entirely. Always use `sudo ip netns exec ue1` to route traffic through the UE's PDU session.

## Step 1 — Get Connection Details

Ask the user for:
- **WebSocket URL** — host and port of the gNB remote control interface (default: `127.0.0.1:8001`)
- **Config file path** — path to the gNB YAML config file (optional, for config queries)

## Step 2 — Collect Metrics

Use the helper script to grab a metrics snapshot:

```bash
python3 <SKILL_DIR>/scripts/ws_metrics.py --url <HOST>:<PORT> --count 3 --timeout 10
```

Arguments:
| Argument | Default | Description |
|----------|---------|-------------|
| `--url` | `127.0.0.1:8001` | WebSocket host:port |
| `--count` | `3` | Number of metric messages to collect |
| `--timeout` | `10` | Connection timeout in seconds |
| `--pretty` | (flag) | Pretty-print JSON output |

The script connects, subscribes to metrics, collects the specified number of messages, then cleanly disconnects. Output is one JSON object per line on stdout.

**Important**: Each metrics message covers a different layer of the stack. You will typically receive separate messages for:
- Scheduler metrics (per-UE and per-cell)
- MAC metrics
- CU-CP metrics (NGAP + RRC)
- Executor metrics
- App resource usage
- RU/OFH metrics (if Open Fronthaul is used)
- RLC metrics (per-bearer)

Collect at least 3 messages to get a representative cross-section.

## Step 3 — Interpret Metrics

After collecting metrics, parse the JSON. Refer to `references/metrics_schema.md` for the complete field reference.

### Common Questions and Which Fields to Check

| User Question | Metrics to Check |
|---------------|-----------------|
| "How many UEs are connected?" | Count entries in `cells[].ue_list[]` |
| "What's the signal quality?" | `ue_list[].cqi`, `ue_list[].pusch_snr_db`, `ue_list[].pusch_rsrp_db` |
| "What's the throughput?" | `ue_list[].dl_brate` (bps), `ue_list[].ul_brate` (bps) |
| "Are there packet drops?" | `ue_list[].dl_nof_nok`, `ue_list[].ul_nof_nok` — compute error rate as `nok / (ok + nok) * 100` |
| "Is the AMF connected?" | `cu-cp.ngaps.ngap[].connected` (boolean) |
| "Any scheduling issues?" | `cells[].cell_metrics.nof_failed_pdcch_allocs`, `late_dl_harqs`, `late_ul_harqs` |
| "System resource usage?" | `app_resource_usage.cpu_usage_percent`, `memory_usage_mb`, `power_consumption_watts` |
| "Executor bottlenecks?" | `executor_metrics.cpu_load`, `task_avg`, `enqueue_avg` |
| "Fronthaul issues?" | `ru.ofh.timing_stats.nof_skipped_symbols`, `cells[].ul.rx_window_stats` |

## Step 4 — Read Configuration (Optional)

To answer questions about how the gNB is configured, read and parse the YAML config file:

```bash
cat <CONFIG_FILE_PATH>
```

Refer to `references/config_params.md` for the key configuration sections and parameters.

## Important Notes

- **Operations**: This skill can start, stop, and monitor gNB/UE. Always confirm with the user before starting or stopping components.
- **Bitrate units**: `dl_brate` and `ul_brate` from the scheduler are in **bits per second** (bps). Convert: divide by 1e6 for Mbps.
- **CQI range**: 0–15 (15 = best). A value of -1 means no CQI report received.
- **MCS range**: 0–28 (higher = more efficient modulation).
- **PUSCH SNR**: in dB. Clamped to [-99.9, 99.9]. `null` means no measurement.
- **RSRP**: in dBm. Clamped to [-99.9, 0.0]. `null` means no measurement.
- **Timing Advance**: `ta_ns`, `pusch_ta_ns`, `pucch_ta_ns`, `srs_ta_ns` are in **nanoseconds**.
- **BSR (Buffer Status Report)**: in bytes, from the UE's perspective.
- **PHR (Power Headroom)**: in dB. `null` if not reported.
- **Latency values**: `average_latency`, `max_latency` in scheduler cell metrics are in **microseconds**.
- **MAC latency**: `average_latency_us`, `min_latency_us`, `max_latency_us` are in **microseconds**.
- **No authentication**: The WebSocket interface has zero authentication. It is bound to `127.0.0.1` by default for security.
- **Metrics periodicity**: Metrics are pushed periodically (configurable, typically every 1 second). Each call to `ws_metrics.py` gets a snapshot.
- **Multiple metric types per connection**: Different metric categories arrive as separate JSON messages. A single subscribe call yields multiple messages over time.

