HID-Protokoll für Kopf-Tracker

Das Human Interface Device (HID)-Protokoll für den Head Tracker, das für Geräte verfügbar ist mit Android 13 und höher, ermöglicht mit einem Android-Gerät über USB oder Bluetooth und das Android-Framework und die Apps können über die sensors-Framework. Dieses Protokoll wird für steuern ein Audio-Virtualizer-Effekt (3D-Audio). Auf dieser Seite werden die Begriffe Gerät und host im Sinne von Bluetooth, wobei device für das Head-Tracker-Gerät steht und host steht für den Android-Host.

Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass sie die Unterstützung für das HID-Protokoll des Kopf-Trackers. Nähere Informationen über finden Sie in der README-Datei für dynamische Sensoren

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

Struktur der obersten Ebene

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

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

Auf der obersten Ebene ist das Kopf-Tracker-Gerät eine App-Sammlung mit dem Sensors Seite (0x20) und die Other: Custom-Nutzung (0xE1). In diesem Sammlung besteht aus mehreren Datenfeldern (Eingaben) und Eigenschaften (Features).

Eigenschaften und Datenfelder

In diesem Abschnitt werden die Eigenschaften und Datenfelder in einer Anwendung beschrieben. Sammlung eines Kopf-Tracker-Geräts.

Eigenschaft: Sensorbeschreibung (0x0308)

Das Attribut „Sensorbeschreibung“ (0x0308) ist ein schreibgeschützter ASCII-String (8-Bit) die die folgenden Werte enthalten muss:

Kopf-Tracker-Version 1.0:

#AndroidHeadTracker#1.0

Kopf-Tracker-Version 2.0 (verfügbar mit Android 15 oder ) – einschließlich Unterstützung für LE Audio:

#AndroidHeadTracker#2.0#x

x ist eine Ganzzahl (1, 2, 3), die den Supporttransport angibt:

  • 1: ACL
  • 2: ISO
  • 3: ACL und ISO

Es wird kein Nullterminator erwartet. Das bedeutet, dass die Gesamtgröße dieser Property ist 23 8-Bit-Zeichen für Version 1.0.

Diese Eigenschaft dient als Diskriminator, um Kollisionen mit anderen Sensoren.

Property: Persistente eindeutige ID (0x0302)

Das Attribut der persistenten eindeutigen ID (0x0302) ist ein schreibgeschütztes Array aus 16 -Elemente mit jeweils 8 Bit (insgesamt 128 Bit). Es wird kein Nullabschlusszeichen erwartet. Dieses ist optional.

Mit dieser Eigenschaft können in Audio integrierte Headtracking-Geräte verwendet werden. Geräte so, dass sie auf das Audiogerät verweisen, an das sie angeschlossen sind. Die folgenden Schemas werden unterstützt.

Eigenständiger Kopf-Tracker

Wenn das Attribut „Persistente eindeutige ID“ (0x0302) nicht vorhanden oder auf „Alle“ festgelegt ist bedeutet dies, dass der Kopf-Tracker nicht dauerhaft mit einem Audiogerät und kann separat verwendet werden, z. B. indem der Nutzer den Kopf-Tracker 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

Bei diesem Schema müssen die ersten acht Oktette 0 sein, die Oktette 8 und 9 müssen die ASCII-Werte B bzw. T und die folgenden 6 Oktette sind als Bluetooth-MAC-Adresse interpretiert, unter der Annahme, dass das Kopf-Tracker-Gerät gilt für alle Audiogeräte mit dieser MAC-Adresse. Diese Adresse muss die Identitätsadresse, auch wenn das Gerät eine zufällige MAC-Adresse verwendet, um Verbindungen. Dual-Mode-Geräte, die sich über Bluetooth Classic verbinden (HID-Format v1.0) und Bluetooth LE (HID-Format v2.0) müssen zwei HID verfügbar machen. Deskriptoren mit derselben Identitätsadresse. Dual-Mode-Geräte mit getrennten das linke und das rechte Gerät muss Bluetooth LE HID über die primäre Dual- Gerät im Modus statt auf das sekundäre Gerät nur für LE.

Referenz mit UUID

