HID-Protokoll des Kopf-Trackers

Das Human Interface Device-Protokoll (HID) für den Head-Tracker ist für Geräte mit Android 13 und höher verfügbar. Es ermöglicht, ein Head-Tracking-Gerät über USB oder Bluetooth mit einem Android-Gerät zu verbinden und es über das Sensor-Framework für das Android-Framework und Apps verfügbar zu machen. Dieses Protokoll wird zur Steuerung eines Audio-Virtualisierungseffekts (3D-Audio) verwendet. Auf dieser Seite werden die Begriffe Gerät und Host im Bluetooth-Sinne verwendet. Gerät bezieht sich auf das Head-Tracking-Gerät und Host auf den Android-Host.

Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass sie das HID-Protokoll für den Head-Tracker unterstützen. Weitere Informationen zur Konfiguration finden Sie in der README-Datei zu dynamischen Sensoren.

Auf dieser Seite wird vorausgesetzt, dass Sie mit den folgenden Ressourcen vertraut sind:

Struktur der obersten Ebene

Das Android-Framework identifiziert das Head-Tracker-Gerät als HID-Gerät.

Ein vollständiges Beispiel für einen gültigen HID-Deskriptor finden Sie in Anhang 1: Beispiel für einen HID-Deskriptor.

Auf der obersten Ebene ist das Head-Tracker-Gerät eine App-Sammlung mit der Seite Sensors (0x20) und der Nutzung Other: Custom (0xE1). In dieser Sammlung befinden sich mehrere Datenfelder (Eingaben) und Attribute (Funktionen).

Attribute und Datenfelder

In diesem Abschnitt werden die Attribute und Datenfelder in einer App-Sammlung eines Head-Tracker-Geräts beschrieben.

Attribut: Sensor Description (0x0308)

Das Attribut Sensor Description (0x0308) ist ein schreibgeschütztes ASCII-Stringattribut (8 Bit), das die folgenden Werte enthalten muss:

Head-Tracker-Version 1.0:

#AndroidHeadTracker#1.0

Head-Tracker-Version 2.0 (verfügbar in Android 15 oder höher), einschließlich Unterstützung für LE-Audio:

#AndroidHeadTracker#2.0#x

Das x ist eine Ganzzahl (1, 2, 3), die den unterstützten Transport angibt:

  • 1: ACL
  • 2: ISO
  • 3: ACL + ISO

Es wird kein Null-Terminator erwartet. Die Gesamtgröße dieses Attributs beträgt für Version 1.0 also 23 8-Bit-Zeichen.

Dieses Attribut dient als Unterscheidungsmerkmal, um Kollisionen mit anderen benutzerdefinierten Sensoren zu vermeiden.

Attribut: Persistent Unique ID (0x0302)

Das Attribut Persistent Unique ID (0x0302) ist ein schreibgeschütztes Array mit 16 Elementen, jeweils 8 Bit (insgesamt 128 Bit). Es wird kein Null-Terminator erwartet. Dieses Attribut ist optional.

Mit diesem Attribut können Head-Tracking-Geräte, die in Audiogeräte integriert sind, auf das Audiogerät verweisen, an das sie angeschlossen sind. Die folgenden Schemas werden unterstützt.

Eigenständiger Head-Tracker

Wenn das Attribut Persistent Unique ID (0x0302) nicht vorhanden oder auf „0“ gesetzt ist, bedeutet das, dass das Head-Tracker-Gerät nicht dauerhaft an ein Audiogerät angeschlossen ist und separat verwendet werden kann. Der Nutzer kann das Head-Tracker-Gerät beispielsweise manuell mit einem separaten Audiogerät verknüpfen.

Referenz mit Bluetooth-MAC-Adresse

Oktett 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
Wert 0 0 0 0 0 0 0 0 B T Bluetooth-MAC

In diesem Schema müssen die ersten 8 Oktette 0 sein, die Oktette 8 und 9 müssen die ASCII-Werte B bzw. T enthalten und die folgenden 6 Oktette werden als Bluetooth-MAC-Adresse interpretiert. Das Head-Tracker-Gerät gilt für alle Audiogeräte mit dieser MAC-Adresse. Diese Adresse muss die Identitätsadresse sein, auch wenn das Gerät eine zufällige MAC-Adresse verwendet, um Verbindungen herzustellen. Dual-Mode-Geräte, die über Bluetooth Classic (HID-Format v1.0) und Bluetooth LE (HID-Format v2.0) verbunden sind, müssen zwei HID-Deskriptoren mit derselben Identitätsadresse bereitstellen. Dual-Mode-Geräte mit getrennten linken und rechten Geräten müssen Bluetooth LE HID über das primäre Dual-Mode-Gerät anstelle des sekundären Geräts nur mit LE bereitstellen.

