Application Configuration Guide
# Application Configuration API - User Guide <!-- omit from toc --> ## Table of Contents <!-- omit from toc --> - [Overview](#overview) - [Getting Started](#getting-started) - [Authentication](#authentication) - [API Endpoints](#api-endpoints) - [Understanding Geofence Configurations](#understanding-geofence-configurations) - [What is a Geofence Configuration?](#what-is-a-geofence-configuration) - [Configuration Lifecycle](#configuration-lifecycle) - [Geofence Geometry](#geofence-geometry) - [Supported Geometry Types](#supported-geometry-types) - [Geometry Requirements](#geometry-requirements) - [LineString](#linestring) - [MultiLineString](#multilinestring) - [Polygon](#polygon) - [MultiPolygon](#multipolygon) - [Area Size Limits](#area-size-limits) - [Trigger Conditions](#trigger-conditions) - [Trigger Types](#trigger-types) - [Visual Guide to Trigger Conditions](#visual-guide-to-trigger-conditions) - [Polygon Triggers (enter, leave, inside)](#polygon-triggers-enter-leave-inside) - [LineString Triggers (crossing)](#linestring-triggers-crossing) - [Multi-Geometry Triggers](#multi-geometry-triggers) - [Geometry-Specific Rules](#geometry-specific-rules) - [Multiple Trigger Conditions](#multiple-trigger-conditions) - [Messages](#messages) - [Message Fields](#message-fields) - [`isPrivate` (boolean, required)](#isprivate-boolean-required) - [`roadUserType` (array, required)](#roadusertype-array-required) - [`triggerConditions` (array, required)](#triggerconditions-array-required) - [`limits` (object, optional)](#limits-object-optional) - [`distributionType` (array, optional)](#distributiontype-array-optional) - [`distributionSchedule` (object, optional)](#distributionschedule-object-optional) - [Message Types](#message-types) - [Generic Custom Messages](#generic-custom-messages) - [Custom Message Examples](#custom-message-examples) - [Example 1: Simple JSON Custom Message](#example-1-simple-json-custom-message) - [Example 2: TAM (TollAdvertisementMessage)](#example-2-tam-tolladvertisementmessage) - [Example 3: EVA (Emergency Vehicle Alert)](#example-3-eva-emergency-vehicle-alert) - [Example 4: J2735\_gr (GeoRouted) Custom Message](#example-4-j2735_gr-georouted-custom-message) - [Custom Message Payload Encoding Reference](#custom-message-payload-encoding-reference) - [SAE Road Side Alert (RSA)](#sae-road-side-alert-rsa) - [SAE Traveler Information Message (TIM)](#sae-traveler-information-message-tim) - [ETSI Decentralized Environmental Notification Message (DENM)](#etsi-decentralized-environmental-notification-message-denm) - [Message Standards](#message-standards) - [Advanced Configuration Options](#advanced-configuration-options) - [Speed Limits](#speed-limits) - [Heading/Direction Limits](#headingdirection-limits) - [Road User Types](#road-user-types) - [Distribution Types](#distribution-types) - [Targeted Distribution](#targeted-distribution) - [Broadcast Distribution](#broadcast-distribution) - [Coverage/Broadcast Area Calculation](#coveragebroadcast-area-calculation) - [Geohash Grid System](#geohash-grid-system) - [Example: Geofence Coverage Calculation](#example-geofence-coverage-calculation) - [Coverage Area Size](#coverage-area-size) - [Performance Considerations](#performance-considerations) - [Hybrid Distribution (Targeted + Broadcast)](#hybrid-distribution-targeted--broadcast) - [Choosing the Right Distribution Type](#choosing-the-right-distribution-type) - [Validation Rules](#validation-rules) - [Message Type Considerations](#message-type-considerations) - [Best Practices](#best-practices) - [Configuration Design](#configuration-design) - [Performance Optimization](#performance-optimization) - [Geometry Best Practices](#geometry-best-practices) - [Message Design](#message-design) - [Common Use Cases](#common-use-cases) - [Use Case 1: School Zone Speed Warning](#use-case-1-school-zone-speed-warning) - [Use Case 2: Construction Zone Advisory](#use-case-2-construction-zone-advisory) - [Use Case 3: Wrong-Way Driver Detection](#use-case-3-wrong-way-driver-detection) - [Use Case 4: Hybrid Distribution - Critical Intersection Hazard](#use-case-4-hybrid-distribution---critical-intersection-hazard) - [Use Case 5: Mid-block Pedestrian Crossing Warning](#use-case-5-mid-block-pedestrian-crossing-warning) - [Use Case 6: OEM Client-Side Logic - Broadcast Zone Definition](#use-case-6-oem-client-side-logic---broadcast-zone-definition) - [Use Case 7: Multi-Zone Parking Alert](#use-case-7-multi-zone-parking-alert) - [MQTT Integration](#mqtt-integration) - [Troubleshooting](#troubleshooting) - [Common Errors and Solutions](#common-errors-and-solutions) - [Error: "Invalid area location"](#error-invalid-area-location) - [Error: "AreaSizeError"](#error-areasizeerror) - [Error: "Invalid trigger condition for geometry type"](#error-invalid-trigger-condition-for-geometry-type) - [Error: "Polygon ring not closed"](#error-polygon-ring-not-closed) - [Error: "Polygon ring not counter-clockwise"](#error-polygon-ring-not-counter-clockwise) - [Error: "Configuration not found" (404)](#error-configuration-not-found-404) - [Error: "Forbidden" (403)](#error-forbidden-403) - [Error: "Too many requests" (429)](#error-too-many-requests-429) - [Error: "Invalid configuration" (400)](#error-invalid-configuration-400) - [Additional Resources](#additional-resources) ## Overview The Edge Transportation Exchange (ETX) Application Configuration APIs allow you to create geofence-based message configurations that automatically send V2X messages to road users when specific conditions are met. These APIs enable you to: - Define geographic zones using GeoJSON format - Set trigger conditions (enter, leave, inside, crossing) - Configure V2X messages (RSA alerts, TIM information, DENM notifications, or custom messages) - Apply speed and direction filters - Choose between targeted or broadcast distribution - Manage multiple configurations per vendor The configurations are distributed to Multi-Access Edge Computing (MEC) nodes where geofence violations are evaluated in real-time with low latency, ensuring rapid response to safety-critical events. --- ## Getting Started ### Authentication All Application Configuration APIs require two authentication tokens from ThingSpace: 1. **Access/Bearer Token**: Used for authorization 2. **Session/M2M Token**: Used for authentication 3. **VendorID Header**: Identifies your organization For detailed instructions on obtaining these tokens: - [ThingSpace Quick Start Guide - Obtaining an Access Token](https://thingspace.verizon.com/documentation/api-documentation.html#/http/quick-start/credentials-and-tokens/obtaining-an-access_token) - [ThingSpace Quick Start Guide - Obtaining a VZ-M2M Session Token](https://thingspace.verizon.com/documentation/api-documentation.html#/http/quick-start/credentials-and-tokens/obtaining-a-vz-m2m-sessiontoken-programmatically) - [ThingSpace API Video Guides](https://www.youtube.com/watch?v=QPJQFT3637w) **Example Request Headers:** ```text Authorization: Bearer M2E3NTlkY2YtNTc4YS00MTcMDItYTNiMTE5ZTgwN2M4 SessionToken: 290386b7-dad7-8447-c50c-31648cc233ea VendorID: VerizonETX ``` ### API Endpoints | Method | Endpoint | Description | | -------- | ---------- | --------------- | | POST | `/api/v1/application/configurations/geofence` | Create a new configuration | | GET | `/api/v1/application/configurations/geofence/ids` | List all configuration IDs for your vendor | | GET | `/api/v1/application/configurations/geofence?id={configId}` | Get details of a specific configuration | | PUT | `/api/v1/application/configurations/geofence?id={configId}` | Update an existing configuration | | DELETE | `/api/v1/application/configurations/geofence?id={configId}` | Delete a configuration | - **Production**: `https://imp.thingspace.verizon.com` --- ## Understanding Geofence Configurations ### What is a Geofence Configuration? A geofence configuration combines: 1. **Geographic boundaries** - Defined using GeoJSON format 2. **Trigger conditions** - When to send messages (enter, leave, inside, crossing) 3. **V2X messages** - What to send (up to 10 messages per configuration) 4. **Filters** - Speed and direction limits (optional) 5. **Distribution rules** - How to deliver messages (targeted or broadcast) ### Configuration Lifecycle 1. **Create** - POST request returns a unique configuration ID (UUID) 2. **Activate** - Set `isActive: true` to deploy to MECs 3. **Update** - Modify any aspect using PUT request 4. **Deactivate** - Set `isActive: false` to temporarily disable 5. **Delete** - Remove configuration permanently **Important**: All coordinates in your geofence must fall within a single MEC's coverage area. Configurations spanning multiple MECs will be rejected. --- ## Geofence Geometry ### Supported Geometry Types The Application Configuration API supports standard [GeoJSON](https://geojson.org/) geometry types: | Geometry Type | Dimension | Use Case | | --------------- | ----------- | ---------- | | **LineString** | 1D | Roads, lanes, boundaries that can be crossed | | **MultiLineString** | 1D | Multiple separate lines (e.g., multiple lanes) | | **Polygon** | 2D | Areas, zones, regions | | **MultiPolygon** | 2D | Multiple separate areas (e.g., multiple intersections) | ### Geometry Requirements #### LineString - **Minimum**: 2 coordinate points - **Maximum**: 63 coordinate points - **Coordinates**: `[longitude, latitude]` in decimal degrees - **Range**: Longitude [-180, 180], Latitude [-180, 180] **Example:** ```json { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [-77.0366, 38.8977], [-77.0360, 38.8980], [-77.0354, 38.8983] ] }, "properties": {} }] } ``` #### MultiLineString - **Minimum**: 2 LineStrings (must have MORE than 1 line) - **Maximum**: 10 LineStrings - Each LineString follows the same rules as single LineString **Example:** ```json { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "MultiLineString", "coordinates": [ [[-77.0366, 38.8977], [-77.0360, 38.8980]], [[-77.0354, 38.8983], [-77.0348, 38.8986]] ] }, "properties": {} }] } ``` #### Polygon - **Minimum**: 4 coordinate points (first and last must be identical to close the ring) - **Maximum**: 63 coordinate points - **Ring Order**: Counter-clockwise (required) - **Holes**: Not currently supported (single ring only) **Critical Requirements:** - First and last points MUST be identical - Ring MUST be in counter-clockwise order - System validates ring closure and winding order **Example:** ```json { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [-77.0366, 38.8977], [-77.0360, 38.8980], [-77.0354, 38.8983], [-77.0366, 38.8977] ]] }, "properties": {} }] } ``` #### MultiPolygon - **Minimum**: 2 Polygons (must have MORE than 1 polygon) - **Maximum**: 10 Polygons - Each Polygon follows the same rules as single Polygon **Example:** ```json { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": [ [[[-77.0366, 38.8977], [-77.0360, 38.8980], [-77.0354, 38.8983], [-77.0366, 38.8977]]], [[[-77.0400, 38.9000], [-77.0394, 38.9003], [-77.0388, 38.9006], [-77.0400, 38.9000]]] ] }, "properties": {} }] } ``` ### Area Size Limits The system limits the geographic area covered by your geofence to ensure efficient processing: - **Maximum Coverage**: 288 geohashes at precision level 7 - **Calculation Method**: System converts your geometry to geohashes and counts coverage - **Error**: If area is too large, you'll receive `AreaSizeError` **Tip**: Use multiple smaller configurations instead of one large configuration if you exceed area limits. --- ## Trigger Conditions ### Trigger Types Trigger conditions determine when messages are sent to road users: | Trigger | Description | Valid For | | --------- | --------------- | ----------- | | **enter** | Message sent when road user enters the zone | Polygon, MultiPolygon | | **leave** | Message sent when road user exits the zone | Polygon, MultiPolygon | | **inside** | Message sent while road user is within the zone | Polygon, MultiPolygon | | **crossing** | Message sent when road user crosses the line | LineString, MultiLineString | ### Visual Guide to Trigger Conditions #### Polygon Triggers (enter, leave, inside) **ENTER Trigger** - Message sent when vehicle enters the geofence zone: <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-enter.gif" alt="ENTER Trigger Animation" width="400"> *Vehicle moves from outside into the geofence. Trigger activates at zone boundary.* **LEAVE Trigger** - Message sent when vehicle exits the geofence zone: <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-leave.gif" alt="LEAVE Trigger Animation" width="400"> *Vehicle moves from inside to outside. Trigger activates when crossing boundary.* **INSIDE Trigger** - Message sent continuously while vehicle is within the zone: <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-inside.gif" alt="INSIDE Trigger Animation" width="400"> *Vehicle moves through the geofence. Trigger remains active throughout.* **Example Use Cases:** - **ENTER**: School zone warning when vehicle enters - **LEAVE**: "Thank you for driving safely" when exiting school zone - **INSIDE**: Continuous speed limit reminder while in work zone --- #### LineString Triggers (crossing) **CROSSING Trigger** - Message sent when vehicle crosses a LineString boundary: <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-crossing.gif" alt="CROSSING Trigger Animation" width="400"> *Vehicle crosses the vertical boundary line. Trigger activates at the crossing point.* **Use Cases:** - Stop line detection at intersections - Lane boundary violations - Perimeter breach detection - Wrong-way driver alerts --- #### Multi-Geometry Triggers **MultiPolygon:** <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-multipolygon.gif" alt="MultiPolygon Trigger Animation" width="400"> *Vehicle entering Zone B demonstrates OR logic - trigger fires when entering ANY zone (A, B, or C).* - **ENTER**: Fires when vehicle enters ANY zone (1, 2, or 3) - **LEAVE**: Fires when vehicle leaves ANY zone (1, 2, or 3) - **INSIDE**: Fires when vehicle is inside ANY zone (1, 2, or 3) Multiple independent zones = OR logic for triggers **MultiLineString:** <!-- markdownlint-disable-next-line MD033 --> <img src="/static/images/etx/trigger-multilinestring.gif" alt="MultiLineString Trigger Animation" width="400"> *Vehicle crossing Line B demonstrates OR logic - trigger fires when crossing ANY line (A, B, or C).* - **CROSSING**: Fires when vehicle crosses ANY line (A, B, or C) Multiple independent lines = OR logic for crossing ### Geometry-Specific Rules **For LineString and MultiLineString:** - **ONLY** `crossing` trigger is allowed - **EXACTLY** 1 trigger condition must be specified - Any other trigger conditions will be rejected **For Polygon and MultiPolygon:** - **ONLY** `enter`, `leave`, or `inside` triggers are allowed - **NO** `crossing` trigger permitted - Can specify 1 to 3 trigger conditions - Multiple conditions are evaluated with OR logic ### Multiple Trigger Conditions When you specify multiple trigger conditions for a message, they are evaluated with **OR** logic: ```json "triggerConditions": ["enter", "inside"] ``` This message will be sent when the road user **either**: - Enters the geofence, **OR** - Is inside the geofence **For MultiLineString/MultiPolygon:** - Trigger condition is met if ANY of the geometries satisfy it - Example: For MultiPolygon with 3 polygons, "enter" triggers if user enters ANY of the 3 polygons --- ## Messages Each geofence configuration contains a `messages` array with 1-10 message objects. Each message defines: - Who receives it (road user types) - When it triggers (trigger conditions, limits) - How it's delivered (distribution type and schedule) - What content it contains (message type: generic, SAE, or ETSI) The `messages` field follows the OpenAPI schema structure where each message inherits from `MessageBase` and includes one specific message type. ### Message Fields Every message in the `messages` array must include these fields: #### `isPrivate` (boolean, required) Defines whether the message is private or public. - `true`: Published under your Vendor ID, visible only to selected vendors - `false`: Published under Public vendor ID, visible to all users - Example: `"isPrivate": false` #### `roadUserType` (array, required) Filters which types of road users receive the message. - Valid values: `"Vehicle"`, `"VulnerableRoadUser"` - `"Vehicle"`: Vehicles with a metal box (Car, Truck, Bus, etc.) - `"VulnerableRoadUser"`: Road users without protective housing (Pedestrian, Cyclist, Motorcyclist, etc.) - Minimum: 1 item, Maximum: 2 items - Example: `"roadUserType": ["Vehicle", "VulnerableRoadUser"]` #### `triggerConditions` (array, required) Defines when the message is triggered relative to the geofence. - Valid values: `"enter"`, `"leave"`, `"inside"`, `"crossing"` - At least one trigger condition must be specified - Example: `"triggerConditions": ["enter", "inside"]` - See [Trigger Conditions](#trigger-conditions) section for detailed explanation #### `limits` (object, optional) Speed and heading constraints for message triggering. The `limits` object contains `speed` and/or `heading` constraints: - **`speed`**: Defines acceptable speed range. Messages trigger when road user speed is OUTSIDE this range (either too slow OR too fast). Contains `min` and `max` values in m/s (0-160). - **`heading`**: Filters messages by direction of travel. Messages trigger only when road user heading falls within this range. Contains `min` and `max` values in degrees (0-360). Example: ```json "limits": [{ "speed": {"min": 10, "max": 20}, "heading": {"min": 45, "max": 135} }] ``` See [Speed Limits](#speed-limits) and [Heading/Direction Limits](#headingdirection-limits) sections for detailed examples. #### `distributionType` (array, optional) How the message is distributed to recipients. - Valid values: `"Targeted"`, `"Broadcast"` - Defaults to `["Targeted"]` if not specified - Example: `"distributionType": ["Targeted"]` - See [Distribution Types](#distribution-types) section for details #### `distributionSchedule` (object, optional) Controls message repetition and timing. - `repeatPeriod`: Seconds between message publications (minimum 5 seconds) - `duration`: Total duration in minutes - `startTime`: ISO 8601 UTC timestamp for scheduled start (must end with `Z`) - Example: ```json "distributionSchedule": { "repeatPeriod": 30, "duration": 3600, "startTime": "2024-06-15T12:00:00Z" } ``` **Note**: Requirements for `distributionSchedule` vary by distribution type. See [Validation Rules](#validation-rules) for details on when this field is required vs. optional. --- ### Message Types In addition to the common message fields above, each message must include **exactly one** of the following message type objects: - **`generic`**: Custom V2X messages (TAM, EVA, vendor-specific) - **`saeAlert`**: SAE J2735 Road Side Alert (RSA) - **`saeInfo`**: SAE J2735 Traveler Information Message (TIM) - **`etsiAlert`**: ETSI Decentralized Environmental Notification Message (DENM) #### Generic Custom Messages Generic messages allow you to send any custom V2X message format, including standard messages not natively supported by the API (TAM, EVA, etc.) or vendor-specific custom messages. **Required Fields:** - `messageType`: String identifying the message type (e.g., "BSM", "PSM", "TAM", "EVA", "VendorCustom") - `messageFormat`: Encoding format (e.g., "j2735", "j2735_gr", "protobuf", "json") - `payload`: Base64-encoded message content **Important**: ETX prefers `j2735` or `j2735_gr` encoding for standard V2X messages. Only vendor-specific message types should use alternative formats like JSON. --- ##### Custom Message Examples ##### Example 1: Simple JSON Custom Message **Use Case**: Send a custom vendor-specific warning in JSON format. **Original JSON Message:** ```json { "warning": "Construction ahead", "severity": "high", "distance": 500, "speed_limit": 45 } ``` **Step-by-Step Encoding:** ```bash # Step 1: Create your JSON message echo '{"warning":"Construction ahead","severity":"high","distance":500,"speed_limit":45}' > message.json # Step 2: Encode to base64 base64 message.json # Result: eyJ3YXJuaW5nIjoiQ29uc3RydWN0aW9uIGFoZWFkIiwic2V2ZXJpdHkiOiJoaWdoIiwiZGlzdGFuY2UiOjUwMCwic3BlZWRfbGltaXQiOjQ1fQo= ``` **Configuration:** ```json { "isPrivate": true, "roadUserType": ["Vehicle"], "triggerConditions": ["enter"], "distributionType": ["Targeted"], "generic": { "messageType": "VendorWarning", "messageFormat": "json", "payload": "eyJ3YXJuaW5nIjoiQ29uc3RydWN0aW9uIGFoZWFkIiwic2V2ZXJpdHkiOiJoaWdoIiwiZGlzdGFuY2UiOjUwMCwic3BlZWRfbGltaXQiOjQ1fQo=" } } ``` **Client-Side Decoding:** ```javascript // Receive message via MQTT const base64Payload = message.payload; // Decode base64 to string const jsonString = atob(base64Payload); // Parse JSON const customMessage = JSON.parse(jsonString); console.log(customMessage.warning); // "Construction ahead" console.log(customMessage.severity); // "high" ``` --- ##### Example 2: TAM (TollAdvertisementMessage) **Use Case**: Broadcast toll pricing and collection point information to vehicles approaching tolled road segments or express lanes. **TAM Message Structure (SAE J3217):** ```text TollAdvertisementMessage ::= SEQUENCE { tollChargesTable TollChargesTable, tollAdvInfo TollAdvertisementInfo OPTIONAL, ... } TollAdvertisementInfo ::= SEQUENCE { tamSequenceNum MsgCount, totalTams INTEGER (1..10, ...), tamNum INTEGER (1..10, ...), tollChargerInfo TollChargerInfo, timestamp DDateTime, tumInstructions TumInstructions OPTIONAL, tollPointMap TollPointMap, tumPublicKey OCTET STRING (SIZE(32)), ackPolicy AckPolicy, lastTollPtDataReq BOOLEAN OPTIONAL, ... } ``` > **Important:** The message structure shown above is provided as an example for illustration purposes. Always refer to the most current version of the SAE J3217 standard when creating, encoding, and decoding TollAdvertisementMessage. Message definitions, field requirements, and data types may change with standard updates. **Step-by-Step Process:** **Create TAM message using ASN.1 encoder:** ```python # Using asn1tools or similar library import asn1tools import base64 # Load SAE J3217 schema j3217 = asn1tools.compile_files(['SAE-J3217.asn']) # Create TAM message (SAE J3217) tam_message = { 'tollChargesTable': { 'tollCharges': [ { 'tollPointId': 1001, 'tollLaneSet': [1, 2, 3], # Lane IDs subject to toll 'paymentFee': { 'amount': 250, # $2.50 in cents 'currency': 840 # ISO 4217 code for USD }, 'vehicleClass': 1 # Passenger vehicle }, { 'tollPointId': 1001, 'tollLaneSet': [1, 2, 3], 'paymentFee': { 'amount': 750, # $7.50 in cents 'currency': 840 }, 'vehicleClass': 2 # Commercial vehicle } ] }, 'tollAdvInfo': { 'tamSequenceNum': 1, 'totalTams': 1, 'tamNum': 1, 'tollChargerInfo': { 'tollChargerId': 5001, 'tollPointId': 1001 }, 'timestamp': { 'year': 2024, 'month': 3, 'day': 15, 'hour': 14, 'minute': 30 }, 'tollPointMap': { 'tollPoints': [ { 'tollPointId': 1001, 'position': { 'lat': 385447190, # Latitude * 10^7 'lon': -771407890 # Longitude * 10^7 } } ] }, 'tumPublicKey': b'\x04' + b'\x00' * 31, # 32-byte public key 'ackPolicy': 0 # No acknowledgment required } } # Encode to UPER (Unaligned Packed Encoding Rules) tam_bytes = j3217.encode('TollAdvertisementMessage', tam_message, 'uper') # Encode to base64 for API tam_base64 = base64.b64encode(tam_bytes).decode('utf-8') print(tam_base64) # Result: AQEyNFZ4AAHgQA... ``` **Configuration:** ```json { "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["inside"], "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 60, "duration": 480 }, "generic": { "messageType": "TAM", "messageFormat": "j3217", "payload": "AQEyNFZ4AAHgQAIAAAEBZAAAAAEyAAAA..." } } ``` **Why Use Generic for TAM:** - TAM is not natively supported as a field in the API (only RSA, TIM, DENM) - Must be sent as generic message with proper encoding - Message type "TAM" allows receivers to identify and parse correctly --- ##### Example 3: EVA (Emergency Vehicle Alert) **Use Case**: Alert road users when emergency vehicle is approaching the geofence area. **EVA Message Structure (SAE J2735):** ```text EmergencyVehicleAlert ::= SEQUENCE { timeStamp MinuteOfTheYear OPTIONAL, id TemporaryID, rsaMsg RoadSideAlert, responseType ResponseType OPTIONAL, details EmergencyDetails, ... } ``` > **Important:** The message structure shown above is provided as an example for illustration purposes. Always refer to the most current version of the SAE J2735 standard when creating, encoding, and decoding EmergencyVehicleAlert. Message definitions, field requirements, and data types may change with standard updates. **Step-by-Step Process:** **Create EVA message:** ```python import asn1tools import base64 j2735 = asn1tools.compile_files(['SAE-J2735.asn']) # Create EVA message eva_message = { 'timeStamp': 234567, 'id': b'\xAB\xCD\xEF\x01', 'rsaMsg': { 'msgCnt': 5, 'typeEvent': 9729, # Emergency vehicle 'description': [9730, 9731] }, 'responseType': 1, # Emergency 'details': { 'eventType': 3, # Fire truck 'responseType': 1, # Code 3 (lights and sirens) 'sirenUse': 1, # Siren active 'lightsUse': 1 # Lights active } } # Encode to UPER eva_bytes = j2735.encode('EmergencyVehicleAlert', eva_message, 'uper') # Encode to base64 eva_base64 = base64.b64encode(eva_bytes).decode('utf-8') ``` **Configuration:** ```json { "isPrivate": false, "roadUserType": ["Vehicle", "VulnerableRoadUser"], "triggerConditions": ["inside"], "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 5, "duration": 60 }, "generic": { "messageType": "EVA", "messageFormat": "j2735", "payload": "AwOrzwEABSYBJgImAwEBCgEBAQ==" } } ``` **Why Use Generic for EVA:** - EVA is part of SAE J2735 but not natively supported as API field - Requires UPER encoding of the complete message structure - Broadcast distribution ensures all nearby vehicles receive alert --- ##### Example 4: J2735_gr (GeoRouted) Custom Message **Use Case**: Send vendor-specific message wrapped in GeoRoutedMsg protocol buffer. **GeoRoutedMsg Structure:** ```protobuf message GeoRoutedMsg { bytes msgBytes = 1; // Your actual message google.protobuf.Timestamp time = 2; // Timestamp optional Position position = 3; // GPS position } message Position { double latitude = 1; double longitude = 2; } ``` **Create your custom message:** ```python import base64 from google.protobuf.timestamp_pb2 import Timestamp from datetime import datetime # Your custom message (already encoded as bytes) custom_msg_bytes = b'\x01\x02\x03\x04...' # Your message content # Create GeoRoutedMsg geo_msg = GeoRoutedMsg() geo_msg.msgBytes = custom_msg_bytes geo_msg.time.GetCurrentTime() geo_msg.position.latitude = 38.8977 geo_msg.position.longitude = -77.0366 # Serialize to bytes geo_bytes = geo_msg.SerializeToString() # Encode to base64 geo_base64 = base64.b64encode(geo_bytes).decode('utf-8') ``` **Configuration:** ```json { "isPrivate": true, "roadUserType": ["Vehicle"], "triggerConditions": ["enter"], "distributionType": ["Targeted"], "generic": { "messageType": "CustomVendorMessage", "messageFormat": "j2735_gr", "payload": "CgQBAgMEEg4I4/fA..." } } ``` **When to Use j2735_gr:** - Message needs geographic routing information - Want to include precise timestamp and position - Using GeoRelevance MQTT topics (requires GeoRoutedMsg wrapper) --- ##### Custom Message Payload Encoding Reference **Encoding Process Summary:** ```text Step 1: Client - Create Message ├─ JSON: Write JSON object └─ Custom: Encode based on message format Step 2: Client - Encode to Base64 ├─ Input: Raw bytes from Step 1 ├─ Process: base64.b64encode() └─ Output: Base64 string Step 3: Client - Submit Configuration to API ├─ Set messageType (identifies message, e.g., "CustomMsg", "RSA") ├─ Set messageFormat (encoding type, e.g., "j2735", "protobuf") └─ Set payload (base64 string) Step 4: Server - Process Configuration ├─ Base64 decode payload to raw bytes ├─ Publish raw bytes to MQTT broker (no wrapping for generic messages) └─ Note: SAE/ETSI standard messages are wrapped in GeoRoutedMsg protobuf Step 5: Client - Receive and Process ├─ Subscribe to MQTT topic ├─ Receive raw bytes ├─ Parse based on messageFormat └─ Process message content ``` **Common Tools for Encoding:** ```bash # Command line base64 echo -n "your message" | base64 # Python import base64 base64.b64encode(b"your message").decode('utf-8') # JavaScript/Node.js Buffer.from("your message").toString('base64') # Java Base64.getEncoder().encodeToString("your message".getBytes()) ``` **Message Format Guidelines:** | Format | Use Case | Encoding | Preferred For | | -------- | ---------- | ---------- | ----------------- | | `j2735` | SAE standard messages | ASN.1 UPER (pre-encoded) | EVA, standard V2X | | `j3217` | C-V2X toll messages | ASN.1 UPER (pre-encoded) | TAM (Toll Advertisement) | | `j2735_gr` | Pre-encoded geo-routed messages | ASN.1 UPER (pre-encoded) | Pre-encoded UPER messages | | `json` | Vendor custom data | JSON + Base64 | Simple custom alerts | | `protobuf` | Structured custom messages | Protocol Buffers | High-performance custom | **Note:** For generic custom messages, all formats refer to pre-encoded payloads. The `j2735_gr` suffix indicates the message format, not that ETX will add GeoRoutedMsg wrapping (generic messages are published as-is). **Message Publishing Behavior:** | Message Type | Payload Processing | GeoRoutedMsg Wrapper | Position Data | Use Case | |-------------|-------------------|---------------------|---------------|----------| | **Generic (custom)** | Published as raw bytes (as-is) | ❌ NO | ❌ NO | Pre-encoded vendor messages | | **SAE (RSA/TIM)** | JSON → UPER encoding | ✅ YES | ✅ YES | Standard SAE messages | | **ETSI (DENM)** | JSON → UPER encoding | ✅ YES | ✅ YES | Standard ETSI messages | **Critical Distinction:** Generic custom messages are published as **raw bytes without any wrapper**. If your use case requires position data or GeoRoutedMsg wrapping, you must include it in your pre-encoded payload, or use the standard SAE/ETSI message types which are automatically wrapped. **Important Notes:** - Always validate your encoded message can be decoded before deployment - Test with actual V2X receivers to ensure compatibility - ETX system does NOT validate message content (only base64 format) - Clients are responsible for parsing and validating message semantics - Use Public vendorId for interoperable messages, Direct vendorId for proprietary formats - **Base64 decoding happens server-side** - clients receive already-decoded raw bytes via MQTT - Generic messages are published exactly as provided (no encoding, no wrapping, no position) #### SAE Road Side Alert (RSA) RSA messages send alerts for nearby hazards to travelers, as defined in SAE J2735 standard. **Required Fields:** - `typeEvent`: ITIS code describing the alert/danger/hazard (integer 0-65535) - `description`: Optional array of additional ITIS codes (up to 8 items) - `msgCnt`: Optional message counter (0-127, auto-populated if omitted) **Example:** ```json { "isPrivate": false, "roadUserType": ["Vehicle", "VulnerableRoadUser"], "triggerConditions": ["enter"], "distributionType": ["Targeted"], "saeAlert": { "msgCnt": 0, "typeEvent": 521, "description": [522, 523] } } ``` **ITIS Codes**: Complete set available in Volume Two of SAE J2540 standard. **Common ITIS Event Codes:** - 521: Accident - 522: Multi-vehicle accident - 771: Road construction - 1025: Severe weather > **Important:** Always refer to the most current versions of the SAE J2735 and SAE J2540 standards when implementing RSA messages. ITIS codes, message field requirements, and encoding specifications may change with standard updates. Verify your implementation against the latest published standards. **Message Standard**: Must use `"messageStandard": "sae"` in configuration. #### SAE Traveler Information Message (TIM) TIM messages send advisory and road sign information to equipped devices. **Required Fields for saeInfo:** **`dataFrames`** (array, required) - Array of data frames (1-8 items). Each data frame contains: - `frameType`: Type of frame - "unknown", "advisory", "roadSignage", or "commercialSignage" - `msgId`: Message identifier object containing either: - `furtherInfoID`: 4-character hexadecimal string (for advisory frame types) - `roadSignID`: Object with position (lat/long) and viewAngle (for roadSignage frame types) - `startTime`: Elapsed minutes of current year (0-527040) - `durationTime`: Duration in minutes (0-32000, where 32000 = forever) - `priority`: Importance level (0-7, where 7 is most important) - `regions`: Array of geographic paths (1-16 items). Auto-populated from geofence if not specified - `content`: Content object, must be one of: - `advisory`: Array of ITIS codes and text items - `workZone`: Array of work zone signs and directions - `genericSign`: Array of MUTCD signs and directions - `speedLimit`: Array of speed limits and cautions - `exitService`: Array of roadside available services **Optional Fields for saeInfo:** - `msgCnt`: Message counter (0-127, auto-populated if omitted) - `timeStamp`: Elapsed minutes of current year (0-527040) - `packetID`: Unique packet identifier (18-character hex string) - `urlB`: Base URL for additional information (max 45 characters) **Optional Fields for DataFrame:** - `startYear`: Year according to Gregorian calendar (0-4095, where 0 = unknown) - `contentNew`: Extended content for future entities (e.g., friction information) **Example:** ```json { "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["inside"], "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 30, "duration": 60, "startTime": "2024-06-15T12:00:00Z" }, "saeInfo": { "msgCnt": 5, "timeStamp": 100000, "packetID": "B343B343B343B343A5", "urlB": "http://example.com", "dataFrames": [{ "frameType": "advisory", "msgId": { "furtherInfoID": "1101" }, "startYear": 2024, "startTime": 100000, "durationTime": 120, "priority": 5, "regions": [{ "description": { "path": { "offset": { "ll": { "nodes": [ { "delta": { "node-LatLon": { "lon": -771496500, "lat": 389568100 } } }, { "delta": { "node-LatLon": { "lon": -771495000, "lat": 389569000 } } } ] } } } } }], "content": { "advisory": [ {"item": {"itis": 8960}}, {"item": {"text": "Road Work Ahead"}} ] } }] } } ``` **Message Standard**: Must use `"messageStandard": "sae"` in configuration. **Note on Regions**: The `regions` field in each DataFrame is required and defines the geographic area where the message applies. While you can explicitly specify regions using SAE J2735 GeographicalPath format (as shown in the example above with lat/lon node arrays), the system can automatically populate this field from your geofence geometry if you omit it, converting it to the proper format. **Important Limitation**: When the system auto-populates regions from your geofence, it only supports LineString and Polygon geometries. Circle regions are not supported in the automatic conversion process because the API currently only supports line strings and polygons. If you need to define circular regions, you must explicitly add them to the `regions` field in your payload using the SAE J2735 GeographicalPath format with appropriate node coordinates. > **Important:** Always refer to the most current version of the SAE J2735 standard when implementing TIM messages. Message field requirements, DataFrame structures, content types, and encoding specifications may change with standard updates. Verify your implementation against the latest published standards. #### ETSI Decentralized Environmental Notification Message (DENM) DENM messages alert road users of detected events using ITS communication technologies, as defined in ETSI standards. **Required Fields:** **`header`** (object, required) - `protocolVersion`: Must be 2 - `messageId`: Must be 1 (for DENM) - `stationId`: Station identifier (uint32) **`denm`** (object, required) - `management`: Management container with event metadata - `management.actionId`: Originating station ID and sequence number - `management.detectionTime`: Timestamp in milliseconds since start of 2004 - `management.referenceTime`: Timestamp in milliseconds since start of 2004 - `management.eventPosition`: Latitude, longitude, altitude, confidence ellipse - `management.stationType`: Type of ITS station (0-255) - `situation`: Optional situation container - `situation.informationQuality`: Quality level (0-7) - `situation.eventType`: Cause and sub-cause codes **Example:** ```json { "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["enter"], "distributionType": ["Targeted"], "etsiAlert": { "header": { "protocolVersion": 2, "messageId": 1, "stationId": 12345 }, "denm": { "management": { "actionId": { "originatingStationId": 12345, "sequenceNumber": 1 }, "detectionTime": 123456789, "referenceTime": 123456789, "eventPosition": { "latitude": 388977000, "longitude": -770366000, "positionConfidenceEllipse": { "semiMajorConfidence": 100, "semiMinorConfidence": 50, "semiMajorOrientation": 0 }, "altitude": { "altitudeValue": 10000, "altitudeConfidence": "alt-010-00" } }, "stationType": 15 }, "situation": { "informationQuality": 5, "eventType": { "ccAndScc": { "trafficCondition1": 5 } } } } } } ``` **Message Standard**: Must use `"messageStandard": "etsi"` in configuration. **Station Types** (stationType): - 0: unknown - 1: pedestrian - 5: passengerCar - 6: bus - 15: roadSideUnit **Cause Codes** (eventType): - Traffic Condition (1): trafficCondition1 with sub-cause 0-255 - Accident (2): accident2 with sub-cause 0-255 - Roadworks (3): roadworks3 with sub-cause 0-255 - Impassability (5): impassability5 with sub-cause 0-255 - Wrong Way Driving (14): wrongWayDriving14 with sub-cause 0-255 - Emergency Vehicle Approaching (95): emergencyVehicleApproaching95 with sub-cause 0-255 > **Important:** Always refer to the most current ETSI standards (ETSI EN 302 637-3) when implementing DENM messages. Message field requirements, station types, cause codes, and encoding specifications may change with standard updates. Verify your implementation against the latest published ETSI standards. ## Message Standards When creating a configuration, specify the V2X message standard: ```json { "messageStandard": "sae" } ``` **Options:** - `"sae"` - SAE J2735 (North American) standard (default) - `"etsi"` - ETSI (European) standard **Validation Rules:** - If using `saeAlert` or `saeInfo`, must specify `"messageStandard": "sae"` (or omit for default) - If using `etsiAlert`, must specify `"messageStandard": "etsi"` - Generic messages can use either standard --- ## Advanced Configuration Options ### Speed Limits Speed limits define an acceptable speed range for road users. Messages are triggered when the road user's speed falls **outside** this range (either too slow OR too fast) AND the trigger conditions are met. **Fields:** - `min`: Minimum required speed in m/s (0-160) - `max`: Maximum acceptable speed in m/s (0-160) - Constraint: `max >= min` **Example:** ```json { "limits": [{ "speed": { "min": 10, "max": 20 } }] } ``` **Behavior**: Message sends if road user speed is: - **Below 10 m/s** (too slow), OR - **Above 20 m/s** (too fast) **Use Cases:** - School zone warnings for speeding vehicles - Work zone alerts for vehicles exceeding speed limits - Slow-moving vehicle warnings in high-speed areas **Speed Conversion:** - 1 m/s = 3.6 km/h = 2.237 mph - 25 mph ≈ 11.18 m/s - 55 mph ≈ 24.59 m/s - 100 km/h ≈ 27.78 m/s ### Heading/Direction Limits Heading limits filter messages based on the road user's direction of travel (0-360 degrees). **Fields:** - `min`: Minimum heading in degrees (0-360) - `max`: Maximum heading in degrees (0-360) **Example:** ```json { "limits": [{ "heading": { "min": 45, "max": 135 } }] } ``` **Behavior**: Message sends if road user heading is between 45° and 135° (generally eastbound). **Special Case - Wrap Around:** If `min > max`, the system handles wrap-around for directions near north: ```json { "heading": { "min": 350, "max": 10 } } ``` This captures headings from 350° to 360° (clockwise) and 0° to 10° (continuing clockwise), representing ±10° around north. **Heading Reference:** - 0° = North - 90° = East - 180° = South - 270° = West **Use Cases:** - Wrong-way driver alerts (heading opposite to expected direction) - Lane-specific warnings (e.g., only for northbound traffic) - Directional construction zone alerts ### Road User Types Specify which types of road users should receive the message: **Options:** - `"Vehicle"` - Vehicles with protective housing (cars, trucks, buses) - `"VulnerableRoadUser"` - Road users without protective housing (pedestrians, cyclists, motorcyclists) **Rules:** - Must specify 1 or 2 types - Cannot specify the same type twice **Examples:** ```json { "roadUserType": ["Vehicle"] } ``` ```json { "roadUserType": ["Vehicle", "VulnerableRoadUser"] } ``` **Use Cases:** - Pedestrian crossing warnings for vehicles only - Bike lane alerts for vulnerable road users only - General hazard warnings for all road users ### Distribution Types Distribution types control how and where messages are delivered to road users through MQTT topics. Understanding the difference between Targeted and Broadcast distribution is critical for proper system integration. **Understanding Distribution Types:** **Key Concept**: The distribution type determines which MQTT topic namespace is used: - **Targeted** → Messages sent to **Direct** MQTT topics - **Broadcast** → Messages sent to **Regional** MQTT topics #### Targeted Distribution Targeted distribution sends messages **directly to specific road users** who trigger the geofence conditions. The system evaluates geofence violations in real-time and generates personalized messages for affected road users. **How It Works:** 1. Road user enters/exits/crosses geofence or meets trigger conditions 2. System evaluates speed/heading limits if defined 3. Message is generated and sent to that specific user's Direct MQTT topic 4. Only the triggered road user receives the message **MQTT Topic Structure:** ```text vzimp/1/Direct/{clientType}/{clientSubtype}/{vendorId}/{messageFormat}/{messageType}/{sessionID} ``` **Example Subscription for Targeted Messages:** ```text # Subscribe to all RSA messages for your vehicle vzimp/1/Direct/Vehicle/PassengerCar/YourVendorID|Public/j2735_gr/RSA/+ # Subscribe to all TIM messages for your vendor vzimp/1/Direct/+/+/YourVendorID/j2735_gr/TIM/+ # Subscribe to all TIM messages Public vendor vzimp/1/Direct/+/+/Public/j2735_gr/TIM/+ # Subscribe to all targeted messages (any type) vzimp/1/Direct/+/+/+/+/+/+ ``` **Characteristics:** - **Delivery**: Unicast (one-to-one) to specific session - **Latency**: Real-time, immediate upon trigger - **Overhead**: Low message volume - **Schedule**: Uses `repeatPeriod` only (default 5 seconds) - **Duration/StartTime**: Ignored for targeted messages - **Use for**: Real-time personalized warnings **Configuration Example:** ```json { "distributionType": ["Targeted"] } ``` **When to Use Targeted Distribution:** - ✅ Speed limit violations (message only for speeding vehicles) - ✅ Enter/leave zone notifications (personalized alerts) - ✅ Wrong-way driver warnings (only for violating vehicle) - ✅ Personalized safety alerts - ✅ Client-specific instructions - ✅ Low latency requirements - ✅ Minimize message overhead **Important Notes:** - Client must be connected to MQTT to receive targeted messages - Messages are not persisted (if client disconnects, messages are lost) - Requires active position reporting from road users - Default repeat period: 5 seconds - Best for dynamic, event-driven alerts --- #### Broadcast Distribution Broadcast distribution sends messages **to geographic regions** defined by 7-digit geohashes that intersect with your geofence. All road users in those regions can receive the messages. **How It Works:** 1. System calculates all 7-digit geohashes covered by your geofence 2. Messages are broadcast to Regional MQTT topics for each geohash 3. Messages are sent according to distribution schedule (repeatPeriod, duration, startTime) 4. All subscribers to those geohash regions receive the messages --- ### Coverage/Broadcast Area Calculation When you create a Broadcast configuration, the system calculates which 7-digit geohashes are affected by your geofence. Here's how it works: #### Geohash Grid System A [Geohash](https://en.wikipedia.org/wiki/Geohash) is a geocoding system that encodes geographic coordinates into a short string of letters and digits. Each character in the geohash represents increased precision, dividing the world into a hierarchical grid. You can calculate geohashes for specific coordinates using this [Geohash Calculator](https://www.movable-type.co.uk/scripts/geohash.html) tool by providing latitude, longitude, and desired precision. | Precision | Coverage Area | Use Case | | --------- | ------------- | -------- | | 6-digit | ~1.2km × 0.6km | Large area | | 7-digit | ~153m × 153m | **ETX broadcast level** | | 8-digit | ~38m × 19m | Fine-grained | #### Example: Geofence Coverage Calculation **Your Polygon Geofence:** <!-- markdownlint-disable MD033 --> <img src="/static/images/etx/geofence-polygon.png" alt="Polygon Geofence Example" width="25%" /> <!-- markdownlint-enable MD033 --> Step 1: System Calculates Overlapping Geohashes Step 2: Add Buffer Zone (Neighboring geohashes) Step 3: Broadcast to Regional Topics #### Coverage Area Size **Maximum Coverage Limit:** - **288 geohashes** at precision 7 (approximately 6.5 km²) - a 3×3 grid of 6-digit geohashes - If exceeded, split into multiple configurations **Coverage Estimation:** ```text Small Intersection: 4-6 geohashes Work Zone: 10-20 geohashes Highway Zone: 50-100 geohashes Maximum Allowed: 288 geohashes at 7-digit precision ``` #### Performance Considerations **Broadcast Message Volume:** ```text Calculation: - Geohashes affected: N - Repeat period: R seconds - Duration: D minutes - Total messages: N × (D × 60 / R) Example: - 20 geohashes - 30 second repeat period - 120 minute duration - Total: 20 × (120 × 60 / 30) = 20 × 240 = 4,800 messages Use Targeted if possible to reduce network overhead! ``` **MQTT Topic Structure:** ```text vzimp/1/Regional/{geohash}/{clientType}/{clientSubtype}/{vendorId}/{messageFormat}/{messageType}/{sessionID} ``` **Geohash Format:** Slash-delimited 8 characters (e.g., `d/j/z/f/u/4/k/g`) **Example Subscription for Broadcast Messages:** ```text # Subscribe to RSA messages in specific 7-digit geohash vzimp/1/Regional/d/j/z/f/u/4/k/+/+/+/Public/j2735_gr/RSA/+ # Subscribe to all messages in 7-digit geohash (all 32 8-digit sub-regions) vzimp/1/Regional/d/j/z/f/u/4/k/+/+/+/+/+/+/+ # Subscribe to TIM messages in 6-digit geohash area (wider area) vzimp/1/Regional/d/j/z/f/u/4/+/+/+/+/Public/j2735_gr/TIM/+ # Wide-area subscription (7-digit level only, not sub-geohashes) vzimp/1/Regional/d/j/z/f/u/4/k/-/+/+/Public/+/+/+ ``` **Characteristics:** - **Delivery**: Multicast (one-to-many) to geographic regions - **Latency**: Scheduled transmission - **Overhead**: Higher message volume (sent to all geohashes in area) - **Schedule**: Requires full `distributionSchedule` (repeatPeriod, duration, startTime optional) - **Use for**: Area-wide information and persistent warnings - **Geohash Precision**: 7-digit geohashes (approximately 150m x 150m) **Configuration Example:** ```json { "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 60, "duration": 120, "startTime": "2024-06-15T12:00:00Z" } } ``` **Distribution Schedule Fields:** **`repeatPeriod`** (integer, required) - How often to send messages in seconds (5-3600) - Example: 30 = send message every 30 seconds **`duration`** (integer, required) - How long to continue sending in minutes (1-32000) - 32000 minutes ≈ 22 days = maximum duration - Example: 120 = continue broadcasting for 2 hours **`startTime`** (string, optional) - ISO 8601 UTC timestamp when to start (must end with `Z`) - If omitted: broadcasting starts immediately - Example: "2024-06-15T07:00:00Z" = start at 7 AM UTC on June 15, 2024 - Format: `YYYY-MM-DDTHH:MM:SSZ` (the `Z` suffix indicates UTC timezone) **When to Use Broadcast Distribution:** - ✅ Construction/work zones (everyone in area needs to know) - ✅ Weather warnings (affects all road users in region) - ✅ Road closures (area-wide impact) - ✅ Persistent hazards (debris, flooding) - ✅ Traffic advisories - ✅ Physical RSU (pRSU) support **Important Notes:** - Messages are broadcast to **all 7-digit geohashes** that intersect your geofence - System automatically includes 1 extra geohash buffer around geofence to avoid gaps - Subscribers receive messages based on their location/geohash subscription - Higher network overhead (messages sent repeatedly to regions) - Best for persistent, area-wide information --- #### Hybrid Distribution (Targeted + Broadcast) You can enable **both distribution types** simultaneously for comprehensive coverage: ```json { "distributionType": ["Targeted", "Broadcast"], "distributionSchedule": { "repeatPeriod": 30, "duration": 60 } } ``` **How It Works:** 1. **Targeted component**: Sends real-time personalized messages to road users who trigger geofence 2. **Broadcast component**: Sends scheduled messages to all Regional topics for geohash regions **When to Use Both:** - ✅ Maximum coverage (reach both position-reporting and non-reporting vehicles) - ✅ Redundancy (ensure message delivery through multiple channels) - ✅ Support diverse client implementations (some monitor Direct, others monitor Regional) - ✅ Physical RSU + cloud-based processing - ✅ Critical safety messages requiring guaranteed delivery **Example Scenario:** School zone safety warning: - **Targeted**: Immediate alert when vehicle enters zone while speeding - **Broadcast**: Continuous advisory to all vehicles approaching the area --- #### Choosing the Right Distribution Type | Scenario | Recommended Type | Reason | | ---------- | ------------------ | -------- | | Speed violation in work zone | Targeted | Only speeding vehicles need warning | | Construction zone advisory | Broadcast | All vehicles in area need information | | Wrong-way driver alert | Targeted | Only specific vehicle is violating | | Weather warning (icy roads) | Broadcast | Affects entire region | | Pedestrian crossing detection | Targeted | Alert specific approaching vehicles | | Road closure information | Broadcast | Area-wide impact | | Red light violation | Targeted | Specific vehicle warning | | School zone awareness | Broadcast | All vehicles in vicinity | | Critical intersection hazard | Both | Maximum coverage | --- #### Validation Rules **Distribution Schedule Requirements:** **For Targeted Distribution:** - `distributionSchedule` is **optional** - If provided, only `repeatPeriod` is used (default: 5 seconds) - `duration` and `startTime` are **ignored** - Messages sent in real-time upon trigger **For Broadcast Distribution:** - `distributionSchedule` is **required** - Must include both `repeatPeriod` and `duration` - `startTime` is optional (defaults to immediate start) - Messages sent on schedule to all geohash regions **For Hybrid (Targeted + Broadcast):** - `distributionSchedule` is **required** (for Broadcast component) - Targeted component sends immediately, Broadcast follows schedule **Distribution Type Validation:** - Cannot specify same type twice: ❌ `["Broadcast", "Broadcast"]` - Both types allowed: ✅ `["Targeted", "Broadcast"]` - If neither specified: defaults to `["Targeted"]` --- #### Message Type Considerations **TIM (Traveler Information Message) - Special Handling:** When using Broadcast distribution with TIM messages: 1. System auto-converts your geofence to SAE J2735 `GeographicalPath` format 2. `GeographicalPath` is added to each DataFrame if not already present 3. Heading limits are added to `GeographicalPath/direction` field if defined 4. Distribution schedule parameters are added to TIM message structure **RSA (Road Side Alert):** - Works with both Targeted and Broadcast - Broadcast: Published to all geohashes affected by geofence - Use Public vendor ID for maximum reach **DENM (ETSI Messages):** - Follows same distribution rules as SAE messages - Must use `messageStandard: "etsi"` in configuration **Generic Custom Messages:** - Support both distribution types - Can use vendor-specific message formats and types --- ## Best Practices ### Configuration Design **Keep Geofences Reasonably Sized:** System enforces maximum 288 geohashes at precision 7. Break large areas into multiple configurations. Consider MEC coverage boundaries. **Use Appropriate Geometry Types:** Use LineString/MultiLineString for crossing detection (lane boundaries, stop lines). Use Polygon/MultiPolygon for area monitoring (work zones, school zones). **Choose the Right Trigger Conditions:** Use "enter" for initial warnings (entering work zone), "inside" for persistent alerts (inside school zone), "leave" for exit notifications (leaving restricted area), and "crossing" for line violations (wrong-way driving, red light). **Optimize Message Count:** Maximum 10 messages per configuration. Group related messages in single configuration. Use different trigger conditions to vary timing. ### Performance Optimization **Distribution Type Selection:** Use "Targeted" for real-time, personalized alerts. Use "Broadcast" for persistent area-wide information. Avoid unnecessary broadcast messages (higher overhead). **Speed and Heading Limits:** Only add limits when necessary for filtering. Speed limits reduce false positives. Heading limits enable directional warnings. **Configuration Activation:** Set `isActive: false` when testing. Activate only when ready for production. Use updates to modify without recreating. ### Geometry Best Practices **Polygon Winding Order:** Always use counter-clockwise winding. Tools like [geojson.io](https://geojson.io) generate correct winding. System validates and rejects clockwise polygons. **Coordinate Precision:** Use 6-7 decimal places for accuracy. 6 decimal places ≈ 0.11 meter precision. Avoid excessive precision (no benefit beyond 7 places). **Ring Closure:** First and last points must be identical. System validates closure automatically. Use array copy to ensure match: `[point1, point2, point3, point1]`. ### Message Design **Private vs Public Messages:** `isPrivate: true` makes messages only visible to your vendor's devices. `isPrivate: false` makes messages visible to all ETX users. Consider privacy implications. **ITIS Code Selection:** Use standard ITIS codes for interoperability. Reference SAE J2540 Volume Two. Test with common V2X receivers. **Distribution Schedule Tuning:** Longer `repeatPeriod` reduces message overhead. Shorter `repeatPeriod` ensures timely delivery. Balance based on urgency and area size. --- ## Common Use Cases ### Use Case 1: School Zone Speed Warning Alert drivers entering a school zone during school hours if they're speeding. **Distribution Strategy**: Use **Targeted** for immediate alerts to speeding vehicles. ```json { "name": "Lincoln Elementary School Zone", "description": "Speed warning for school zone during school hours", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [-77.0366, 38.8977], [-77.0360, 38.8980], [-77.0360, 38.8970], [-77.0366, 38.8967], [-77.0366, 38.8977] ]] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["enter"], "limits": [{ "speed": { "min": 0, "max": 11.18 } }], "distributionType": ["Targeted"], "saeAlert": { "typeEvent": 8960, "description": [8961] } }], "isActive": true } ``` **Key Features:** - Polygon geofence around school - "enter" trigger for initial warning - Speed limit: 11.18 m/s (≈25 mph) - Targeted distribution for real-time alerts - SAE RSA message with school zone ITIS codes **MQTT Subscription (Client Side):** ```text # Vehicle subscribes to Direct topic to receive targeted alerts vzimp/1/Direct/Vehicle/PassengerCar/YourVendorID|Public/j2735_gr/RSA/+ ``` **How It Works:** 1. Vehicle enters school zone geofence 2. System checks vehicle speed: if > 11.18 m/s (25 mph), trigger alert 3. RSA message sent to vehicle's Direct topic: `vzimp/1/Direct/Vehicle/PassengerCar/YourVendorID/j2735_gr/RSA/{sessionID}` 4. Only the speeding vehicle receives the alert ### Use Case 2: Construction Zone Advisory Broadcast work zone information to all vehicles approaching an intersection. ```json { "name": "Main St & 5th Ave Construction", "description": "Active road work at intersection", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [-77.0400, 38.9000], [-77.0394, 38.9003], [-77.0394, 38.8997], [-77.0400, 38.8994], [-77.0400, 38.9000] ]] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle", "VulnerableRoadUser"], "triggerConditions": ["inside"], "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 30, "duration": 480, "startTime": "2024-06-15T07:00:00Z" }, "saeInfo": { "dataFrames": [{ "frameType": "workZone", "msgId": { "furtherInfoID": "2001" }, "startTime": 100000, "durationTime": 480, "priority": 6, "content": { "workZone": [ {"item": {"itis": 771}}, {"item": {"text": "Lane Closed Ahead"}} ] } }] } }], "isActive": true } ``` **Key Features:** - Broadcast every 30 seconds for 8 hours - Both vehicles and vulnerable road users - SAE TIM message with work zone content - High priority (6) for visibility **Distribution Strategy**: Use **Broadcast** to inform all road users in the area. **MQTT Subscription (Client Side):** ```text # Vehicle subscribes to Regional topics based on current location # Assuming intersection is at geohash djzfu4kg (example) vzimp/1/Regional/d/j/z/f/u/4/k/+/+/+/Public/j2735_gr/TIM/+ ``` **How It Works:** 1. System calculates all 7-digit geohashes covered by intersection polygon 2. Every 30 seconds, TIM message is broadcast to Regional topics for each geohash: - `vzimp/1/Regional/d/j/z/f/u/4/k/g/Software/Application/Public/j2735_gr/TIM/{sessionID}` - `vzimp/1/Regional/d/j/z/f/u/4/k/u/Software/Application/Public/j2735_gr/TIM/{sessionID}` - (and all other affected geohashes) 3. All vehicles subscribed to those Regional topics receive the work zone advisory 4. Messages continue for 8 hours (480 minutes) starting at 7 AM UTC ### Use Case 3: Wrong-Way Driver Detection Detect and alert vehicles traveling in the wrong direction on a one-way street. ```json { "name": "Oak Street Wrong-Way Detection", "description": "Alert for wrong-way drivers on one-way street", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [-77.0500, 38.9100], [-77.0494, 38.9103], [-77.0488, 38.9106] ] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["crossing"], "limits": [{ "heading": { "min": 180, "max": 360 } }], "distributionType": ["Targeted"], "saeAlert": { "typeEvent": 9729, "description": [9730] } }], "isActive": true } ``` **Key Features:** - LineString geofence across street - "crossing" trigger for line violation - Heading filter: 180-360° (southbound/westbound = wrong way) - Immediate targeted alert to wrong-way driver - ITIS codes for wrong-way driving **Distribution Strategy**: Use **Targeted** to alert only the violating vehicle. **MQTT Subscription (Client Side):** ```text # Vehicle subscribes to Direct RSA messages vzimp/1/Direct/Vehicle/+/Public/j2735_gr/RSA/+ ``` **How It Works:** 1. Vehicle crosses LineString geofence 2. System checks vehicle heading: if 180-360° (wrong direction), trigger alert 3. RSA message sent immediately to vehicle's Direct topic 4. Wrong-way driver receives critical safety alert 5. Only the violating vehicle is alerted (not other drivers) ### Use Case 4: Hybrid Distribution - Critical Intersection Hazard Alert for a critical intersection with both real-time targeted warnings and area-wide broadcast. **Scenario**: Ice storm has created hazardous conditions at a major intersection. Need to: 1. Alert vehicles as they approach (Targeted) 2. Provide persistent area-wide advisory (Broadcast) ```json { "name": "Critical Intersection Ice Hazard", "description": "Icy conditions at Main St & Highway 50 intersection", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [-77.0550, 38.9150], [-77.0544, 38.9153], [-77.0544, 38.9147], [-77.0550, 38.9144], [-77.0550, 38.9150] ]] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle", "VulnerableRoadUser"], "triggerConditions": ["enter"], "distributionType": ["Targeted", "Broadcast"], "distributionSchedule": { "repeatPeriod": 20, "duration": 360, "startTime": "2024-01-15T06:00:00Z" }, "saeAlert": { "typeEvent": 1025, "description": [1026, 1027] } }], "isActive": true } ``` **Key Features:** - Both Targeted and Broadcast distribution - Immediate alerts when entering zone (Targeted) - Persistent regional broadcasts every 20 seconds (Broadcast) - Duration: 6 hours (360 minutes) - Public message (isPrivate: false) for maximum reach **Distribution Strategy**: Use **Both** for comprehensive coverage. **MQTT Subscriptions (Client Side):** **For Targeted Messages:** ```text # Subscribe to Direct RSA messages vzimp/1/Direct/Vehicle/PassengerCar/Public/j2735_gr/RSA/+ ``` **For Broadcast Messages:** ```text # Subscribe to Regional RSA messages (update geohash based on location) vzimp/1/Regional/d/j/z/f/u/5/5/+/+/+/Public/j2735_gr/RSA/+ vzimp/1/Regional/d/j/z/f/u/5/h/+/+/+/Public/j2735_gr/RSA/+ vzimp/1/Regional/d/j/z/f/u/5/k/+/+/+/Public/j2735_gr/RSA/+ ``` **How It Works:** **Targeted Component:** 1. Vehicle enters intersection geofence 2. Immediate RSA sent to vehicle's Direct topic 3. Real-time, low-latency warning **Broadcast Component:** 1. Every 20 seconds, RSA broadcast to all geohashes covering intersection 2. Messages sent to Regional topics: `vzimp/1/Regional/{geohash}/Software/Application/Public/j2735_gr/RSA/{sessionID}` 3. All vehicles in area receive advisory 4. Continues for 6 hours or until ice clears **Benefits:** - Vehicles with active position reporting get immediate Targeted alerts - Vehicles without position reporting still receive Broadcast messages - Redundant delivery ensures critical safety message reaches all road users - Supports diverse client implementations (some monitor Direct, others Regional) --- ### Use Case 5: Mid-block Pedestrian Crossing Warning Alert vehicles when pedestrians are crossing at a designated crosswalk. ```json { "name": "Main St Crosswalk", "description": "Pedestrian crossing detection", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "LineString", "coordinates": [ [-77.0600, 38.9200], [-77.0594, 38.9200] ] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["VulnerableRoadUser"], "triggerConditions": ["crossing"], "distributionType": ["Targeted"], "saeAlert": { "typeEvent": 8961 } }], "isActive": true } ``` **Key Features:** - LineString at crosswalk location - "crossing" trigger detects pedestrians - Targeted to vulnerable road users - Can be combined with vehicle warning message ### Use Case 6: OEM Client-Side Logic - Broadcast Zone Definition OEM wants vehicles to process geofence logic onboard. Send zone definition via Broadcast so vehicles can generate warnings themselves. **Scenario**: Support OEM requirement to handle geofencing onboard the vehicle, or support physical RSU deployment. ```json { "name": "Work Zone Definition for Client Logic", "description": "Broadcast work zone definition for onboard processing", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "Polygon", "coordinates": [[ [-77.0800, 38.9400], [-77.0794, 38.9403], [-77.0794, 38.9397], [-77.0800, 38.9394], [-77.0800, 38.9400] ]] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["inside"], "distributionType": ["Broadcast"], "distributionSchedule": { "repeatPeriod": 60, "duration": 32000 }, "saeInfo": { "dataFrames": [{ "frameType": "workZone", "msgId": {"furtherInfoID": "3001"}, "startTime": 100000, "durationTime": 32000, "priority": 7, "content": { "workZone": [ {"item": {"itis": 771}}, {"item": {"text": "Active Work Zone"}} ] } }] } }], "isActive": true } ``` **Key Features:** - TIM message with GeographicalPath (auto-populated from geofence) - Broadcast distribution for area-wide dissemination - Long duration (32000 minutes ≈ 22 days) - Regions field auto-populated with geofence converted to SAE J2735 format **Distribution Strategy**: Use **Broadcast** to support client-side logic and pRSUs. **MQTT Subscription (Client Side):** ```text # Vehicle subscribes to Regional TIM messages vzimp/1/Regional/d/j/z/f/u/8/0/+/+/+/Public/j2735_gr/TIM/+ ``` **How It Works:** 1. System converts geofence polygon to SAE J2735 GeographicalPath 2. TIM message broadcast every 60 seconds to Regional topics for affected geohashes 3. Vehicle receives TIM with zone definition in DataFrame.regions 4. Vehicle processes GeographicalPath onboard: - Detects when vehicle is inside the work zone - Generates warning using onboard HMI - No server-side evaluation needed 5. Supports pRSUs: TIM can be forwarded through physical roadside units **Use Cases:** - OEM requirement for onboard geofence processing - Physical RSU (pRSU) deployments - Vehicles without position reporting capability - Regulatory compliance requiring client-side logic --- ### Use Case 7: Multi-Zone Parking Alert Alert drivers entering any of several restricted parking zones. ```json { "name": "Downtown No Parking Zones", "description": "Multiple restricted parking areas", "geoFence": { "type": "FeatureCollection", "features": [{ "type": "Feature", "geometry": { "type": "MultiPolygon", "coordinates": [ [[[-77.0700, 38.9300], [-77.0694, 38.9303], [-77.0694, 38.9297], [-77.0700, 38.9294], [-77.0700, 38.9300]]], [[[-77.0720, 38.9320], [-77.0714, 38.9323], [-77.0714, 38.9317], [-77.0720, 38.9314], [-77.0720, 38.9320]]] ] }, "properties": {} }] }, "messageStandard": "sae", "messages": [{ "isPrivate": false, "roadUserType": ["Vehicle"], "triggerConditions": ["enter"], "limits": [{ "speed": { "min": 0, "max": 5.56 } }], "distributionType": ["Targeted"], "generic": { "messageType": "ParkingRestriction", "messageFormat": "json", "payload": "eyJtZXNzYWdlIjogIk5vIFBhcmtpbmcgWm9uZSJ9" } }], "isActive": true } ``` **Key Features:** - MultiPolygon for multiple zones - Speed limit filter: only slow-moving vehicles (< 5.56 m/s ≈ 12.5 mph) - Custom generic message for parking alert - Triggers on entering any zone --- ## MQTT Integration For complete details on MQTT integration, including client connection, topic subscriptions, message publishing, and troubleshooting, please refer to the comprehensive [MQTT API User Documentation](../ThingSpaceDevPortal/mqtt-api.md). The MQTT API documentation covers: - Client registration and connection workflow - Topic structure and naming conventions - Subscription patterns for Direct, Regional, and RegionalStatic namespaces - Message publishing to GeoRelevance topics - Geohash calculation and dynamic subscription updates - QoS and retain settings - Message flow examples for Targeted and Broadcast distributions - Debugging tips and common MQTT issues - Best practices for robust MQTT client implementation --- ## Troubleshooting ### Common Errors and Solutions #### Error: "Invalid area location" **Cause**: Geofence coordinates span multiple MEC coverage areas or fall outside all MECs. **Solution**: - Ensure all coordinates are within a single MEC's geographic boundary - Check coordinate precision and validity - Verify coordinates are in correct order [longitude, latitude] - Contact support if MEC boundaries are unclear #### Error: "AreaSizeError" **Cause**: Geofence area exceeds maximum coverage limit (288 geohashes at precision 7). **Solution**: - Reduce geofence size - Split into multiple smaller configurations - Remove unnecessary coordinate points - Simplify geometry #### Error: "Invalid trigger condition for geometry type" **Cause**: Using incompatible trigger condition with geometry type. **Solution**: - LineString/MultiLineString: Use only "crossing" - Polygon/MultiPolygon: Use "enter", "leave", or "inside" (not "crossing") - Check geometry type in GeoJSON - Verify trigger conditions array #### Error: "Polygon ring not closed" **Cause**: First and last coordinate points are not identical. **Solution**: ```json // Incorrect "coordinates": [[[0, 0], [1, 0], [1, 1], [0, 1]]] // Correct "coordinates": [[[0, 0], [1, 0], [1, 1], [0, 1], [0, 0]]] ``` #### Error: "Polygon ring not counter-clockwise" **Cause**: Polygon coordinates are in clockwise order instead of counter-clockwise. **Solution**: - Use GeoJSON editing tools that enforce correct winding order - Manually reverse coordinate array order - Use [geojson.io](https://geojson.io) to visualize and correct #### Error: "Configuration not found" (404) **Cause**: Using incorrect configuration ID or configuration was deleted. **Solution**: - Verify configuration ID is correct UUID - Use GET `/api/v1/application/configurations/geofence/ids` to list all IDs - Check that configuration wasn't deleted - Ensure using correct VendorID header #### Error: "Forbidden" (403) **Cause**: Authentication/authorization issue with tokens or VendorID. **Solution**: - Verify Bearer token is valid and not expired - Verify SessionToken is valid and not expired - Check VendorID header matches your registered vendor - Ensure tokens have correct permissions - Regenerate tokens if needed #### Error: "Too many requests" (429) **Cause**: Rate limit exceeded for API calls. **Solution**: - Implement exponential backoff retry logic - Reduce API call frequency - Batch operations when possible - Contact support if limits are too restrictive #### Error: "Invalid configuration" (400) **Cause**: Request body doesn't conform to schema or validation rules. **Solution**: - Validate JSON syntax - Check all required fields are present - Verify data types match schema (strings, integers, booleans) - Review field constraints (min/max values, string lengths, patterns) - Use OpenAPI spec for reference --- ## Additional Resources - [Application Configuration API Reference](/api/v1/documentation/app-config-service-api/index.html) - API documentation - [Registration API Reference](/api/v1/documentation/registration-api/index.html) - Client registration and connection






