> ## 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.

# ITracker Interface

> Complete API reference for implementing tracker plugins

## Overview

The `ITracker` interface is the base class for all tracker plugins. Trackers are responsible for capturing head pose data from various sources including cameras, hardware devices, and network sources.

## Interface Definition

```cpp theme={null}
struct ITracker
{
    ITracker();
    virtual ~ITracker();
    
    // Core methods (must implement)
    virtual module_status start_tracker(QFrame* frame) = 0;
    virtual void data(double *data) = 0;
    
    // Optional methods
    virtual bool center();
    
    // Helper methods
    static module_status status_ok();
    static module_status error(const QString& error);
    
    // Deleted methods (non-copyable)
    ITracker(const ITracker&) = delete;
    ITracker& operator=(const ITracker&) = delete;
};
```

## Methods

### start\_tracker()

```cpp theme={null}
virtual module_status start_tracker(QFrame* frame) = 0;
```

Initializes the tracker and optionally sets up video preview display.

<ParamField path="frame" type="QFrame*">
  Qt frame widget for displaying video preview. Can be `nullptr` if no preview needed.
</ParamField>

<ResponseField name="return" type="module_status">
  Returns `status_ok()` on success, or `error(message)` on failure.
</ResponseField>

**Description:**

* Called once when tracking is started
* Initialize hardware, open cameras, start threads
* Set up video preview widget if frame is provided
* Return error status if initialization fails

**Example implementation:**

<CodeGroup>
  ```cpp tracker-easy/tracker-easy.cpp theme={null}
  module_status Tracker::start_tracker(QFrame* video_frame)
  {
      // Validate configuration
      if (iSolver != cv::SOLVEPNP_P3P && 
          iSolver != cv::SOLVEPNP_AP3P && 
          iModel.size() == 3)
      {
          return error("Solver not supported, use P3P or AP3P");
      }

      // Create camera
      camera = video::make_camera(iSettings.camera_name);
      if (!camera)
          return error(QStringLiteral("Can't open camera %1")
                       .arg(iSettings.camera_name));

      // Set up video preview widget
      widget = std::make_unique<video_widget>(video_frame);
      layout = std::make_unique<QHBoxLayout>(video_frame);
      layout->setContentsMargins(0, 0, 0, 0);
      layout->addWidget(&*widget);
      video_frame->setLayout(&*layout);
      video_frame->show();

      // Start processing thread
      iTicker.setTimerType(Qt::PreciseTimer);
      SetFps(iSettings.cam_fps);
      iTicker.moveToThread(&iThread);
      connect(&iTicker, SIGNAL(timeout()), SLOT(Tick()), 
              Qt::DirectConnection);
      iTicker.connect(&iThread, SIGNAL(started()), SLOT(start()));
      
      iFpsTimer.start();
      iThread.setObjectName("EasyTrackerThread");
      iThread.setPriority(QThread::HighPriority);
      iThread.start();

      return status_ok();
  }
  ```

  ```cpp protocol-ft/ftnoir_protocol_ft.cpp theme={null}
  module_status freetrack::initialize()
  {
      // Check shared memory
      if (!shm.success())
          return error(tr("Can't load freetrack memory mapping"));

      // Set up protocols
      if (auto ret = set_protocols(); !ret.is_ok())
          return ret;

      // Initialize shared memory data
      pMemData->data.DataID = 1;
      pMemData->data.CamWidth = 100;
      pMemData->data.CamHeight = 250;
      store(pMemData->GameID2, 0);

      // Start dummy process if needed
      if (s.used_interface != settings::enable_freetrack)
          start_dummy();

      return status_ok();
  }
  ```
</CodeGroup>

### data()

```cpp theme={null}
virtual void data(double *data) = 0;
```

Provides current head pose data to the tracking pipeline.

<ParamField path="data" type="double*">
  Output array of 6 doubles: `[TX, TY, TZ, Yaw, Pitch, Roll]`. Must be filled by implementation.
</ParamField>

<Warning>
  This method is called approximately 250 times per second. Keep it fast and non-blocking.
</Warning>

**Description:**

* Called at \~250Hz from the tracking pipeline thread
* Fill the data array with current pose
* Use mutex protection for thread-safe access
* Don't perform heavy computation here
* Use a background thread for processing

**Coordinate system:**