Wenn das höchstwertige Bit (MSB) von Oktett 8 (≥0x80) festgelegt wird, wird das Feld als UUID interpretiert, wie in RFC-4122. Die dasselbe Audiogerät dieselbe UUID auf, die unter des Android-Frameworks mithilfe eines nicht angegebenen Mechanismus für die Art des verwendeten Transportmittels.

Property: Meldestatus (0x0316)

Das Attribut für den Berichtsstatus (0x0316) ist ein Lese-/Schreibattribut, das die Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese um dem Gerät anzugeben, welche Ereignisse gemeldet werden sollen. Nur die Werte „Nein“ Es werden die Ereignisse „0x0840“ und „Alle Ereignisse“ (0x0841) verwendet.

Der Anfangswert dieses Feldes darf „No Events“ (Keine Ereignisse) sein. vom Gerät, nur vom Host verändert.

Property: Leistungszustand (0x0319)

Das Attribut „Leistungsstatus“ (0x0319) ist ein Lese-/Schreibattribut, das die Eigenschaft Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese , um dem Gerät anzugeben, in welchem Zustand das Gerät sich befinden muss. Nur die werden die Werte „Volle Power“ (0x0851) und „Ausschalten“ (0x0855) verwendet.

Der Anfangswert für dieses Feld wird vom Gerät bestimmt und darf auf keinen Fall vom Gerät, nur vom Host verändert.

Property: Berichtsintervall (0x030E)

Das Attribut „Berichtsintervall“ (0x030E) ist eine Lese-/Schreibeigenschaft mit folgendem Wert: Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese festgelegt, wie oft das Gerät seine Datenmesswerte melden soll. Die Einheiten sind Sekunden. Der gültige Bereich für diesen Wert wird vom Gerät bestimmt und unter Verwendung des Mechanismus der physischen Min/Max beschrieben. Mindestens 50 Hz Reporting-Rate muss unterstützt werden und die empfohlene maximale Reporting-Rate ist 100 Hz. Daher muss das minimale Berichtsintervall kleiner oder gleich sein auf 20 ms. Der Wert sollte größer oder gleich 10 ms sein.

Property: Vom Anbieter reservierter LE Transport (0xF410)

Das Attribut für den vom Anbieter reservierten LE-Transport (0xF410) ist ein Lese-/Schreibattribut die die in der HID-Spezifikation definierte Standardsemantik hat. Host verwendet diese Eigenschaft, um den ausgewählten Transport anzugeben (ACL oder ISO). Nur die Die Werte ACL (0xF800) und ISO (0xF801) werden verwendet und müssen beide angegeben werden in der logischen Sammlung.

Diese Eigenschaft wird vor dem Ein/Aus- oder Berichterstellungsstatus konfiguriert.

Datenfeld: Benutzerdefinierter Wert 1 (0x0544)

Das Feld Benutzerdefinierter Wert 1 (0x0544) ist ein Eingabefeld für die Informationen zur Erfassung von Kopfbewegungen. Es ist ein Array mit drei Elementen, die normalen HID-Regeln für physikalische Werte, wie in Abschnitt 6.2.2.7 die HID-Spezifikation. Der gültige Bereich für jedes Element ist [-hol, pi] rad. Einheiten sind immer Radianten.

Die Elemente werden so interpretiert: [rx, ry, rz], wobei [rx, ry, rz] ein Rotationsvektor, die die Transformation vom Referenzframe zum Headframe darstellt. Die Magnitude muss im Bereich [0–...] liegen.

Der Referenzrahmen ist willkürlich, er ist aber in der Regel unveränderlich und muss Rechtshänder. Eine geringe Abweichung ist akzeptabel. Die Kopfachsen sind:

  • X vom linken Ohr nach rechts
  • Y vom Kopf bis zur Nase (hinten nach vorne)
  • Z vom Hals bis zur Spitze des Kopfes

Datenfeld: Benutzerdefinierter Wert 2 (0x0545)

Das Feld für den benutzerdefinierten Wert 2 (0x0545) ist ein Eingabefeld für die Informationen zur Erfassung von Kopfbewegungen. Ein Festpunktarray mit 3 Elementen, wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert. Die Einheiten sind immer Radianten/Sekunde.

