HID-Protokoll des Kopf-Trackers

Das HID-Protokoll (Human Interface Device) für die Kopf-Tracker-Funktion ist für Geräte mit Android 13 und höher verfügbar. Es ermöglicht, ein Kopf-Tracking-Gerät über USB oder Bluetooth mit einem Android-Gerät zu verbinden und über das Sensors-Framework für das Android-Framework und Apps verfügbar zu machen. Dieses Protokoll wird zum Steuern 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 Gerät für die Head-Tracking-Funktion und Host auf den Android-Host.

Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass das HID-Protokoll für die Kopf-Tracker unterstützt wird. Ausführlichere Informationen zur Konfiguration finden Sie in der README-Datei zu dynamischen Sensoren.

Auf dieser Seite wird davon ausgegangen, 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 Other: Custom-Nutzung (0xE1). Innerhalb dieser Sammlung befinden sich mehrere Datenfelder (Eingaben) und Eigenschaften (Funktionen).

Properties und Datenfelder

In diesem Abschnitt werden die Eigenschaften und Datenfelder in einer Anwendungssammlung eines Head-Tracker-Geräts beschrieben.

Property: 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

Version 2.0 des Head-Trackers (verfügbar ab Android 15), die LE Audio unterstützt:

#AndroidHeadTracker#2.0#x

x ist eine Ganzzahl (1, 2, 3), die den Support-Transport angibt:

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

Es wird kein Null-Terminator erwartet. Das bedeutet, dass die Gesamtgröße dieser Eigenschaft in Version 1.0 23 8-Bit-Zeichen beträgt.

Diese Eigenschaft dient als Unterscheidungsmerkmal, um Kollisionen mit anderen benutzerdefinierten Sensoren zu vermeiden.

Property: Persistent Unique ID (0x0302)

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

Mit dieser Property können Geräte zur Kopf-Tracker-Funktion, die in Audiogeräte integriert sind, auf das Audiogerät verweisen, an dem sie angebracht sind. Die folgenden Schemas werden unterstützt.

Eigenständiger Headtracker

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

Verweis 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. Es wird davon ausgegangen, dass der Head-Tracker für alle Audiogeräte mit dieser MAC-Adresse gilt. 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 werden, müssen zwei HID-Deskriptoren mit derselben Identitätsadresse bereitstellen. Dual-Mode-Geräte mit separaten linken und rechten Geräten müssen Bluetooth LE HID über das primäre Dual-Mode-Gerät und nicht über das sekundäre Gerät, das nur LE unterstützt, bereitstellen.

Verweis mit UUID

Wenn das MSB (Most Significant Bit) von Oktett 8 gesetzt ist (≥0x80), wird das Feld als UUID interpretiert, wie in RFC-4122 angegeben. Das entsprechende Audiogerät stellt dieselbe UUID bereit, die über einen nicht näher bezeichneten Mechanismus, der für den verwendeten Transporttyp spezifisch ist, im Android-Framework registriert wird.

Property: Reporting State (0x0316)

Die Eigenschaft „Reporting State“ (0x0316) ist eine Lese-/Schreibeigenschaft mit der Standardsyntax, die in der HID-Spezifikation definiert ist. Der Host verwendet diese Eigenschaft, um dem Gerät mitzuteilen, welche Ereignisse gemeldet werden sollen. Es werden nur die Werte „Keine Ereignisse“ (0x0840) und „Alle Ereignisse“ (0x0841) verwendet.

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

Property: Energiezustand (0x0319)

Die Eigenschaft „Power State“ (0x0319) ist eine Lese-/Schreibeigenschaft mit der Standardsyntax, die in der HID-Spezifikation definiert ist. Der Host verwendet diese Property, um dem Gerät mitzuteilen, in welchem Stromversorgungsstatus es sich befinden muss. Nur die Werte „Volle Leistung“ (0x0851) und „Ausschalten“ (0x0855) werden verwendet.

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

Property: Report Interval (0x030E)

Die Eigenschaft „Berichtsintervall“ (0x030E) ist eine Lese-/Schreibeigenschaft mit der Standardsyntax, die in der HID-Spezifikation definiert ist. Der Host verwendet diese Property, um dem Gerät mitzuteilen, wie oft es seine Datenwerte melden soll. Die Einheit sind Sekunden. Der gültige Bereich für diesen Wert wird durch das 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 Mindestmeldeintervall muss also kleiner oder gleich 20 ms sein und sollte mindestens 10 ms betragen.

Property: Vendor-reserved LE Transport (0xF410)

Die Eigenschaft „Vendor-reserved LE Transport“ (0xF410) ist eine Lese-/Schreibeigenschaft, die die in der HID-Spezifikation definierten Standardsmantiken hat. Der Host verwendet diese Property, um den ausgewählten Transport (ACL oder ISO) anzugeben. Es werden nur die Werte ACL (0xF800) und ISO (0xF801) verwendet. Beide müssen in der logischen Sammlung enthalten sein.

Diese Property wird vor den Stromversorgungs- oder Berichtsstatus konfiguriert.

Datenfeld: Benutzerdefinierter Wert 1 (0x0544)

