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

CommonNH.BTP_A

Interactive packet transport (with source port for replies)

BTP-B

CommonNH.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.


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

btp_type

BTP_A (interactive) or BTP_B (non-interactive)

source_port

Source port (used for BTP-A replies, 0 for BTP-B)

destination_port

Target port number (e.g., 2001 for CAM)

destination_port_info

Additional port info (usually 0)

gn_packet_transport_type

GeoNetworking transport type (broadcast, unicast, etc.)

gn_destination_address

GN destination (for unicast)

gn_area

Geographic area (for geo-broadcast)

traffic_class

Priority and channel settings

data

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_type

BTP-A or BTP-B

source_port

Sender’s port (BTP-A only)

destination_port

Port this packet was sent to

destination_port_info

Additional port info

data

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