Knowledge Base

Everything you need to know and understand to develop V2X applications.

denm-generator.py
msg = self.generate_denm()
future = self.send_request(msg)
future.add_done_callback(self.request_completed)

The Contract

Every great API needs a clear contract and cube-radio-rpc is no exception. The schema file is your single source of truth, defining every frame format, parameter, callback, and RPC method available for remotely controlling the cube's V2X radio.

Think of it as the blueprint: once you understand this schema, you can build robust RPC clients in any language supported by Cap'n Proto. Let's break it down.


Frames – The Core Data Structure

A Frame is the fundamental unit exchanged between your client and the radio. It is also the primary data observable over-the-air exchanged among V2X radios.

struct Frame {
sourceAddress @0 :Data;
destinationAddress @1 :Data;
payload @2 :Data;
}

What's inside:

  • sourceAddress — The sender's MAC or sidelink address
  • destinationAddress — The intended recipient's address (can be a broadcast address)
  • payload — Your actual V2X message data

You'll pass frames into transmitData() and receive them through DataListener callbacks.


Transmit Parameters – Controlling Your Transmission

While a Frame determines what should be sent, the TxParameters describe how it is sent. The adjustable parameters depend on the employed radio technology:

struct TxParameters {
union {
unspecified @0 :Void;
wlan @1 :WlanParameters;
cv2x @2 :Cv2xParameters;
}
}

For ITS-G5 / WLAN

struct WlanParameters {
priority @0 :UInt8; # Access category (07)
power @1 :Int16; # Transmit power in dBm × 8
datarate @2 :UInt16; # Data rate in 500 kbps steps
}

Examples:

  • priority = 3 → AC_BE (Best Effort)
  • power = 160 → 20 dBm (160 / 8)
  • datarate = 12 → 6 Mbps (12 × 500 kbps)

For C-V2X / LTE-V2X

struct Cv2xParameters {
priority @0 :UInt8; # ProSe Per-Packet Priority (PPPP, 07)
power @1 :Int16; # Transmit power in dBm × 8
}

Note: Lower PPPP values = higher priority (0 is highest)


Receive Parameters – Understanding Incoming Frames

When a frame arrives, the cube provides additional metadata in the form of RxParameters:

struct RxParameters {
union {
unspecified @0 :Void;
wlan @1 :WlanParameters;
cv2x @2 :Cv2xParameters;
}
timestamp :union {
none @3 :Void;
hardware @4 :UInt64;
software @5 :UInt64;
}
}

You get:

  • Link-layer parameters — Same structure as TX (priority, power, data rate)
  • Timestamp — Reception time (timestamped by hardware if available, otherwise software)

This data can be crucial for time-sensitive applications and analyzing reception quality. Please note that the encoded power value is not the value set by the transmitter but the power level measured by the receiving radio.


Listeners – Asynchronous Callbacks

Don't poll for data in a busy loop but let the cube push frames to you efficiently via subscriptions:

interface DataListener {
onDataIndication @0 (frame: Frame, rxParams :RxParameters);
}
interface CbrListener {
onCbrReport @0 (cbr :ChannelBusyRatio);
}

A subscribed DataListener receives every incoming V2X frame. A CbrListener monitors channel congestion by receving Channel Busy Ratio (CBR) samples, which are essential for compliance with ETSI's Decentralized Congestion Control (DCC).

Register your listeners once, then handle frames as they arrive — perfect for real-time applications where latency matters.


RPC Methods – Your Remote Control API

The LinkLayer interface gives you five essential operations:

1. Radio identification identify() → DeviceInfo

Get information about the remote device including its ID, software version and additional information. The latter field also describes the active radio on the cube, e.g. cube-radio=dsrc or cube-radio=cv2x.

2. V2X transmission transmitData(frame, txParams) → ErrorCode

Send a V2X frame with specific transmission parameters.

Your main workhorse for outgoing communication.

3. V2X subscription subscribeData(listener) → ErrorCode

Register a DataListener to receive incoming frames asynchronously.

Call once at startup, handle frames as they arrive.

4. CBR subscription subscribeCbr(listener) → ErrorCode

Monitor Channel Busy Ratio — critical for adapting the station to prevailing channel congestion levels.

Recommended for production deployments to comply with ETSI DCC.

5. Source address setSourceAddress(address) → ErrorCode

Configure the cube's own MAC/sidelink address.

Set once unless you need dynamic address changes. The ETSI ITS security profile requires you to change your source address based on your current pseudonym. Your C-ITS stack should handle this for you.


Error Handling – We have you covered

Potentially failing RPC calls, e.g. transmissions with invalid settings, return an ErrorCode:

enum ErrorCode {
ok @0; # Success
invalidArgument @1; # Bad parameters (check your inputs)
unsupported @2; # Feature not available on this radio
internalError @3; # Something went wrong (check logs)
}

As a rule of thumb, you should always check the error code instead of silently assuming success. It's your first debugging clue.


Putting It All Together

This schema is the complete reference for cube-radio-rpc. Here's your workflow:

  1. Download the schema file
  2. Generate language bindings with Cap'n Proto tools
  3. Connect to the cube on port 23057
  4. Subscribe to listeners for incoming data
  5. Transmit frames with appropriate parameters

Want to see this in action? Check out our Python Example for a complete, working implementation.

Ready to build? 🚀

Previous
RPC