# ULS-DeCoNet Blitz Messages Specification

> Reference for developers and integrators of ULS-DeCoNet Blitz Protocol — the ULS Devices Connection Network protocol for UART and CAN.

---

Site [llms.txt](/llms.txt)

---

> **ULSDC-PBDS051125** · version 1.0.4 · issued November 13, 2025  
> The numbered PDF is the controlled copy: [download it from the portal](https://downloads.uavlas.com).

## Introduction

### Purpose

The Blitz protocol is a specialized messaging subsystem within ULS-DeCoNet designed for high-frequency transmission of sensor measurement data from ULS positioning systems.

### Key Characteristics

- **Low Overhead**: Fixed 11-byte messages (3-byte header + 8-byte payload)
- **High Frequency**: Optimized for 100+ Hz update rates
- **Real-time**: Minimal latency from sensor to network
- **Type-safe**: Strongly typed message definitions
- **Compact**: Efficient packing of measurement data

---

## Blitz Protocol Overview

### Protocol Stack Position

```mermaid
block-beta
  columns 1
  A["Application Layer\n(Blitz Message Handling)"]
  B["IF Protocol\n(IF_CMD_BLITZ = 0x06)"]
  C["Framing Layer"]
```

### Message Flow

```mermaid
flowchart LR
  Sensor --> Measurement --> BlitzEncode["Blitz Encode"] --> IFLayer["IF Layer"] --> Transport --> Receiver
  BlitzEncode --> MsgId["MSG_ID + Data"] --> BlitzCmd["IF_CMD_BLITZ"] --> Frame["UART/CAN Frame"]
```

---

## Message Format

### IF Layer Packet Structure

```mermaid
packet-beta
title "Blitz IF Layer Packet"
0-7: "CMD 0x06 (1 byte)"
8-15: "SRC_LID (1 byte)"
16-31: "MSG_ID (2 bytes)"
32-95: "DATA (8 bytes)"
```

**Total Size**: 11 bytes

**Fields**:
- **CMD**: Always 0x06 (IF_CMD_BLITZ)
- **SRC_LID**: Source device ID
- **MSG_ID**: Blitz message type identifier (uint16_t, little-endian)
- **DATA**: Message-specific payload (8 bytes maximum)

### Common Data Structure

```cpp
typedef struct {
    uint8_t src_lid;         // Source logical ID
    uint16_t msg_id;         // Message type ID
    union {
        uint8_t data[8];     // Raw data access
        // Specific message structures
        __uls_blitz_xcg1_rx_raw_measurmet xcg1_rx_raw_measurmet;
        __uls_blitz_msg_rx_raw_measurmet rx_raw_measurmet;
        __uls_blitz_msg_rx_pos_in_tx_frame rx_pos_in_tx_frame;
        __uls_blitz_msg_rx_mrx rx_mrx;
        __uls_blitz_msg_rx_dacs rx_dacs;
    };
} __blitz_common_data_struct;
```

---

## Message Types

### Message Type Summary

| Message ID | Name | Size | Description |
|------------|------|------|-------------|
| 0x0010 | XCG1_RX_RAW_MEASURMENT | 8 bytes | Legacy raw measurement (±30°) |
| 0x0011 | RX_RAW_MEASURMENT | 8 bytes | Raw measurement (±180°) |
| 0x0012 | RX_POS_IN_TX_FRAME | 8 bytes | 3D position in TX frame |
| 0x0013 | RX_MRX | 6 bytes | Multi-receiver info |
| 0x0014 | RX_DACS | 8 bytes | DACS with continuous yaw |
| 0x0020 | GIMU_MEASUREMENT | 8 bytes | Ground IMU data |
| 0x0030 | XX_TXC_IMU_OVERRIDE | 8 bytes | IMU override command |
| 0x0031 | XX_TXC_MAG_OVERRIDE | 8 bytes | Magnetometer override |

---

### XCG1 Raw Measurement (0x0010)

**Legacy message for XCopter Generation 1 systems**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;    // Status bits
    uint8_t d;         // Distance in 10 cm increments
    int16_t aX;        // Angle X: [-30000 = -30°, +30000 = +30°]
    int16_t aY;        // Angle Y: [-30000 = -30°, +30000 = +30°]
    int8_t oF;         // Sensor offset Forward (cm)
    int8_t oR;         // Sensor offset Right (cm)
} __uls_blitz_xcg1_rx_raw_measurmet;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags (see section 5) |
| d | uint8_t | 0-255 | Distance in 10 cm steps (0-25.5m) |
| aX | int16_t | -30000 to +30000 | Angle X proportional to ±30° |
| aY | int16_t | -30000 to +30000 | Angle Y proportional to ±30° |
| oF | int8_t | -128 to +127 | Forward offset in cm |
| oR | int8_t | -128 to +127 | Right offset in cm |