Referenz mit UUID

Wenn das höchstwertige Bit (MSB) von Oktett 8 gesetzt ist (≥0x80), wird das Feld gemäß RFC-4122als UUID interpretiert. Das entsprechende Audiogerät stellt dieselbe UUID über einen nicht angegebenen Mechanismus bereit, der für den verwendeten Transporttyp spezifisch ist. Diese UUID wird im Android-Framework registriert.

Attribut: Reporting State (0x0316)

Das Attribut Reporting State (0x0316) ist ein Attribut mit Lese-/Schreibzugriff, das die in der HID-Spezifikation definierte Standardsyntax hat. Der Host verwendet dieses Attribut, um dem Gerät mitzuteilen, welche Ereignisse gemeldet werden sollen. Es werden nur die Werte No Events (0x0840) und All Events (0x0841) verwendet.

Der Anfangswert für dieses Feld muss No Events sein und darf nur vom Host, nicht vom Gerät geändert werden.

Attribut: Power State (0x0319)

Das Attribut Power State (0x0319) ist ein Attribut mit Lese-/Schreibzugriff, das die in der HID-Spezifikation definierte Standardsyntax hat. Der Host verwendet dieses Attribut, um dem Gerät mitzuteilen, in welchem Energiestatus es sich befinden muss. Es werden nur die Werte Full Power (0x0851) und Power Off (0x0855) verwendet.

Der Anfangswert für dieses Feld wird vom Gerät festgelegt und darf nur vom Host, nicht vom Gerät geändert werden.

Attribut: Report Interval (0x030E)

Das Attribut Report Interval (0x030E) ist ein Attribut mit Lese-/Schreibzugriff, das die in der HID-Spezifikation definierte Standardsyntax hat. Der Host verwendet dieses Attribut, um dem Gerät mitzuteilen, wie oft die Datenwerte gemeldet werden sollen. Die Einheit sind Sekunden. Der gültige Bereich für diesen Wert wird vom Gerät bestimmt und mit dem Mechanismus Physical Min/Max beschrieben. Es muss eine Melderate von mindestens 50 Hz unterstützt werden. Die empfohlene maximale Melderate beträgt 100 Hz. Das minimale Meldeintervall muss also kleiner oder gleich 20 ms sein und sollte mindestens 10 ms betragen.

Attribut: Vendor-reserved LE Transport (0xF410)

Das Attribut Vendor-reserved LE Transport (0xF410) ist ein Attribut mit Lese-/Schreibzugriff, das die in der HID-Spezifikation definierte Standardsyntax hat. Der Host verwendet dieses Attribut, um den ausgewählten Transport anzugeben (ACL oder ISO). Es werden nur die Werte ACL (0xF800) und ISO (0xF801) verwendet. Beide müssen in der logischen Sammlung enthalten sein.

Dieses Attribut wird vor den Energie- oder Meldezuständen konfiguriert.

Datenfeld: Custom Value 1 (0x0544)

Das Feld Custom Value 1 (0x0544) ist ein Eingabefeld, das zum Melden der tatsächlichen Head-Tracking-Informationen verwendet wird. Es ist ein Array mit drei Elementen, das gemäß den normalen HID-Regeln für physikalische Werte interpretiert wird, wie in Abschnitt 6.2.2.7 der HID-Spezifikation beschrieben. Der gültige Bereich für jedes Element ist [-π, π] rad. Die Einheit ist immer Radiant.

Die Elemente werden interpretiert als: [rx, ry, rz], wobei [rx, ry, rz] ein Rotationsvektorist, der die Transformation vom Referenzrahmen zum Head-Rahmen darstellt. Die Größe muss im Bereich [0..π] liegen.

Der Referenzrahmen ist beliebig, aber in der Regel fest und muss rechtshändig sein. Eine geringe Abweichung ist akzeptabel. Die Head-Achsen sind:

  • X vom linken zum rechten Ohr
  • Y vom Hinterkopf zur Nase (hinten nach vorne)
  • Z vom Hals zum Oberkopf

Datenfeld: Custom Value 2 (0x0545)

Das Feld Custom Value 2 (0x0545) ist ein Eingabefeld, das zum Melden der tatsächlichen Head-Tracking-Informationen verwendet wird. Es ist ein Array mit drei Elementen mit fester Kommastelle, das gemäß den normalen HID-Regeln für physikalische Werte interpretiert wird. Die Einheit ist immer Radiant pro Sekunde.

