> ## Documentation Index
> Fetch the complete documentation index at: https://mintlify.com/opentrack/opentrack/llms.txt
> Use this file to discover all available pages before exploring further.

# UDP Network

> Send raw head tracking data over network via UDP protocol

The UDP protocol transmits raw head tracking data over a network using UDP packets. This is useful for custom applications, development, external tools, or networked tracking setups.

## How It Works

The UDP protocol:

1. Creates a UDP socket bound to any local address
2. Packages 6DOF tracking data into a binary format
3. Sends UDP datagrams to a configured IP address and port
4. Data is sent in real-time with minimal overhead

### Data Format

Each UDP packet contains 48 bytes of binary data:

```cpp theme={null}
// Six double-precision floats (8 bytes each)
double data[6] = {
    headpose[Yaw],    // 0-7:   Yaw in degrees
    headpose[Pitch],  // 8-15:  Pitch in degrees  
    headpose[Roll],   // 16-23: Roll in degrees
    headpose[TX],     // 24-31: X position in cm
    headpose[TY],     // 32-39: Y position in cm
    headpose[TZ]      // 40-47: Z position in cm
};
```

**Byte order**: Native (host) byte order
**Packet size**: 48 bytes
**No header or checksum** - pure tracking data

## Use Cases

### Custom Applications

<CardGroup cols={2}>
  <Card title="Game Development" icon="gamepad">
    Integrate head tracking into your own game or application
  </Card>

  <Card title="Research Tools" icon="flask">
    Collect tracking data for analysis or machine learning
  </Card>

  <Card title="Visualization" icon="chart-line">
    Real-time visualization of head movements
  </Card>

  <Card title="Networked Tracking" icon="network-wired">
    Run OpenTrack on one PC, game on another
  </Card>
</CardGroup>

### Integration Examples

* **Unity/Unreal Engine**: Read UDP data for camera control
* **Python scripts**: Process tracking data for analysis
* **Arduino/Raspberry Pi**: Control hardware based on head position
* **OBS Studio**: Dynamic camera control for streaming
* **VR applications**: Hybrid tracking systems

## Setup Instructions

<Steps>
  <Step title="Select UDP protocol">
    In OpenTrack, go to the **Output** dropdown and select **"UDP over network"**.
  </Step>

  <Step title="Configure network settings">
    Click the settings button next to the Output dropdown.

    **Network configuration:**

    * **IP Address**: Destination IP (where to send data)
      * Default: `192.168.0.2`
      * For same computer: `127.0.0.1`
      * For network: Enter target PC's IP
    * **Port**: UDP port number
      * Default: `4242`
      * Choose any available port (1024-65535)
  </Step>

  <Step title="Start OpenTrack">
    Click **Start**. OpenTrack will:

    * Bind a UDP socket
    * Begin sending tracking data to configured address
    * Send packets at tracking input rate (typically 50-60 Hz)
  </Step>

  <Step title="Receive data in your application">
    Implement UDP receiver in your application. See code examples below.
  </Step>
</Steps>

## Configuration

### Default Settings

```ini theme={null}
IP Address: 192.168.0.2
Port: 4242
```

### Network Modes

**Localhost (same computer):**

```
IP: 127.0.0.1
Port: 4242
```

**Local network:**

```
IP: 192.168.1.100 (target PC's LAN IP)
Port: 4242
```

**Remote/Internet:**

```
IP: <public IP or hostname>
Port: <forwarded port>
```

<Warning>
  For internet transmission:

  * Configure port forwarding on router
  * Use firewall exceptions
  * Consider security implications of exposing UDP port
  * Data is sent unencrypted
</Warning>

## Receiving Data

### Python Example

```python theme={null}
import socket
import struct

# Create UDP socket
sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind(('0.0.0.0', 4242))  # Listen on all interfaces, port 4242

print("Listening for OpenTrack UDP data on port 4242...")

while True:
    # Receive packet
    data, addr = sock.recvfrom(1024)
    
    # Unpack 6 doubles (48 bytes)
    if len(data) == 48:
        yaw, pitch, roll, x, y, z = struct.unpack('dddddd', data)
        
        print(f"Yaw: {yaw:7.2f}°  Pitch: {pitch:7.2f}°  Roll: {roll:7.2f}°  "
              f"X: {x:6.1f}cm  Y: {y:6.1f}cm  Z: {z:6.1f}cm")
```