* `TX` (data\[0]): X translation in centimeters (left: negative, right: positive)
* `TY` (data\[1]): Y translation in centimeters (down: negative, up: positive)
* `TZ` (data\[2]): Z translation in centimeters (backward: negative, forward: positive)
* `Yaw` (data\[3]): Rotation around Y axis in degrees (left: negative, right: positive)
* `Pitch` (data\[4]): Rotation around X axis in degrees (down: negative, up: positive)
* `Roll` (data\[5]): Rotation around Z axis in degrees (left: negative, right: positive)

**Example implementation:**

<CodeGroup>
  ```cpp tracker-easy/tracker-easy.cpp theme={null}
  void Tracker::data(double* aData)
  {
      if (ever_success.load(std::memory_order_relaxed))
      {
          // Thread-safe data access
          QMutexLocker l(&iDataLock);
          
          // Auto-center if no recent tracking
          if (iSettings.iAutoCenter && 
              iBestTime.elapsed_ms() > iSettings.iAutoCenterTimeout)
          {
              // Reset to center
              FeedData(aData, iCenterAngles, iCenterTranslation);
          }
          else
          {
              // Provide current tracking data
              FeedData(aData, iBestAngles, iBestTranslation);
          }
      }
  }

  // Helper function to populate data array
  void FeedData(double* aData, const cv::Vec3d& aAngles, 
                const cv::Vec3d& aTranslation)
  {
      aData[Yaw] = aAngles[1];
      aData[Pitch] = aAngles[0];
      aData[Roll] = aAngles[2];
      aData[TX] = aTranslation[0];
      aData[TY] = aTranslation[1];
      aData[TZ] = aTranslation[2];
  }
  ```
</CodeGroup>

### center()

```cpp theme={null}
virtual bool center();
```

Called when the user requests to center/reset tracking.

<ResponseField name="return" type="bool">
  * `true`: Use current pose as center (identity transform)
  * `false`: Use default center behavior (current pose as offset)
</ResponseField>

**Description:**

* Called from UI thread when user presses center hotkey
* Store current pose as center reference
* Return `false` to use default centering (recommended)
* Return `true` to make identity the center pose

**Example implementation:**

```cpp theme={null}
bool Tracker::center()
{
    QMutexLocker l(&iDataLock);
    
    // Store current pose as center offset
    iCenterTranslation = iBestTranslation;
    iCenterAngles = iBestAngles;
    
    // Use default center behavior
    return false;
}
```

### status\_ok() and error()

```cpp theme={null}
static module_status status_ok();
static module_status error(const QString& error);
```

Helper methods to create module status objects.

<ParamField path="error" type="const QString&">
  Error message to display to user
</ParamField>

**Example:**

```cpp theme={null}
if (!device->open())
    return error("Failed to open device");

return status_ok();
```

## Dialog Interface

```cpp theme={null}
struct ITrackerDialog : public BaseDialog
{
    virtual void register_tracker(ITracker *tracker);
    virtual void unregister_tracker();
};
```

### register\_tracker()

```cpp theme={null}
virtual void register_tracker(ITracker *tracker);
```

Receives a pointer to the active tracker instance.

<ParamField path="tracker" type="ITracker*">
  Pointer to the running tracker instance. Can be `nullptr`.
</ParamField>

**Description:**

* Called from UI thread when tracker starts
* Store pointer for runtime interaction (if needed)
* Can query tracker state or update preview
* Default implementation does nothing

**Example:**

```cpp theme={null}
void Dialog::register_tracker(ITracker *tracker)
{
    this->tracker = static_cast<Tracker*>(tracker);
    // Can now interact with tracker if needed
}
```

### unregister\_tracker()

```cpp theme={null}
virtual void unregister_tracker();
```

Called when tracker is about to be destroyed.

**Description:**

* Clear any stored tracker pointers
* Stop accessing tracker data
* Default implementation does nothing

**Example:**

```cpp theme={null}
void Dialog::unregister_tracker()
{
    tracker = nullptr;
}
```

## Complete Example

