Basic Transport Protocol (BTP)ο
BTP is the transport layer of the V2X protocol stack. Think of it like UDP for V2X β it provides port-based multiplexing so multiple services (CAM, DENM, etc.) can share the same GeoNetworking layer.
Note
BTP is defined in ETSI EN 302 636-5-1. It sits between GeoNetworking (below) and Facilities services like CAM and DENM (above).
What BTP Doesο
π Port multiplexing β Routes packets to the correct service based on port numbers
π¨ Simple delivery β Lightweight, connectionless transport (like UDP)
π·οΈ Service identification β Well-known ports for standard services (CAM=2001, DENM=2002, etc.)
Architectureο
BTP connects GeoNetworking to upper-layer services:
flowchart LR
FAC["Facilities Service<br/>(CAM, DENM, etc.)"]
BTP["Basic Transport Protocol (BTP)"]
GN["GeoNetworking Layer"]
FAC -->|BTPDataRequest| BTP
BTP -->|GNDataRequest | GN
GN -->|GNDataIndication| BTP
BTP -->|BTPDataIndication| FAC
BTP Typesο
BTP defines two packet types:
Type |
Constant |
Description |
|---|---|---|
BTP-A |
|
Interactive packet transport (with source port for replies) |
BTP-B |
|
Non-interactive packet transport (broadcast, no reply expected) |
Most V2X services use BTP-B since CAMs and DENMs are broadcast without expecting replies.
Well-Known Portsο
Port |
Service |
Description |
|---|---|---|
2001 |
CA (CAM) |
Cooperative Awareness Messages |
2002 |
DEN (DENM) |
Decentralized Environmental Notifications |
2003 |
RLT (MAPEM) |
Road and Lane Topology |
2004 |
TLM (SPATEM) |
Traffic Light Maneuver |
2005 |
SA (SAEM) |
Service Announcement |
2006 |
IVI (IVIM) |
Infrastructure to Vehicle Information |
2007 |
TLC (SREM) |
Traffic Light Control |
2008 |
TLC (SSEM) |
Signal Status Extension Message |
2009 |
CP (CPM) |
Collective Perception Service |
2010 |
EVCSN POI |
EV Charging Station Notification |
2011 |
TPG (TRM, TCM, VDRM, VDPM, EOFM) |
Tow-Away and Parking Guidance |
2012 |
Charging (EV-RSR) |
EV Roaming Service Request |
2013 |
GPC (RTCMEM) |
GNSS Positioning Correction |
2014 |
CTL (CTLM) |
Certificate Trust List Message |
2015 |
CRL (CRLM) |
Certificate Revocation List Message |
2016 |
EC/AT Request |
Enrollment and Authorization Ticket Request |
2017 |
MCD (MCDM) |
Misbehavior Complaint Data Message |
2018 |
VA (VAM) |
Vulnerable Road User Awareness Message |
2019 |
IMZ (IMZM) |
Imminent Safety-Related Condition Indication |
Getting Startedο
Step 1: Create GeoNetworking Routerο
BTP requires a GeoNetworking router underneath:
from flexstack.geonet.router import Router as GNRouter
from flexstack.geonet.mib import MIB
from flexstack.geonet.gn_address import GNAddress, M, ST, MID
MAC_ADDRESS = b'\x00\x11\x22\x33\x44\x55'
mib = MIB(
itsGnLocalGnAddr=GNAddress(
m=M.GN_MULTICAST,
st=ST.CYCLIST,
mid=MID(MAC_ADDRESS),
),
)
gn_router = GNRouter(mib=mib, sign_service=None)
Step 2: Create BTP Routerο
Create the BTP router and connect it to GeoNetworking:
from flexstack.btp.router import Router as BTPRouter
# Create BTP router on top of GN
btp_router = BTPRouter(gn_router)
# Connect GN indications to BTP (incoming packets)
gn_router.register_indication_callback(btp_router.btp_data_indication)
Step 3: Register Port Callbacksο
Register callbacks for the ports you want to receive on:
from flexstack.btp.service_access_point import BTPDataIndication
def on_cam_received(indication: BTPDataIndication):
"""Handle incoming CAM packets (port 2001)."""
print(f"π₯ CAM received: {indication.data.hex()}")
def on_denm_received(indication: BTPDataIndication):
"""Handle incoming DENM packets (port 2002)."""
print(f"β οΈ DENM received: {indication.data.hex()}")
# Register callbacks for specific ports
btp_router.register_indication_callback_btp(port=2001, callback=on_cam_received)
btp_router.register_indication_callback_btp(port=2002, callback=on_denm_received)
Step 4: Freeze Callbacksο
Before any packets flow, you must freeze the callback registry:
# Lock the callback registry (required before sending/receiving)
btp_router.freeze_callbacks()
Warning
Always call freeze_callbacks() after registering all port callbacks and before
starting the link layer. This ensures thread safety and optimal performance.
Step 5: Connect Link Layerο
Finally, connect the GN router to a link layer:
from flexstack.linklayer.raw_link_layer import RawLinkLayer
link_layer = RawLinkLayer(
iface="lo",
mac_address=MAC_ADDRESS,
receive_callback=gn_router.gn_data_indicate
)
gn_router.link_layer = link_layer
Sending Packetsο
To send a BTP packet, create a BTPDataRequest:
from flexstack.btp.service_access_point import BTPDataRequest
from flexstack.geonet.service_access_point import (
Area,
PacketTransportType,
CommunicationProfile,
TrafficClass,
CommonNH,
)
from flexstack.geonet.gn_address import GNAddress
# Your payload
payload = b"Hello, BTP!"
# Create BTP request
btp_request = BTPDataRequest(
btp_type=CommonNH.BTP_B, # Non-interactive (broadcast)
source_port=0, # Not used for BTP-B
destination_port=2001, # Target port (e.g., CAM port)
destination_port_info=0,
gn_packet_transport_type=PacketTransportType(),
gn_destination_address=GNAddress(),
gn_area=Area(),
communication_profile=CommunicationProfile.UNSPECIFIED,
traffic_class=TrafficClass(),
length=len(payload),
data=payload,
)
# Send it!
btp_router.btp_data_request(btp_request)
BTPDataRequest Parameters:
Parameter |
Description |
|---|---|
|
|
|
Source port (used for BTP-A replies, 0 for BTP-B) |
|
Target port number (e.g., 2001 for CAM) |
|
Additional port info (usually 0) |
|
GeoNetworking transport type (broadcast, unicast, etc.) |
|
GN destination (for unicast) |
|
Geographic area (for geo-broadcast) |
|
Priority and channel settings |
|
Payload bytes |
Receiving Packetsο
Warning
All reception callbacks must be registered before calling freeze_callbacks()!
Incoming packets are delivered to the registered callback for that port:
from flexstack.btp.service_access_point import BTPDataIndication
def on_packet_received(indication: BTPDataIndication):
"""Called when a packet arrives on our registered port."""
print(f"π₯ Received on port {indication.destination_port}")
print(f" Source port: {indication.source_port}")
print(f" Data length: {len(indication.data)} bytes")
print(f" Payload: {indication.data.hex()}")
# Register for a specific port
btp_router.register_indication_callback_btp(
port=1234,
callback=on_packet_received
)
BTPDataIndication Fields:
Field |
Description |
|---|---|
|
BTP-A or BTP-B |
|
Senderβs port (BTP-A only) |
|
Port this packet was sent to |
|
Additional port info |
|
Payload bytes |
Complete Exampleο
Hereβs a complete script that sends and receives BTP packets:
1#!/usr/bin/env python3
2"""
3BTP Example
4
5Demonstrates sending and receiving BTP packets over GeoNetworking.
6"""
7
8import time
9from flexstack.linklayer.raw_link_layer import RawLinkLayer
10from flexstack.geonet.router import Router as GNRouter
11from flexstack.geonet.mib import MIB
12from flexstack.geonet.gn_address import GNAddress, M, ST, MID
13from flexstack.btp.router import Router as BTPRouter
14from flexstack.btp.service_access_point import BTPDataRequest, BTPDataIndication
15from flexstack.geonet.service_access_point import (
16 Area,
17 PacketTransportType,
18 CommunicationProfile,
19 TrafficClass,
20 CommonNH,
21)
22
23MAC_ADDRESS = b'\x00\x11\x22\x33\x44\x55'
24MY_PORT = 1234
25
26
27def on_btp_received(indication: BTPDataIndication):
28 """Handle incoming BTP packets."""
29 print(f"π₯ Received BTP on port {indication.destination_port}")
30 print(f" Data: {indication.data[:20].hex()}...")
31
32
33def main():
34 # Step 1: Create GN Router
35 mib = MIB(
36 itsGnLocalGnAddr=GNAddress(
37 m=M.GN_MULTICAST,
38 st=ST.CYCLIST,
39 mid=MID(MAC_ADDRESS),
40 ),
41 )
42 gn_router = GNRouter(mib=mib, sign_service=None)
43
44 # Step 2: Create BTP Router
45 btp_router = BTPRouter(gn_router)
46 gn_router.register_indication_callback(btp_router.btp_data_indication)
47
48 # Step 3: Register port callback
49 btp_router.register_indication_callback_btp(
50 port=MY_PORT,
51 callback=on_btp_received
52 )
53
54 # Step 4: Freeze callbacks (required!)
55 btp_router.freeze_callbacks()
56
57 # Step 5: Connect Link Layer
58 link_layer = RawLinkLayer(
59 iface="lo",
60 mac_address=MAC_ADDRESS,
61 receive_callback=gn_router.gn_data_indicate,
62 )
63 gn_router.link_layer = link_layer
64
65 print("β
BTP Router active")
66 print(f"π‘ Listening on port {MY_PORT}")
67 print("Press Ctrl+C to exit\n")
68
69 try:
70 while True:
71 # Create payload
72 payload = bytes([i % 256 for i in range(100)])
73
74 # Build BTP request
75 btp_request = BTPDataRequest(
76 btp_type=CommonNH.BTP_B,
77 source_port=0,
78 destination_port=MY_PORT,
79 destination_port_info=0,
80 gn_packet_transport_type=PacketTransportType(),
81 gn_destination_address=GNAddress(),
82 gn_area=Area(),
83 communication_profile=CommunicationProfile.UNSPECIFIED,
84 traffic_class=TrafficClass(),
85 length=len(payload),
86 data=payload,
87 )
88
89 # Send packet
90 btp_router.btp_data_request(btp_request)
91 print(f"π€ Sent BTP to port {MY_PORT}: {payload[:20].hex()}...")
92
93 time.sleep(1)
94
95 except KeyboardInterrupt:
96 print("\nπ Shutting down...")
97
98 # Cleanup
99 link_layer.sock.close()
100 print("β
Shutdown complete")
101
102
103if __name__ == "__main__":
104 main()
Integration with Facilitiesο
In practice, you rarely use BTP directly. Instead, Facilities services (CAM, DENM) use BTP internally:
from flexstack.facilities.ca_basic_service.ca_basic_service import (
CooperativeAwarenessBasicService,
)
# CA Basic Service uses BTP internally on port 2001
ca_service = CooperativeAwarenessBasicService(
btp_router=btp_router, # π Pass BTP router
vehicle_data=vehicle_data,
ldm=ldm,
)
See the Getting Started tutorial for a complete example with CAM.
See Alsoο
Getting Started β Complete tutorial building a V2X application
GeoNetworking (GN) β GeoNetworking layer (underneath BTP)
Cooperative Awareness (CA) Basic Service β CA Basic Service (uses BTP port 2001)
Decentralized Environmental Notification (DEN) Service β DEN Service (uses BTP port 2002)