### C++ Example

```cpp theme={null}
#include <iostream>
#include <winsock2.h>
#include <ws2tcpip.h>

#pragma comment(lib, "ws2_32.lib")

struct TrackingData {
    double yaw, pitch, roll;
    double x, y, z;
};

int main() {
    WSADATA wsaData;
    WSAStartup(MAKEWORD(2, 2), &wsaData);
    
    // Create socket
    SOCKET sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
    
    // Bind to port
    sockaddr_in addr{};
    addr.sin_family = AF_INET;
    addr.sin_port = htons(4242);
    addr.sin_addr.s_addr = INADDR_ANY;
    bind(sock, (sockaddr*)&addr, sizeof(addr));
    
    std::cout << "Listening on port 4242...\n";
    
    while (true) {
        TrackingData data;
        int received = recvfrom(sock, (char*)&data, sizeof(data), 
                                0, nullptr, nullptr);
        
        if (received == sizeof(data)) {
            std::cout << "Yaw: " << data.yaw 
                     << "  Pitch: " << data.pitch 
                     << "  Roll: " << data.roll << "\n";
        }
    }
    
    closesocket(sock);
    WSACleanup();
}
```

### Unity C# Example

```csharp theme={null}
using UnityEngine;
using System;
using System.Net;
using System.Net.Sockets;
using System.Threading;

public class OpenTrackReceiver : MonoBehaviour
{
    private UdpClient udpClient;
    private Thread receiveThread;
    
    public float yaw, pitch, roll;
    public float x, y, z;
    
    void Start()
    {
        udpClient = new UdpClient(4242);
        receiveThread = new Thread(ReceiveData);
        receiveThread.Start();
    }
    
    void ReceiveData()
    {
        IPEndPoint endpoint = new IPEndPoint(IPAddress.Any, 0);
        
        while (true)
        {
            try
            {
                byte[] data = udpClient.Receive(ref endpoint);
                
                if (data.Length == 48)  // 6 doubles
                {
                    yaw   = BitConverter.ToDouble(data, 0);
                    pitch = BitConverter.ToDouble(data, 8);
                    roll  = BitConverter.ToDouble(data, 16);
                    x     = BitConverter.ToDouble(data, 24);
                    y     = BitConverter.ToDouble(data, 32);
                    z     = BitConverter.ToDouble(data, 40);
                }
            }
            catch (Exception e)
            {
                Debug.LogError("UDP Error: " + e.Message);
            }
        }
    }
    
    void OnDestroy()
    {
        receiveThread?.Abort();
        udpClient?.Close();
    }
    
    void Update()
    {
        // Use tracking data to control camera
        transform.rotation = Quaternion.Euler(-pitch, yaw, -roll);
        transform.position = new Vector3(x/100f, y/100f, z/100f);
    }
}
```

## Data Interpretation

### Coordinate System

**Rotation** (degrees):

* **Yaw**: Left (-) / Right (+)
* **Pitch**: Down (-) / Up (+)
* **Roll**: Left (-) / Right (+)

**Position** (centimeters):

* **X**: Left (-) / Right (+)
* **Y**: Down (-) / Up (+)
* **Z**: Back (-) / Forward (+)

### Typical Value Ranges

| Axis  | Typical Range | Units   |
| ----- | ------------- | ------- |
| Yaw   | -180 to +180  | degrees |
| Pitch | -90 to +90    | degrees |
| Roll  | -180 to +180  | degrees |
| X     | -30 to +30    | cm      |
| Y     | -20 to +20    | cm      |
| Z     | -20 to +20    | cm      |

<Note>
  Actual ranges depend on OpenTrack mapping settings. Users can configure custom ranges.
</Note>

## Troubleshooting

### "Can't bind socket" Error