#### Angular Conversion

```c
float angle_x_deg = (aX * 30.0f) / 30000.0f;
float angle_y_deg = (aY * 30.0f) / 30000.0f;
```

#### Distance Conversion

```c
float distance_m = d * 0.1f;  // d in 10 cm steps
```

---

### Raw Measurement (0x0011)

**Standard raw measurement with full angular range**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;      // Status bits
    uint8_t counter;     // Message counter
    int16_t aX;          // Angle X: [-180° to +180°]
    int16_t aY;          // Angle Y: [-180° to +180°]
    uint16_t distance;   // Distance measurement
} __uls_blitz_msg_rx_raw_measurmet;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags |
| counter | uint8_t | 0-255 | Packet counter (wraps at 256) |
| aX | int16_t | -32768 to +32767 | Angle X scaled to ±180° |
| aY | int16_t | -32768 to +32767 | Angle Y scaled to ±180° |
| distance | uint16_t | 0-65535 | Distance in cm (0-655.35m) |

#### Angular Conversion

```c
float angle_x_deg = (aX * 180.0f) / 32767.0f;
float angle_y_deg = (aY * 180.0f) / 32767.0f;
```

**Precision**: ~0.0055° per LSB

#### Distance Conversion

```c
float distance_m = distance * 0.01f;  // distance in cm
```

---

### Position in TX Frame (0x0012)

**3D position vector in transmitter coordinate frame**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;    // Status bits
    uint8_t counter;   // Message counter
    int16_t posX;      // X position (cm)
    int16_t posY;      // Y position (cm)
    int16_t posZ;      // Z position (cm)
} __uls_blitz_msg_rx_pos_in_tx_frame;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags |
| counter | uint8_t | 0-255 | Packet counter |
| posX | int16_t | -32768 to +32767 | X position in cm (±327.68m) |
| posY | int16_t | -32768 to +32767 | Y position in cm (±327.68m) |
| posZ | int16_t | -32768 to +32767 | Z position in cm (±327.68m) |

#### Coordinate System

```
Transmitter Frame:
         +Z (up)
          |
          |
          +------- +X (forward)
         /
        /
      +Y (right)
```

#### Position Conversion

```c
float pos_x_m = posX * 0.01f;  // cm to m
float pos_y_m = posY * 0.01f;
float pos_z_m = posZ * 0.01f;
```

**Range**: ±327.68 meters  
**Resolution**: 1 cm

---

### MRX Information (0x0013)