Die Elemente werden als [vx, vy, vz] interpretiert, wobei [vx, vy, vz] ein Rotationsvektor, ist, der die Winkelgeschwindigkeit des Head-Rahmens (relativ zu sich selbst) darstellt.

Datenfeld: Custom Value 3 (0x0546)

Das Feld Custom Value 3 (0x0546) ist ein Eingabefeld, das zum Verfolgen von Diskontinuitäten im Referenzrahmen verwendet wird. Es ist eine skalare Ganzzahl mit 8 Bit. Sie muss vom Gerät jedes Mal inkrementiert werden (mit Überlauf), wenn sich der Referenzrahmen ändert, z. B. wenn der Status eines Orientierungsfilteralgorithmus, der zur Bestimmung der Orientierung verwendet wird, zurückgesetzt wurde. Dieser Wert wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert. Der physikalische Wert und die Einheiten spielen jedoch keine Rolle. Die einzige Information, die für den Host relevant ist, ist ein geänderter Wert. Um numerische Probleme im Zusammenhang mit dem Verlust der Genauigkeit bei der Umwandlung von logischen in physikalische Einheiten zu vermeiden, sollten Sie die Werte für Physical Min, Physical Max und Unit Exponent für dieses Feld auf „0“ setzen.

Berichtsstruktur

Die Gruppierung von Attributen in Berichten (durch Zuweisung von Berichts-IDs) ist flexibel. Aus Effizienzgründen empfehlen wir, die schreibgeschützten Attribute von den Attributen mit Lese-/Schreibzugriff zu trennen.

Für die Datenfelder müssen sich die Felder Custom Value 1, 2 und 3 im selben Bericht befinden und für ein bestimmtes Gerät (App-Sammlung) nur in einem Bericht enthalten sein.

Eingabeberichte senden

Das Gerät muss regelmäßig und asynchron (über HID-INPUT-Nachrichten) Eingabeberichte senden, wenn alle folgenden Bedingungen erfüllt sind:

  • Das Attribut Power State ist auf Full Power gesetzt.
  • Das Attribut Reporting State ist auf All Events gesetzt.
  • Das Attribut Reporting Interval ist nicht null.

Das Attribut Reporting Interval bestimmt, wie oft die Berichte gesendet werden. Wenn eine der oben genannten Bedingungen nicht erfüllt ist, darf das Gerät keine Berichte senden.

Vorwärts- und Abwärtskompatibilität

Das HID-Protokoll für die Erfassung von Kopfbewegungen verwendet ein Versionsverwaltungsschema, das Updates ermöglicht und gleichzeitig die Interoperabilität zwischen einem Host und einem Gerät ermöglicht, die unterschiedliche Versionen des Protokolls verwenden. Versionen für das Protokoll werden durch zwei Zahlen identifiziert: Haupt- und Nebenversion. Sie haben unterschiedliche Semantiken, wie in den folgenden Abschnitten beschrieben.

Die von einem Gerät unterstützten Versionen können anhand des Attributs Sensor Description (0x0308) ermittelt werden.

Kompatibilität von Nebenversionen

Änderungen an der Nebenversion sind abwärtskompatibel mit früheren Nebenversionen, die auf derselben Hauptversion basieren. Bei Updates der Nebenversion ignoriert der Host zusätzliche Datenfelder und Attribute. Ein Gerät mit Protokollversion 1.6 ist beispielsweise mit einem Host kompatibel, der Protokollversion 1.x unterstützt, einschließlich Version 1.5.

Kompatibilität von Hauptversionen

Bei Änderungen an Hauptversionen sind nicht abwärtskompatible Änderungen zulässig. Um mehrere Hauptversionen für die Interoperabilität mit alten und neuen Hosts zu unterstützen, können Geräte in ihren Berichtsdeskriptoren mehrere App-Sammlungen angeben. Beispiel:

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.5"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,

    HID_COLLECTION(HID_APPLICATION),
        // Feature report 12 (read-only).
        HID_REPORT_ID(12),

        // Magic value: "#AndroidHeadTracker#2.4"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

      ...

    HID_END_COLLECTION,
};

In diesem Fall kann der Host alle verschiedenen App-Sammlungen auflisten, die vom Gerät beworben werden, und anhand des Attributs Sensor Description die Protokollversionen ermitteln, die jeweils implementiert werden. Anschließend wählt er die neueste Protokollversion aus, die vom Host unterstützt wird. Nach der Auswahl verwendet der Host das ausgewählte Protokoll für die gesamte Lebensdauer der Geräteverbindung.