Das Feld „Benutzerdefinierter Wert 1“ (0x0544) ist ein Eingabefeld, das zum Melden der tatsächlichen Head-Tracking-Informationen verwendet wird. Es handelt sich um ein Array mit drei Elementen, das gemäß den normalen HID-Regeln für physische 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 Einheiten sind immer Bogenmaß.

Die Elemente werden als [rx, ry, rz] interpretiert, wobei [rx, ry, rz] ein Rotationsvektor ist, der die Transformation vom Referenzrahmen zum Kopfrahmen darstellt. Der Betrag muss im Bereich [0..π] liegen.

Der Referenzrahmen ist beliebig, aber in der Regel fest und muss rechtshändig sein. Eine geringe Abweichung ist zulässig. Die Achsen des Kopfes sind:

  • X vom linken zum rechten Ohr
  • Y – von der Rückseite des Kopfes bis zur Nase (von hinten nach vorn)
  • Z vom Hals bis zum oberen Ende des Kopfes

Datenfeld: Benutzerdefinierter Wert 2 (0x0545)

Das Feld „Benutzerdefinierter Wert 2“ (0x0545) ist ein Eingabefeld, das zum Melden der tatsächlichen Head-Tracking-Informationen verwendet wird. Es handelt sich um ein 3-Element-Festkomma-Array, das gemäß den normalen HID-Regeln für physische Werte interpretiert wird. Die Einheiten sind immer Radian/Sekunde.

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

Datenfeld: Benutzerdefinierter Wert 3 (0x0546)

Das Feld „Benutzerdefinierter Wert 3“ (0x0546) ist ein Eingabefeld, das zum Erfassen von Diskontinuitäten im Referenzrahmen verwendet wird. Es handelt sich um eine skalare 8-Bit-Ganzzahl. Sie muss vom Gerät jedes Mal, wenn sich der Bezugsrahmen ändert, inkrementiert werden (mit Überlauf). Das ist beispielsweise der Fall, wenn der Status eines Orientierungsfilteralgorithmus, der zur Bestimmung der Ausrichtung verwendet wird, zurückgesetzt wurde. Dieser Wert wird gemäß den normalen HID-Regeln für physische Werte interpretiert. Der physische Wert und die Einheiten spielen jedoch keine Rolle. Die einzige für den Host relevante Information ist ein geänderter Wert. Um numerische Probleme im Zusammenhang mit dem Verlust von Genauigkeit bei der Umrechnung von logischen in physische Einheiten zu vermeiden, empfiehlt es sich, die Werte für „Physischer Mindestwert“, „Physischer Maximalwert“ und „Einheitenexponent“ für dieses Feld auf null zu setzen.

Berichtsstruktur

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

Die Felder „Benutzerdefinierter Wert 1“, „Benutzerdefinierter Wert 2“ und „Benutzerdefinierter Wert 3“ müssen im selben Bericht enthalten sein und für ein bestimmtes Gerät (App-Sammlung) nur in einem Bericht vorkommen.

Eingabeberichte senden

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

  • Die Eigenschaft „Power State“ (Betriebsart) ist auf „Full Power“ (Volle Leistung) festgelegt.
  • Die Property „Berichtstatus“ ist auf „Alle Ereignisse“ festgelegt.
  • Die Eigenschaft „Berichtszeitraum“ ist ungleich null.

Mit der Property „Berichtsintervall“ wird festgelegt, 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 den Head-Tracker verwendet ein Versionsschema, das Updates ermöglicht und gleichzeitig die Interoperabilität zwischen einem Host und einem Gerät, die unterschiedliche Versionen des Protokolls verwenden, gewährleistet. Versionen des Protokolls werden durch zwei Zahlen identifiziert, die Haupt- und die Nebenversionsnummer, die unterschiedliche Semantiken haben, wie in den folgenden Abschnitten beschrieben.

Die von einem Gerät unterstützten Versionen können anhand der Eigenschaft „Sensor Description“ (0x0308) ermittelt werden.

Kompatibilität mit 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 Eigenschaften. Ein Gerät, das Protokollversion 1.6 verwendet, ist beispielsweise mit einem Host kompatibel, der Protokollversion 1.x unterstützt, einschließlich Version 1.5.

Kompatibilität mit Hauptversionen

Nicht abwärtskompatible Änderungen sind bei Änderungen an Hauptversionen zulässig. Um mehrere Hauptversionen für die Interoperabilität mit alten und neuen Hosts zu unterstützen, können Geräte mehrere App-Sammlungen in ihren Berichtsdeskriptoren 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 aufzählen, die vom Gerät beworben werden. Er kann die Eigenschaft „Sensor Description“ (Sensorbeschreibung) untersuchen, um die Protokollversionen zu ermitteln, die jeweils implementiert werden, und dann die neueste Protokollversion auswählen, die vom Host unterstützt wird. Wenn ausgewählt, funktioniert der Host mit dem einzelnen Protokoll, das für die Lebensdauer der Geräteverbindung ausgewählt wurde.

Anhang: Beispiel für einen HID-Deskriptor

Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Dabei werden die häufig verwendeten C-Makros verwendet, die in HID Sensor Usages (Abschnitt 4.1) angegeben sind.

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 für Version 2.0

Das folgende Beispiel zeigt einen HID-Deskriptor der Version 2.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,
};