**Multi-Receiver Extension data**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;      // Status bits
    uint8_t counter;     // Message counter
    int16_t yaw;         // Yaw angle [-180° to +180°]
    uint16_t distance;   // Distance in cm
} __uls_blitz_msg_rx_mrx;
```

**Total Size**: 6 bytes (2 bytes unused in 8-byte payload)

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags (bit 5: MRX_OK) |
| counter | uint8_t | 0-255 | Packet counter |
| yaw | int16_t | -32768 to +32767 | Yaw angle scaled to ±180° |
| distance | uint16_t | 0-65535 | Distance in cm |

#### Angular Conversion

```c
float yaw_deg = (yaw * 180.0f) / 32767.0f;
```

#### Usage

MRX data is calculated from measurements of a secondary receiver, providing:
- Relative yaw orientation
- Distance between receivers
- Enhanced positioning accuracy

---

### DACS Information (0x0014)

**Directional Angle Calculation System with continuous yaw tracking**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;         // Status bits
    uint8_t counter;        // Message counter
    int16_t yaw;            // Yaw angle [-180° to +180°]
    uint16_t distance;      // Distance in cm
    int16_t yawContinuous;  // Continuous yaw (±32768°)
} __uls_blitz_msg_rx_dacs;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags |
| counter | uint8_t | 0-255 | Packet counter |
| yaw | int16_t | -32768 to +32767 | Wrapped yaw ±180° |
| distance | uint16_t | 0-65535 | Distance in cm |
| yawContinuous | int16_t | -32768 to +32767 | Unwrapped yaw ±32768° |

#### Angular Conversion

**Wrapped Yaw**:
```c
float yaw_deg = (yaw * 180.0f) / 32767.0f;  // -180 to +180°
```

**Continuous Yaw**:
```c
float yaw_continuous_deg = yawContinuous;  // Direct degree value
```

#### Usage

DACS provides:
- Standard wrapped yaw for heading
- Continuous yaw for counting full rotations
- Useful for applications requiring rotation tracking beyond ±180°

**Example**:
```
Rotation: 450° clockwise
yaw = 90° (wrapped to ±180° range)
yawContinuous = 450°
```

---

### Ground IMU Measurement (0x0020)

**Inertial Measurement Unit data from ground transmitter**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t status;    // Status bits
    uint8_t counter;   // Message counter
    int16_t roll;      // Roll angle [-180° to +180°]
    int16_t pitch;     // Pitch angle [-180° to +180°]
    int16_t yaw;       // Yaw angle [-180° to +180°]
} __uls_blitz_msg_gimu_data;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| status | uint8_t | 0-255 | Status flags (bit 4: GU_IMU_OK) |
| counter | uint8_t | 0-255 | Packet counter |
| roll | int16_t | -32768 to +32767 | Roll scaled to ±180° |
| pitch | int16_t | -32768 to +32767 | Pitch scaled to ±180° |
| yaw | int16_t | -32768 to +32767 | Yaw scaled to ±180° |

#### Angular Conversion

```c
float roll_deg = (roll * 180.0f) / 32767.0f;
float pitch_deg = (pitch * 180.0f) / 32767.0f;
float yaw_deg = (yaw * 180.0f) / 32767.0f;
```

**Precision**: ~0.0055° per LSB

#### Orientation Convention

```
Euler Angles (Tait-Bryan convention):
  Roll:  Rotation about X-axis (forward)
  Pitch: Rotation about Y-axis (right)
  Yaw:   Rotation about Z-axis (up)