Anhang: Beispiel für einen HID-Deskriptor

Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Es werden die häufig verwendeten C-Makros verwendet, die unter HID-Sensornutzungen (Abschnitt 4.1) bereitgestellt werden.

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#1.0"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(23),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};

Anhang 2: Beispiel für einen HID-Deskriptor v2.0

Das folgende Beispiel zeigt einen HID-Deskriptor v2.0 für ein Gerät, das nur den Bluetooth LE ACL-Transport unterstützt.

const unsigned char ReportDescriptor[] = {
    HID_USAGE_PAGE_SENSOR,
    HID_USAGE_SENSOR_TYPE_OTHER_CUSTOM,
    HID_COLLECTION(HID_APPLICATION),
        // Feature report 2 (read-only).
        HID_REPORT_ID(2),

        // Magic value: "#AndroidHeadTracker#2.0#1"
        HID_USAGE_SENSOR_PROPERTY_SENSOR_DESCRIPTION,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(25),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // UUID.
        HID_USAGE_SENSOR_PROPERTY_PERSISTENT_UNIQUE_ID,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(0xFF),
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(16),
        HID_FEATURE(HID_CONST_VAR_ABS),

        // Feature report 1 (read/write).
        HID_REPORT_ID(1),

        // 1-bit on/off reporting state.
        HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_NO_EVENTS,
            HID_USAGE_SENSOR_PROPERTY_REPORTING_STATE_ALL_EVENTS,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 1-bit on/off power state.
        HID_USAGE_SENSOR_PROPERTY_POWER_STATE,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D4_POWER_OFF,
            HID_USAGE_SENSOR_PROPERTY_POWER_STATE_D0_FULL_POWER,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // 6-bit reporting interval, with values [0x00..0x3F] corresponding to [10ms..100ms].
        HID_USAGE_SENSOR_PROPERTY_REPORT_INTERVAL,
        HID_LOGICAL_MIN_8(0x00),
        HID_LOGICAL_MAX_8(0x3F),
        HID_PHYSICAL_MIN_8(10),
        HID_PHYSICAL_MAX_8(100),
        HID_REPORT_SIZE(6),
        HID_REPORT_COUNT(1),
        HID_USAGE_SENSOR_UNITS_SECOND,
        HID_UNIT_EXPONENT(0xD),  // 10^-3
        HID_FEATURE(HID_DATA_VAR_ABS),

        // 1-bit transport selection
        HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT,
        HID_LOGICAL_MIN_8(0),
        HID_LOGICAL_MAX_8(1),
        HID_REPORT_SIZE(1),
        HID_REPORT_COUNT(1),
        HID_COLLECTION(HID_LOGICAL),
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ACL,
            HID_USAGE_SENSOR_PROPERTY_VENDOR_LE_TRANSPORT_ISO,
            HID_FEATURE(HID_DATA_ARR_ABS),
        HID_END_COLLECTION,

        // Input report 1

        // Orientation as rotation vector (scaled to [-pi..pi] rad).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_1,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_32(0x60, 0x4F, 0x46, 0xED),  // -314159265
        HID_PHYSICAL_MAX_32(0xA1, 0xB0, 0xB9, 0x12),  // 314159265
        HID_UNIT_EXPONENT(0x08),  // 10^-8
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Angular velocity as rotation vector (scaled to [-32..32] rad/sec).
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_2,
        HID_LOGICAL_MIN_16(0x01, 0x80), // LOGICAL_MINIMUM (-32767)
        HID_LOGICAL_MAX_16(0xFF, 0x7F), // LOGICAL_MAXIMUM (32767)
        HID_PHYSICAL_MIN_8(0xE0),
        HID_PHYSICAL_MAX_8(0x20),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(16),
        HID_REPORT_COUNT(3),
        HID_INPUT(HID_DATA_VAR_ABS),

        // Reference frame reset counter.
        HID_USAGE_SENSOR_DATA_CUSTOM_VALUE_3,
        HID_LOGICAL_MIN_16(0x00, 0x00), // LOGICAL_MINIMUM (0)
        HID_LOGICAL_MAX_16(0xFF, 0x00), // LOGICAL_MAXIMUM (255)
        HID_PHYSICAL_MIN_8(0x00),
        HID_PHYSICAL_MAX_8(0x00),
        HID_UNIT_EXPONENT(0x00),  // 10^0
        HID_REPORT_SIZE(8),
        HID_REPORT_COUNT(1),
        HID_INPUT(HID_DATA_VAR_ABS),

    HID_END_COLLECTION,
};