Die Elemente werden so interpretiert: [vx, vy, vz], wobei [vx, vy, vz] ein Rotationsvektor, die die Winkelgeschwindigkeit des Headframes (relativ zu sich selbst) darstellt.

Datenfeld: Benutzerdefinierter Wert 3 (0x0546)

Das Feld für den benutzerdefinierten Wert 3 (0x0546) ist ein Eingabefeld für das Tracking Diskontinuitäten im Bezugsrahmen. Es ist eine skalare Ganzzahl 8 Bit Größe. Sie muss jedes Mal vom Gerät erhöht werden (mit Wraparound), wenn der Bezugsrahmen geändert wird, z. B. wenn ein Ausrichtungsfilter-Algorithmus anhand derer ermittelt wurde, dass der Status der Ausrichtung zurückgesetzt wurde. Dieser Wert ist wird gemäß den normalen HID-Regeln für physikalische Werte interpretiert. Sie können jedoch der physikalische Wert und die Einheiten spielen keine Rolle. Die einzigen Informationen, die für den „host“ ein geänderter Wert ist. Um numerische Probleme im Zusammenhang mit einem Genauigkeitsverlust zu vermeiden beim Umwandeln logischer in physische Einheiten für dieses Feld die physikalischen Minimal- und Maximalwerte sowie den Einheitenexponenten auf null setzen.

Berichtsstruktur

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

Die benutzerdefinierten Werte 1, 2 und 3 für die Datenfelder müssen sich in den Feldern „Benutzerdefinierter Wert 1“, „2“ und „3“ befinden. und sich für ein bestimmtes Gerät (App-Sammlung) nur in einem Bericht befinden.

Eingabeberichte senden

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

  • Die Eigenschaft "Leistungsstatus" ist auf "Full Power" gesetzt.
  • Die Eigenschaft Berichtsstatus ist auf "Alle Ereignisse" festgelegt.
  • Die Eigenschaft Reporting-Intervall ist ungleich null.

Mit der Eigenschaft "Berichtsintervall" wird festgelegt, wie oft die Berichte gesendet werden. Wann? eine der oben genannten Bedingungen nicht erfüllt ist, darf das Gerät keine Berichte senden.

Vorwärts- und Abwärtskompatibilität

Für das HID-Protokoll des Kopf-Trackers wird ein Versionierungsschema verwendet, und gleichzeitig die Interoperabilität zwischen einem Host und einem Gerät, Versionen des Protokolls. Versionen für das Protokoll sind identifiziert die zwei Zahlen, Dur und Moll, haben eine unterschiedliche Semantik die in den folgenden Abschnitten beschrieben werden.

Welche Versionen von einem Gerät unterstützt werden, kann ermittelt werden, indem die Eigenschaft „Sensorbeschreibung“ (0x0308).

Kompatibilität von Nebenversionen

Änderungen an der Nebenversion sind abwärtskompatibel mit früheren Nebenversionen Versionen, die auf derselben Hauptversion basieren. In Änderungen an Minderjährige Version, ignoriert der Host zusätzliche Datenfelder und Eigenschaften. Beispiel: Ein Gerät, das die Protokollversion 1.6 verwendet, ist mit einem Host kompatibel, der Protokollversion 1.x, einschließlich Version 1.5.

Kompatibilität mit Hauptversionen

Nicht abwärtskompatible Änderungen sind für Änderungen an Hauptversionen zulässig. Bis mehrere Hauptversionen für die Interoperabilität mit alten und neuen Hosts, Geräte können in ihrem Bericht mehrere App-Sammlungen angeben. Beschreibungen. 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, untersuchen der Sensorbeschreibung ermitteln die Protokollversionen, die sie jeweils implementieren, und wählen dann neueste Protokollversion, die vom Host unterstützt wird. Bei der Auswahl funktioniert der Organisator mit dem einzelnen Protokoll, das für die Lebensdauer des Geräts ausgewählt wurde

Anhang: Beispiel eines HID-Deskriptors

Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Dabei werden die häufig verwendeten C-Makros, die in Verwendung des HID-Sensors (Abschnitt 4.1).

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 eines HID-Deskriptors in Version 2.0

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

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,
};