```

---

### IMU Override (0x0030)

**Override IMU data on ground transmitter**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t control;   // Control bits
    uint8_t counter;   // Message counter
    int16_t roll;      // Override roll [-180° to +180°]
    int16_t pitch;     // Override pitch [-180° to +180°]
    int16_t yaw;       // Override yaw [-180° to +180°]
} __uls_blitz_msg_xx_txc_imu_override;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| control | uint8_t | 0-255 | Control flags (see below) |
| counter | uint8_t | 0-255 | Packet counter |
| roll | int16_t | -32768 to +32767 | Override roll value |
| pitch | int16_t | -32768 to +32767 | Override pitch value |
| yaw | int16_t | -32768 to +32767 | Override yaw value |

#### Control Bits

| Bit | Name | Description |
|-----|------|-------------|
| 0 | SET_ROLL | Enable roll override |
| 1 | SET_PITCH | Enable pitch override |
| 2 | SET_YAW | Enable yaw override |
| 3-7 | Reserved | Must be 0 |

#### Usage

```c
// Override only yaw
msg.control = (1 << 2);  // SET_YAW
msg.yaw = (45.0f * 32767.0f) / 180.0f;  // 45 degrees
msg.roll = 0;    // Ignored (SET_ROLL not set)
msg.pitch = 0;   // Ignored (SET_PITCH not set)
```

**Purpose**: 
- External orientation source (e.g., motion capture, GNSS heading)
- Calibration and testing
- Compass-less operation

---

### Magnetometer Override (0x0031)

**Override magnetometer data on ground transmitter**

#### Structure

```cpp
typedef struct __attribute__((packed)) {
    uint8_t control;   // Control bits
    uint8_t counter;   // Message counter
    int16_t mx;        // Magnetic field X component
    int16_t my;        // Magnetic field Y component
    int16_t mz;        // Magnetic field Z component
} __uls_blitz_msg_xx_txc_mag_override;
```

**Total Size**: 8 bytes

#### Field Descriptions

| Field | Type | Range | Description |
|-------|------|-------|-------------|
| control | uint8_t | 0-255 | Control flags (see below) |
| counter | uint8_t | 0-255 | Packet counter |
| mx | int16_t | -32768 to +32767 | X magnetic field component |
| my | int16_t | -32768 to +32767 | Y magnetic field component |
| mz | int16_t | -32768 to +32767 | Z magnetic field component |

#### Control Bits

| Bit | Name | Description |
|-----|------|-------------|
| 0 | SET_OVERRIDE | Enable magnetometer override |
| 1-7 | Reserved | Must be 0 |

#### Magnetic Field Conversion

```c
float mx_norm = (mx * 1.0f) / 32767.0f;  // Normalized [-1.0, +1.0]
float my_norm = (my * 1.0f) / 32767.0f;
float mz_norm = (mz * 1.0f) / 32767.0f;
```

**Purpose**:
- External magnetometer source
- Magnetic interference compensation
- Indoor operation with external compass

---

## Status Bits

### Common Status Flags

Most Blitz messages include a status byte with these flags:

| Bit | Flag Name | Description |
|-----|-----------|-------------|
| 0 | CARRIER_OK | IR carrier signal detected |
| 1 | SQ_OK | Signal quality acceptable |
| 2 | POS_OK | Position successfully estimated |
| 3 | VEL_OK | Velocity successfully estimated |
| 4 | GU_IMU_OK | Ground Unit IMU operational |
| 5 | MRX_OK | MRX message received from secondary receiver |
| 6 | MRXDATA_OK | MRX yaw and distance calculated successfully |
| 7 | Reserved | Reserved for future use |

### Status Checking

```c
bool check_measurement_valid(uint8_t status) {
    // Check minimum required flags
    if (!(status & (1 << 0))) return false;  // No carrier
    if (!(status & (1 << 1))) return false;  // Poor signal quality
    if (!(status & (1 << 2))) return false;  // No position
    return true;
}

bool has_velocity(uint8_t status) {
    return (status & (1 << 3)) != 0;
}

bool has_mrx_data(uint8_t status) {
    return ((status & (1 << 5)) != 0) && ((status & (1 << 6)) != 0);
}
```

---

## Data Encoding

### Integer Scaling

**Angular Data** (±180° range):
```
int16_t_value = (angle_degrees / 180.0) * 32767.0
angle_degrees = (int16_t_value / 32767.0) * 180.0
```

**Position Data** (cm):
```
int16_t_value = position_cm
position_m = int16_t_value * 0.01
```

**Distance Data** (cm):
```
uint16_t_value = distance_cm
distance_m = uint16_t_value * 0.01
```

### Byte Order

**All multi-byte fields use little-endian byte order**

Example (int16_t = 0x1234):
```
Memory layout: [0x34, 0x12]
                 LSB   MSB