<CodeGroup>
  ```cpp my-tracker.h theme={null}
  #pragma once
  #include "api/plugin-api.hpp"
  #include <QMutex>
  #include <QThread>

  class MyTracker : public QObject, ITracker
  {
      Q_OBJECT
  public:
      MyTracker();
      ~MyTracker() override;
      
      module_status start_tracker(QFrame* frame) override;
      void data(double* data) override;
      bool center() override;
      
  private slots:
      void process_frame();
      
  private:
      QThread worker_thread;
      QMutex data_mutex;
      
      double current_pose[6] = {};
      double center_pose[6] = {};
      bool tracking_active = false;
  };

  class MyTrackerDialog : public ITrackerDialog
  {
      Q_OBJECT
  public:
      MyTrackerDialog();
      void register_tracker(ITracker* t) override;
      void unregister_tracker() override;
  };

  class MyTrackerMetadata : public Metadata
  {
      Q_OBJECT
      QString name() override { return tr("My Tracker"); }
      QIcon icon() override { return QIcon(":/images/icon.png"); }
  };
  ```

  ```cpp my-tracker.cpp theme={null}
  #include "my-tracker.h"

  MyTracker::MyTracker()
  {
      // Move processing to worker thread
      moveToThread(&worker_thread);
  }

  MyTracker::~MyTracker()
  {
      worker_thread.quit();
      worker_thread.wait();
  }

  module_status MyTracker::start_tracker(QFrame* frame)
  {
      // Initialize hardware/resources
      if (!initialize_device())
          return error("Failed to initialize device");
      
      // Start worker thread
      worker_thread.start();
      tracking_active = true;
      
      return status_ok();
  }

  void MyTracker::data(double* data)
  {
      QMutexLocker lock(&data_mutex);
      
      if (tracking_active)
      {
          // Copy current pose with centering applied
          for (int i = 0; i < 6; i++)
              data[i] = current_pose[i] - center_pose[i];
      }
  }

  bool MyTracker::center()
  {
      QMutexLocker lock(&data_mutex);
      
      // Store current pose as center reference
      for (int i = 0; i < 6; i++)
          center_pose[i] = current_pose[i];
      
      return false;
  }

  void MyTracker::process_frame()
  {
      // Heavy processing in worker thread
      double new_pose[6];
      
      // ... compute pose from sensor/camera ...
      
      // Update current pose thread-safely
      QMutexLocker lock(&data_mutex);
      for (int i = 0; i < 6; i++)
          current_pose[i] = new_pose[i];
  }

  // Register plugin
  OPENTRACK_DECLARE_TRACKER(MyTracker, MyTrackerDialog, 
                            MyTrackerMetadata)
  ```
</CodeGroup>

## Threading Best Practices

<AccordionGroup>
  <Accordion title="Use Background Threads for Processing">
    ```cpp theme={null}
    class Tracker : public QObject, ITracker
    {
        QThread processing_thread;
        QTimer frame_timer;
        
        void start_tracker(QFrame* frame) override
        {
            frame_timer.moveToThread(&processing_thread);
            connect(&frame_timer, SIGNAL(timeout()), 
                    SLOT(process()), Qt::DirectConnection);
            processing_thread.start();
        }
    };
    ```
  </Accordion>

  <Accordion title="Protect Shared Data with Mutexes">
    ```cpp theme={null}
    class Tracker : ITracker
    {
        QMutex pose_mutex;
        double pose_data[6];
        
        void data(double* data) override
        {
            QMutexLocker lock(&pose_mutex);
            memcpy(data, pose_data, sizeof(pose_data));
        }
        
        void update_pose(const double* new_pose)
        {
            QMutexLocker lock(&pose_mutex);
            memcpy(pose_data, new_pose, sizeof(pose_data));
        }
    };
    ```
  </Accordion>

  <Accordion title="Use Atomic Flags for Simple State">
    ```cpp theme={null}
    class Tracker : ITracker
    {
        std::atomic<bool> tracking_successful{false};
        
        void processing_thread()
        {
            if (compute_pose_succeeded)
                tracking_successful.store(true, 
                    std::memory_order_relaxed);
        }
        
        void data(double* data) override
        {
            if (tracking_successful.load(std::memory_order_relaxed))
            {
                // Provide pose data
            }
        }
    };
    ```
  </Accordion>
</AccordionGroup>

## See Also

* [Protocol Interface](/api/protocol-interface) - Output tracking data
* [Filter Interface](/api/filter-interface) - Process tracking data
* [Metadata](/api/metadata) - Plugin metadata requirements
