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
Gaming
Flight Simulation
Universal
Platform-Specific
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 rotationTrackIR Compatible with TrackIR-enabled games. Some games require the official TrackIR software to be installed, even when using OpenTrack.
FlightGear Native integration with FlightGear flight simulator. Protocol : UDP socketConfiguration :// IP address: 127.0.0.1 (local) or remote IP
// Port: 5542 (default)
FSUIPC Integration with Microsoft Flight Simulator via FSUIPC. Requirements :
FSUIPC installed
Compatible Flight Simulator version
SimConnect Direct integration with Microsoft Flight Simulator. Supported Versions :UDP Output Send tracking data over UDP to any application. Data Format :struct UDPPacket {
double x, y, z; // Translation (cm)
double yaw, pitch, roll; // Rotation (degrees)
};
Configuration :
Target IP address
Target port
Optional data format customization
OSC (Open Sound Control) Send tracking data using OSC protocol. Use Cases :
VR applications
Creative tools
Custom integrations
Mouse Control mouse cursor with head movements. Axes : Typically Yaw → X, Pitch → YWine/Proton (Linux) FreeTrack support for Windows games on Linux. Requirements :
Wine or Proton
Compatible game
libevdev (Linux) Create virtual input device on Linux. IOKit/Foohid (macOS) Virtual HID device for macOS.
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
}
}
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
}
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.393701 f ;
float yaw_radians = pose [Yaw] * (M_PI / 180.0 );
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
DCS World
Arma 3
Euro/American Truck Sim
Elite Dangerous
Enable FreeTrack
Open DCS options
Go to Controls → Head Tracking
Select “Enable head tracking”
Configure OpenTrack
Select FreeTrack 2.0 protocol
Start tracking
Configure mapping curves for best response
Fine-tune
Reduce translation sensitivity for cockpit views
Increase yaw/pitch sensitivity for better awareness
Enable relative translation for comfortable operation
Install FreePIE Bridge
Required for FreeTrack support in Arma 3.
Configure Game
Enable TrackIR in game options.
Mapping Recommendations
Moderate yaw sensitivity (1:1 or 2:1)
Lower pitch sensitivity (avoid looking too far up/down)
Minimal translation (can be disorienting)
Enable FreeTrack
In game options, enable head tracking.
Recommended Settings
// Yaw: 45° input → 75° output (1.67:1)
// Pitch: 30° input → 45° output (1.5:1)
// Roll: Disabled or minimal
// Translation: Moderate for depth perception
Enable Camera Suite
Elite requires Camera Suite binding for head look.
Protocol Selection
Use TrackIR or FreeTrack protocol.
Important
Disable “Rotation Lock” in Elite’s head look settings for smooth tracking.
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
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 ();
}
};
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 ();
}
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
)
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
Game doesn't detect tracking
Verify protocol selection matches game requirements
Start OpenTrack before launching game
Check if game has head tracking enabled in settings
Try running game and OpenTrack as administrator (Windows)
Check for antivirus blocking shared memory access
Tracking works but is inverted
Use axis inversion in configuration: axis_opts ::invert_post = true ; // Invert after processing
Some games also have inversion settings.
Network protocol not connecting
Verify IP address and port
Check firewall rules
Test with localhost (127.0.0.1) first
Use Wireshark to verify packets are sent
Next Steps
Filters Add smoothing and stabilization
Mapping Curves Optimize tracking response
Configuration Advanced configuration options