```

### Packing

All structures use `__attribute__((packed))` to ensure:
- No padding bytes inserted by compiler
- Exact memory layout as defined
- Network transmission compatibility

---

## Usage Examples

### Sending Blitz Message

```cpp
// Prepare raw measurement
__uls_blitz_msg_rx_raw_measurmet msg;

msg.status = calculate_status_bits();
msg.counter = packet_counter++;
msg.aX = (int16_t)((angle_x_deg / 180.0f) * 32767.0f);
msg.aY = (int16_t)((angle_y_deg / 180.0f) * 32767.0f);
msg.distance = (uint16_t)(distance_m * 100.0f);  // m to cm

// Send via interface
interface->ifSendBLITZ(
    ULS_BLITZ_MSG_ID_RX_RAW_MEASURMENT,
    (uint8_t*)&msg,
    sizeof(msg)
);
```

### Receiving Blitz Message

```cpp
// Set callback
interface->ifclbkBlitzReceived = &onBlitzReceived;

void onBlitzReceived(ULSBusInterface *iface) {
    uint16_t msg_id = iface->ifRxPacket->blitz.msg_id;
    uint8_t *data = iface->ifRxPacket->blitz.data;
    
    switch(msg_id) {
        case ULS_BLITZ_MSG_ID_RX_RAW_MEASURMENT: {
            __uls_blitz_msg_rx_raw_measurmet *msg =
                (__uls_blitz_msg_rx_raw_measurmet*)data;
            
            // Check status
            if (!(msg->status & 0x07)) break;  // Check flags 0,1,2
            
            // Extract data
            float ax = (msg->aX / 32767.0f) * 180.0f;
            float ay = (msg->aY / 32767.0f) * 180.0f;
            float dist = msg->distance * 0.01f;
            
            // Process measurement
            process_measurement(ax, ay, dist);
            break;
        }
        
        case ULS_BLITZ_MSG_ID_GIMU_MEASUREMENT: {
            __uls_blitz_msg_gimu_data *msg =
                (__uls_blitz_msg_gimu_data*)data;
            
            float roll = (msg->roll / 32767.0f) * 180.0f;
            float pitch = (msg->pitch / 32767.0f) * 180.0f;
            float yaw = (msg->yaw / 32767.0f) * 180.0f;
            
            update_orientation(roll, pitch, yaw);
            break;
        }
        
        // Handle other message types...
    }
}
```

### IMU Override Example

```cpp
// Override yaw angle from external source
void send_yaw_override(float yaw_deg) {
    __uls_blitz_msg_xx_txc_imu_override msg;
    
    msg.control = (1 << 2);  // Enable yaw override only
    msg.counter = counter++;
    msg.roll = 0;
    msg.pitch = 0;
    msg.yaw = (int16_t)((yaw_deg / 180.0f) * 32767.0f);
    
    interface->ifSendBLITZ(
        ULS_BLITZ_MSG_ID_XX_TXC_IMU_OVERRIDE,
        (uint8_t*)&msg,
        sizeof(msg)
    );
}
```

---

## Performance Considerations

### Update Rates

**Typical Rates**:
- Raw measurements: 100-200 Hz
- Position data: 100 Hz
- IMU data: 100-200 Hz
- MRX data: 50-100 Hz

### Bandwidth Usage

**Per Message**:
- UART (COBS mode): ~13 bytes (11 data + 2 overhead)
- CAN: 1 frame (11 bytes in 8-byte frame + header)

**Example Load** (100 Hz, UART @ 115200 bps):
```
Bandwidth = 100 Hz × 13 bytes × 8 bits = 10.4 kbps
Utilization = 10.4 / 115.2 = 9%
```

### Latency

**End-to-end latency**:
- Sensor → Encoding: <1 ms
- Transmission: 0.1-1 ms (depends on transport)
- Reception → Callback: <1 ms
- **Total**: 1-3 ms typical

---

**Copyright © 2020-2026 UAVLAS <www.uavlas.com>**  
**License**: GNU Lesser General Public License v3.0+

---
