Skip to main content
Output protocols send processed head tracking data to games and applications. OpenTrack supports numerous output formats for compatibility with virtually any software.

Protocol Module System

Protocols are plugins that implement the IProtocol interface:
struct OTR_API_EXPORT IProtocol : module_status_mixin {
    // Called 250 times per second with processed pose data
    virtual void pose(const double* pose, const double* raw) = 0;
    
    // Return game name or placeholder text
    virtual QString game_name() = 0;
};

Module Selection

struct module_settings {
    bundle b { make_bundle("modules") };
    value<QString> protocol_dll { b, "protocol-dll", "freetrack" };
};

Available Protocols

FreeTrack

Industry-standard protocol used by many games.Supported Games:
  • Arma series
  • DCS World
  • War Thunder
  • Euro Truck Simulator
  • American Truck Simulator
  • Many others
Interface: Shared memoryData Format: 6DOF position and rotation

TrackIR

Compatible with TrackIR-enabled games.
Some games require the official TrackIR software to be installed, even when using OpenTrack.

Protocol Data Flow

The pipeline calls the protocol at 250Hz:
void pipeline::logic() {
    // ... tracking and processing ...
    
    // Final output
    libs.pProtocol->pose(value, raw);
    
    {
        QMutexLocker foo(&mtx);
        m_output_pose = value;  // Mapped/processed
        m_raw_6dof = raw;       // Raw tracker data
    }
}
1

Receive Data

Protocol receives both processed and raw pose data:
void pose(const double* pose, const double* raw) {
    // pose[0-5]: Fully processed output
    // raw[0-5]:  Raw tracker data
}
2

Transform Data

Convert OpenTrack format to protocol-specific format:
// OpenTrack uses cm and degrees
// Some protocols need different units
float x_inches = pose[TX] * 0.393701f;
float yaw_radians = pose[Yaw] * (M_PI / 180.0);
3

Transmit

Send data via protocol-specific method:
  • Shared memory (FreeTrack, TrackIR)
  • Network socket (UDP, FlightGear)
  • System APIs (Mouse, SimConnect)

FreeTrack Protocol Details

FreeTrack is the most commonly used protocol:

Shared Memory Structure

struct FTData {
    uint32_t DataID;           // Packet identifier
    int32_t CamWidth;          // Camera width
    int32_t CamHeight;         // Camera height
    
    // Raw head pose (1:1 with head)
    float Yaw;                 // +left / -right
    float Pitch;               // +up / -down  
    float Roll;                // +left / -right
    float X;                   // +right / -left
    float Y;                   // +up / -down
    float Z;                   // +forward / -backward
    
    // Raw values
    float RawX, RawY, RawZ;
    float RawYaw, RawPitch, RawRoll;
    
    // Extra data
    float X1, Y1, X2, Y2, X3, Y3, X4, Y4;
};

Memory Mapping

HANDLE hMapFile = OpenFileMappingA(
    FILE_MAP_WRITE,
    false,
    "FT_SharedMem"
);

FTData* pMemData = (FTData*)MapViewOfFile(
    hMapFile,
    FILE_MAP_WRITE,
    0, 0,
    sizeof(FTData)
);

UDP Protocol Details

UDP output is highly flexible:

Basic Configuration

struct UDPSettings {
    QString ip_address { "127.0.0.1" };
    int port { 5005 };
};

Sending Data

void UDPProtocol::pose(const double* data, const double*) {
    QByteArray datagram;
    QDataStream stream(&datagram, QIODevice::WriteOnly);
    
    for (int i = 0; i < 6; i++) {
        stream << data[i];
    }
    
    socket.writeDatagram(datagram, address, port);
}
UDP is connectionless and doesn’t guarantee delivery. This is acceptable for tracking data since new data arrives every 4ms.

Game-Specific Setup

1

Enable FreeTrack

  1. Open DCS options
  2. Go to Controls → Head Tracking
  3. Select “Enable head tracking”
2

Configure OpenTrack

  1. Select FreeTrack 2.0 protocol
  2. Start tracking
  3. Configure mapping curves for best response
3

Fine-tune

  • Reduce translation sensitivity for cockpit views
  • Increase yaw/pitch sensitivity for better awareness
  • Enable relative translation for comfortable operation

Protocol Dialog

Protocols can provide configuration dialogs:
struct OTR_API_EXPORT IProtocolDialog : public BaseDialog {
    virtual void register_protocol(IProtocol *protocol) = 0;
    virtual void unregister_protocol() = 0;
};
The dialog receives the active protocol instance for live updates.

Creating Custom Protocols

1

Implement IProtocol

class MyProtocol : public IProtocol {
    void pose(const double* data, const double* raw) override {
        // Send data to your application
    }
    
    QString game_name() override {
        return "My Game";
    }
    
    module_status initialize() override {
        // Set up connection
        return status_ok();
    }
};
2

Handle Threading

Protocol methods are called from the tracking thread:
void pose(const double* data, const double* raw) override {
    // Don't block here!
    // Queue data and send from separate thread if needed
    
    QMutexLocker lock(&mutex);
    latest_data = Pose(data);
    data_available.wakeOne();
}
3

Register Plugin

class MyProtocolMeta : public Metadata {
    QString name() override { return "My Protocol"; }
    QIcon icon() override { return QIcon(":/icon.png"); }
};

OPENTRACK_DECLARE_PROTOCOL(
    MyProtocol,
    MyProtocolDialog,
    MyProtocolMeta
)

Performance Considerations

The protocol runs at 250Hz, so efficiency matters:
Never block in the pose() method. Network I/O, file operations, and heavy computation should use separate threads.

Good Practice

void pose(const double* data, const double*) override {
    // Fast: Copy data
    memcpy(shared_memory, data, sizeof(double) * 6);
    
    // Fast: Update atomic values
    latest_pose.store(Pose(data));
    
    // Fast: Lock-free queue
    data_queue.enqueue(data);
}

Avoid

void pose(const double* data, const double*) override {
    // Slow: Blocking network I/O
    socket.write(data, sizeof(double) * 6);
    socket.waitForBytesWritten();  // DON'T DO THIS
    
    // Slow: File I/O
    file.write(data, sizeof(double) * 6);
    file.flush();
}

Troubleshooting

  1. Verify protocol selection matches game requirements
  2. Start OpenTrack before launching game
  3. Check if game has head tracking enabled in settings
  4. Try running game and OpenTrack as administrator (Windows)
  5. Check for antivirus blocking shared memory access
Use axis inversion in configuration:
axis_opts::invert_post = true;  // Invert after processing
Some games also have inversion settings.
  1. Verify IP address and port
  2. Check firewall rules
  3. Test with localhost (127.0.0.1) first
  4. Use Wireshark to verify packets are sent
  1. OpenTrack runs at 250Hz - check CPU usage
  2. Disable logging if enabled
  3. Reduce filter complexity
  4. Check for USB bandwidth issues (optical trackers)

Next Steps

Filters

Add smoothing and stabilization

Mapping Curves

Optimize tracking response

Configuration

Advanced configuration options