**Cause**: Another application is using the port, or permission denied.

**Solution**:

1. Check if another program is using the port:
   * Windows: `netstat -ano | findstr :4242`
   * Linux: `netstat -tulpn | grep 4242`
2. Try a different port number
3. Run OpenTrack as administrator
4. Check firewall settings

### Not Receiving Data

**Symptoms**: Application receives no UDP packets.

**Solution**:

1. **Verify OpenTrack is sending**:
   * Check OpenTrack status shows "Running"
   * Verify settings show correct IP and port
2. **Check firewall**:
   * Add exception for OpenTrack
   * Allow inbound UDP on chosen port
   * Temporarily disable firewall to test
3. **Network connectivity**:
   * Ping target IP to verify connectivity
   * Ensure both PCs on same network
   * Check router doesn't block UDP
4. **Wireshark capture**:
   * Use Wireshark to verify packets are being sent
   * Filter: `udp.port == 4242`

### Wrong Byte Order

**Symptoms**: Values are nonsensical or extremely large.

**Cause**: Byte order mismatch between sender and receiver.

**Solution**:

* OpenTrack sends in **host byte order**
* If sender and receiver have different architectures:
  ```cpp theme={null}
  // Convert to network byte order (big-endian)
  double value = ...;
  uint64_t* ptr = (uint64_t*)&value;
  *ptr = htobe64(*ptr);  // Host to big-endian
  ```

### High Latency

**Cause**: Network congestion or WiFi interference.

**Solution**:

* Use wired Ethernet instead of WiFi
* Reduce other network traffic
* Check for packet loss with `ping` statistics
* Consider lowering OpenTrack update rate

### Packet Loss

**Symptoms**: Intermittent or choppy tracking.

**Solution**:

* UDP doesn't guarantee delivery - this is normal
* For critical applications, implement your own packet numbering
* Monitor packet loss rate
* Consider TCP if reliability is required (custom implementation needed)

## Advanced Usage

### Multiple Receivers

Send to multiple destinations:

1. Use broadcast address:
   ```
   IP: 192.168.1.255
   ```
2. Or run multiple OpenTrack instances with different ports
3. Or implement UDP forwarding in your receiver

### Custom Protocol Extension

Add metadata to packets:

```python theme={null}
# Example: Add timestamp and packet ID
import struct
import time

# Receive OpenTrack data
data, addr = sock.recvfrom(1024)
yaw, pitch, roll, x, y, z = struct.unpack('dddddd', data)

# Add metadata
timestamp = time.time()
packet_id = get_next_packet_id()

# Store or forward with metadata
extended_data = struct.pack('ddddddQI', 
    yaw, pitch, roll, x, y, z, 
    int(timestamp * 1000000), packet_id
)
```

### Data Recording

Record tracking sessions:

```python theme={null}
import csv
import time

with open('tracking_log.csv', 'w', newline='') as f:
    writer = csv.writer(f)
    writer.writerow(['timestamp', 'yaw', 'pitch', 'roll', 'x', 'y', 'z'])
    
    while True:
        data, _ = sock.recvfrom(1024)
        if len(data) == 48:
            values = struct.unpack('dddddd', data)
            writer.writerow([time.time()] + list(values))
```

## Performance Characteristics

### Bandwidth Usage

At typical 60 Hz update rate:

* **Packet size**: 48 bytes
* **Packets per second**: 60
* **Bandwidth**: \~23 Kbps
* **With IP/UDP overhead**: \~31 Kbps

Very low bandwidth - suitable for network transmission.

### Latency

**Local (127.0.0.1)**:

* Latency: \<1ms
* Perfect for same-machine applications

**LAN (wired)**:

* Latency: 1-3ms
* Excellent for networked setups

**LAN (WiFi)**:

* Latency: 5-20ms
* May have jitter and packet loss

**Internet**:

* Latency: Varies greatly
* Not recommended for real-time use

<Note>
  The UDP protocol provides maximum flexibility for custom applications and development. For standard gaming use cases, prefer FreeTrack or SimConnect protocols for better game compatibility.
</Note>
