This guide describes how to build and deploy the current IPI reference implementation for:
- radio-path testing with the Mocar V2X SDK
- private 5G latency testing over TCP and MQTT
- basic Edge4AV/IPI application bring-up
- synchronized experiment logging for the Edge4AV paper campaign
A practical lab deployment usually has these roles:
Build Host: where you compile IPITX-EDGE: Linux host that sends SPaT to the transmitting radio over TCPV2X-TX-RADIO: transmitter-side Mocar radio deviceV2X-RX-RADIO: receiver-side V2X radio device5G Vehicle Node: sender-side device on the private 5G network5G Infrastructure Node: receiver-side device on the private 5G networkMQTT Broker: only needed for the MQTT path; may run on the 5G infrastructure node or another reachable host
You can collapse some roles onto one machine, but keeping them separate makes latency measurements cleaner.
Required on the build host:
- Linux
cmake- C++17 compiler
make
Required for Mocar V2X deployment:
- Mocar SDK files under
third_party/mocar/ - ability to run the J2735 bridge binary on the transmitter-side radio
aarch64-linux-gnu-g++if you cross-buildipi_spat_bridgefrom an x86 host, or a nativeg++toolchain if you build directly on the radio
Required for MQTT deployment:
- an MQTT 3.1.1-compatible broker reachable by both 5G endpoints
Required for the proposal-facing ROS 2 reference runtime:
- ROS 2 Humble with
ament_cmake,rclcpp,sensor_msgs, andlaunch_testing_ament_cmake - the system ROS Python (
/usr/bin/python3on the validated Humble host)
Recommended for meaningful latency measurements:
- synchronized clocks across endpoints
- GNSS-disciplined PTP if possible
- otherwise NTP/Chrony and RTT-centric analysis
From the repository root:
cmake -S cpp -B cpp/build -DIPI_ENABLE_TESTS=ON
cmake --build cpp/build
ctest --test-dir cpp/build --output-on-failureThis builds the core IPI library, the Edge4AV examples, and the private 5G
latency tools. It does not build the vendored J2735-2020 Mocar bridge.
Important outputs:
cpp/build/libipi.acpp/build/example_spat_tcp_sendercpp/build/example_private_5g_latency_sendercpp/build/example_private_5g_latency_receivercpp/build/example_edge4av_dual_plane
The library exports IPI::ipi for downstream packages:
cmake -S cpp -B cpp/build \
-DIPI_ENABLE_TESTS=ON \
-DCMAKE_INSTALL_PREFIX="$PWD/cpp/install"
cmake --build cpp/build --parallel
cmake --install cpp/buildDownstream CMake projects can use:
find_package(IPI CONFIG REQUIRED)
target_link_libraries(my_target PRIVATE IPI::ipi)Add cpp/install to CMAKE_PREFIX_PATH if it is not already searchable.
From the repository root:
make -C third_party/mocar/J2735-2020/samples/ipi_spat_bridge clean
make -C third_party/mocar/J2735-2020/samples/ipi_spat_bridgeThis produces:
third_party/mocar/J2735-2020/samples/ipi_spat_bridge/ipi_spat_bridge
The vendored bridge Makefile defaults to aarch64-linux-gnu-g++. If you are
building directly on the radio instead of cross-compiling from the build host,
use:
make -C third_party/mocar/J2735-2020/samples/ipi_spat_bridge CXX=g++Also keep these runtime libraries available on the Mocar target:
third_party/mocar/J2735-2020/lib/libmocarcv2x.sothird_party/mocar/J2735-2020/lib/libzlog.so
The strict IP5X sample is separate from the existing SPaT and custom-RTT
samples. Its application payload is a complete UPER J2735 MessageFrame using
reserved TestMessage00 (DSRCmsgID 240), local RegionId 200, and the typed
IPI regional value from cpp/asn1/IPI.asn:
make -C third_party/mocar/J2735-2020/samples/ipi_pc5_exchange clean
make -C third_party/mocar/J2735-2020/samples/ipi_pc5_exchangeCopy the resulting ipi_pc5_exchange binary and vendor shared libraries to
both radios. Start the responder before the initiator:
./ipi_pc5_exchange --role responder --node-id node-b --peer-id node-a
./ipi_pc5_exchange --role initiator --node-id node-a --peer-id node-b \
--count 100 --body-bytes 128 --timeout-ms 1000--body-bytes is the number of bytes in the IPI offload-content field, not the
final radio application-packet size. The default 2048-byte cap applies to the
complete IP5X packet, including metadata, the J2735 frame, length fields, and
CRC. An input that cannot fit is rejected before transmission.
--max-packet-bytes can be raised only up to the observed 4080-byte SDK
application ceiling. That larger value is deployment-specific and must be
validated again on the actual device pair.
The deterministic vector verifier requires asn1tools 0.167.0, pycrate
0.7.11, and an installed USDOT J2735 pycrate package. It compiles IPI.asn with
both independent ASN.1 implementations, compares every regional UPER value,
then verifies the outer MessageFrame with the unmodified J2735 package:
python3 scripts/verify_j2735_ipi_vectors.pyThe repository includes the SAE standard PDF but not the separately licensed
J2735ASN_202309 module bundle. After obtaining that bundle, prepare a combined
schema in a new directory:
python3 scripts/prepare_j2735_ipi_schema.py \
--base-dir /path/to/J2735ASN_202309 \
--output-dir /tmp/j2735asn-202309-ipiThe preparation step verifies the TestMessage00 identifier, adds the typed
IPI entry to Reg-TestMessage00, and records input and output hashes. Compile
the resulting directory with the licensed production ASN.1/UPER compiler; do
not edit or redistribute the source SAE modules.
On each runtime host, stage a small deployment directory such as:
/opt/ipi/
bin/
lib/
logs/
config/
Suggested contents:
bin/example_spat_tcp_senderexample_private_5g_latency_senderexample_private_5g_latency_receiveripi_spat_bridge
lib/- Mocar shared libraries when needed
logs/- sender CSV files
- receiver CSV files
- receiver stdout/stderr logs
- radio receive logs from
V2X-RX-RADIO - run manifests keyed by
run_idandcondition_id
Example deployment commands:
scp cpp/build/example_spat_tcp_sender user@TX-EDGE:/opt/ipi/bin/
scp cpp/build/example_private_5g_latency_sender user@5G-1:/opt/ipi/bin/
scp cpp/build/example_private_5g_latency_receiver user@5G-2:/opt/ipi/bin/
scp third_party/mocar/J2735-2020/samples/ipi_spat_bridge/ipi_spat_bridge user@V2X-TX-RADIO:/opt/ipi/bin/
scp third_party/mocar/J2735-2020/lib/libmocarcv2x.so user@V2X-TX-RADIO:/opt/ipi/lib/
scp third_party/mocar/J2735-2020/lib/libzlog.so user@V2X-TX-RADIO:/opt/ipi/lib/No repo-provided receive-only binary is required on V2X-RX-RADIO unless you
add your own callback/logger there. That side currently depends on the vendor
receive path and whatever latency logging you already use in the lab.
Useful environment variables:
MOCAR_TCP_PORT- overrides the listen port for
ipi_spat_bridge
- overrides the listen port for
IPI_DEBUG=1- enables debug hex output in some sender-side helpers
If the Mocar target does not already know where to find its shared libraries, set:
export LD_LIBRARY_PATH=/opt/ipi/lib:$LD_LIBRARY_PATHFor the paper experiments, standardize these runtime flags across sender, receiver, and bridge processes:
--run-id--condition-id--condition-label--request-id--network-load-level--qos-profile--mobility-state--clock-sync-state--csv
Recommended condition-label values:
radio-baselineprivate-5g-baselineprivate-5g-stressed
Deploy at minimum:
example_spat_tcp_sender
Responsibilities:
- build or relay the SPaT payload toward
V2X-TX-RADIO - timestamp the sender-side start of the radio-path experiment
- emit run- and condition-tagged experiment logs
Deploy:
ipi_spat_bridge- Mocar shared libraries
Responsibilities:
- receive SPaT payloads from
TX-EDGEover TCP - forward them into the Mocar V2X transmit path
- emit RSU-side bridge logs with the same
run_idandcondition_id
Deploy:
- your receive-side Mocar callback/logger or existing vendor telemetry tooling
Responsibilities:
- receive the over-the-air SPaT broadcast
- record receive timestamps, radio logs, or callback output
The vendored third_party/mocar/J2735-2020/samples/spat/spat_sample.c shows
the receive callback API, but its sample main() also transmits SPaT. Treat it
as a callback reference, not a drop-in receive-only latency harness.
Deploy at minimum:
example_private_5g_latency_receiver- optionally the MQTT broker
- optionally
example_edge4av_dual_planefor API smoke testing
Responsibilities:
- listen for TCP latency probes
- subscribe to MQTT latency request topics
- host or reach the MQTT broker
- emit infrastructure-side experiment logs with condition metadata
Deploy:
example_private_5g_latency_senderon the 5G sender
Responsibilities:
- drive the 5G latency experiments
- write sender-side logs and CSV files
- vary payload size, message type, and interval
Recommended order:
- Build and pass
ctest. - Build
ipi_spat_bridgeand, if measuring radio RTT,ipi_custom_rtt. - Stage binaries on all runtime nodes.
- Verify time synchronization.
- Start receive logging on
V2X-RX-RADIO. - Start
ipi_spat_bridgeonV2X-TX-RADIO. - Run
example_spat_tcp_senderonTX-EDGE. - Start the 5G TCP receiver on
5G-2. - Run the 5G TCP latency sender on
5G-1. - Bring up the MQTT broker.
- Run the 5G MQTT latency test.
- Archive CSV logs and stderr summaries after each run.
The repo does not ship a production broker.
Typical options:
- run Mosquitto on the infrastructure-side 5G node
- run a broker on a separate edge VM reachable from both 5G devices
If you use Mosquitto, the default plain TCP port is usually 1883.
Current code boundary:
- the in-repo MQTT path is a minimal MQTT 3.1.1 client implementation
- it does not add TLS on its own
If you need broker-authenticated TLS for deployment, add that as the next step.
The session transport is distinct from the latency-probe MQTT topics. A deployment instantiates:
make_mqtt_session_message_brokerin each process that connects to the broker;make_broker_session_endpointin the infrastructure process; andmake_broker_private_session_transportin each vehicle-side client process.
Both ends exchange strict IPIS records for registration, heartbeat, patch,
termination, service requests and updates, events, telemetry, and PCV
responses. The shared lifecycle enforces active state, monotonic leases,
ownership, service grants, sequence/freshness, outstanding requests, and
terminal outcomes.
MqttSessionBrokerConfig accepts host, port, client ID, username, password,
keepalive, clean-session, allowed topic prefix, and reconnect backoff. The
configuration also bounds connect and socket I/O waits so an unavailable broker
cannot block shutdown indefinitely. The current implementation reconnects and
resubscribes after detected connection loss. It is a reference MQTT 3.1.1
client using QoS 0 and plain TCP. Do not use it as production evidence until
TLS, deployment access policy, required QoS, and real broker disconnect/restart
tests have passed.
The ipi_msgs package defines the activation, service-intent/link,
policy-decision, offload-request, and offload-decision contracts. ipi_runtime
provides the activation manager, tiered service manager, offload decision node,
and a parameterized reference-zone publisher.
After installing the C++ library as shown in Section 3.1:
source /opt/ros/humble/setup.bash
PATH=/opt/ros/humble/bin:/usr/bin:/bin \
colcon --log-base ros2_ws/log build \
--base-paths ros2_ws/src/ipi_msgs ros2_ws/src/ipi_runtime \
--build-base ros2_ws/build \
--install-base ros2_ws/install \
--cmake-args \
-DCMAKE_PREFIX_PATH="$PWD/cpp/install" \
-DPython3_EXECUTABLE=/usr/bin/python3Run the launch contract test:
source ros2_ws/install/setup.bash
colcon --log-base ros2_ws/log test \
--build-base ros2_ws/build \
--install-base ros2_ws/install \
--packages-select ipi_runtime \
--event-handlers console_direct+
colcon test-result --test-result-base ros2_ws/build --verboseRun the reference intersection:
source ros2_ws/install/setup.bash
ros2 launch ipi_runtime reference_intersection.launch.pyThe launch path publishes decisions only. It does not directly call Autoware operation-mode services or send motion/control commands. A future approved vehicle adapter must retain local safety authority and reject stale or unsafe requests.
Use these quick checks.
On the build host:
./cpp/build/example_edge4av_dual_planeOn 5G-2:
/opt/ipi/bin/example_private_5g_latency_receiver --transport tcp --port 36666 --onceOn 5G-1:
/opt/ipi/bin/example_private_5g_latency_sender --transport tcp --host <5G-2_IP> --port 36666 --count 1 --message serviceFor MQTT, verify:
- broker reachable on
host:port - sender and receiver use the same
--intersection-id - sender and receiver use the same
--source-id - all nodes in the same condition use the same
--run-idand--condition-id - the receiver or bridge side sets the intended
--rsu-id
Be explicit about these when you deploy:
- Radio path currently uses the vendored
J2735-2020Mocar bridge sample for SPaT forwarding. - The radio-path sender host and the transmitting radio are separate roles.
- The repo ships
ipi_custom_rttfor Mocar radio RTT. SPaT receive-side one-way logging still depends on the Mocar callback/logger used in the lab. - Private 5G latency tooling supports both TCP and MQTT.
- Private-5G RTT uses a local steady clock. Wall-clock timestamps are retained for log correlation, and one-way latency is emitted only when clock synchronization is explicitly declared.
- The correlated private-5G executable and native detector schema have local tests, but historical result files were not relabeled; new monotonic or native-detector claims require recollection.
- The broker session reference path has an in-memory broker integration test; it has not yet passed a deployed-broker restart or TLS test.
- The
IP5XPC5 adapter and Mocar sample compile for AArch64, but a two-radio field run remains required. - The ROS 2 reference runtime has a synthetic launch test. It publishes activation/policy/offload decisions and intentionally does not command Autoware motion.
- The current tools standardize experiment logs, but AV-facing outcome metrics still need to be supplied by the operator or higher-level AV stack.
- MQTT support is minimal MQTT 3.1.1 over plain TCP.
- The repo provides a measurement harness, not a full production backend.
- One-way latency is only meaningful if your clocks are synchronized.
- The typed IPI regional schema, complete
TestMessage00codec, dual-compiler payload vectors, and USDOT 202409 outer-frame check are implemented. Exact compilation and interoperability against the separately licensed SAEJ2735ASN_202309modules remain pending because those files are not in this repository.IP5Xitself is project framing over the vendor custom channel, not a standardized J2735 bearer. - The repository does not yet carry an approved project license for external release; ROS package license identifiers remain a release gate.
- The optional CMake Mocar example path under
cpp/examples/devicetargets a different SDK layout and is not the deployment path documented here.