HID-Protokoll des Kopf-Trackers

Das HID-Protokoll (Human Interface Device) für den Head-Tracker, das für Geräte mit Android 13 und höher verfügbar ist, ermöglicht die Verbindung eines Head-Tracking-Geräts über USB oder Bluetooth mit einem Android-Gerät und die Bereitstellung für das Android-Framework und Apps über das Sensors-Framework. Dieses Protokoll wird zum Steuern eines Audio-Virtualisierungseffekts (3D-Audio) verwendet. Auf dieser Seite werden die Begriffe Gerät und Host im Bluetooth-Kontext verwendet. Dabei steht Gerät für das Gerät mit der Kopfverfolgung und Host für den Android-Host.

Gerätehersteller müssen ihre Android-Geräte so konfigurieren, dass das HID-Protokoll des Head-Trackers unterstützt wird. Weitere 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 den Kopf-Tracker als HID-Gerät.

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

Auf der obersten Ebene ist das Gerät mit dem Kopftracker eine App-Sammlung mit der Seite Sensors (0x20) und der Nutzung Other: Custom (0xE1). Diese Sammlung enthält mehrere Datenfelder (Inputs) und Properties (Features).

Properties und Datenfelder

In diesem Abschnitt werden die Eigenschaften und Datenfelder in einer Anwendungssammlung eines Kopf-Trackers beschrieben.

Property: Sensor Description (0x0308)

Die Eigenschaft „Sensor Description“ (0x0308) ist ein schreibgeschütztes ASCII-String-Attribut (8 Bit), das die folgenden Werte enthalten muss:

Kopf-Tracker Version 1.0:

#AndroidHeadTracker#1.0

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

#AndroidHeadTracker#2.0#x

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

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

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

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

Property: Persistent Unique ID (0x0302)

Die Eigenschaft „Persistent Unique ID“ (0x0302) ist ein schreibgeschütztes Array mit 16 Elementen mit jeweils 8 Bit (insgesamt 128 Bit). Es wird kein Nullterminator erwartet. Dieses Attribut ist optional.

Mit dieser Property können Geräte zur Kopfverfolgung, die in Audiogeräten integriert sind, auf das Audiogerät verweisen, mit dem sie verbunden sind. Die folgenden Schemas werden unterstützt.

Eigenständiger Kopf-Tracker

Wenn die Eigenschaft „Persistent Unique ID“ (0x0302) nicht vorhanden ist oder auf „0“ gesetzt ist, ist der Kopf-Tracker nicht dauerhaft mit einem Audiogerät verbunden und kann separat verwendet werden. Der Nutzer kann den Kopf-Tracker 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

Bei diesem Schema müssen die ersten 8 Oktette 0 sein, Oktett 8 und 9 müssen die ASCII-Werte B bzw. T enthalten und die folgenden 6 Oktette werden als Bluetooth-MAC-Adresse interpretiert, vorausgesetzt, der Head-Tracker gilt für jedes Audiogerät mit dieser MAC-Adresse. Diese Adresse muss die Identitätsadresse sein, auch wenn das Gerät eine zufällige MAC-Adresse zum Herstellen von Verbindungen verwendet. 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-Beschreibungen mit derselben Identitätsadresse bereitstellen. Dual-Modus-Geräte mit getrennten linken und rechten Geräten müssen Bluetooth LE HID über das primäre Dual-Modus-Gerät anstelle des sekundären LE-Geräts bereitstellen.

Verweis mit UUID

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

Property: Berichtsstatus (0x0316)

Das Attribut „Reporting State“ (0x0316) ist ein Lese-/Schreibattribut mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host verwendet diese Property, um dem Gerät anzugeben, 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 „Keine Ereignisse“ sein und darf niemals vom Gerät, sondern nur vom Host geändert werden.

Property: Power State (0x0319)

Das Attribut „Power State“ (0x0319) ist ein Lese-/Schreibattribut mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host gibt mit dieser Property an, in welchem Betriebsstatus sich das Gerät befinden muss. Es werden nur die Werte „Vollständige Leistung“ (0x0851) und „Aus“ (0x0855) verwendet.

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

Property: Berichtszeitraum (0x030E)

Die Eigenschaft „Berichtsintervall“ (0x030E) ist eine Lese-/Schreibeigenschaft mit der Standardsemantik, wie in der HID-Spezifikation definiert. Der Host gibt mit dieser Property an, wie oft das Gerät seine Datenwerte melden soll. Die Einheit sind Sekunden. Der gültige Bereich für diesen Wert wird vom Gerät bestimmt und mit dem Mechanismus „Physischer Min/Max“ beschrieben. Es muss eine Berichtsrate von mindestens 50 Hz unterstützt werden. Die empfohlene maximale Berichtsrate beträgt 100 Hz. Das Mindestberichtsintervall muss daher maximal 20 ms betragen. Wir empfehlen jedoch, einen Wert von mindestens 10 ms zu verwenden.

Property: Vendor-reserved LE Transport (0xF410)

Die vom Hersteller reservierte LE-Transporteigenschaft (0xF410) ist eine Lese-/Schreibeigenschaft mit der in der HID-Spezifikation definierten Standardsemantik. 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 Eigenschaft wird vor den Betriebs- oder Berichtsstatus konfiguriert.

Datenfeld: Benutzerdefinierter Wert 1 (0x0544)

Das Feld „Benutzerdefinierter Wert 1“ (0x0544) ist ein Eingabefeld, in dem die tatsächlichen Informationen zur Kopfverfolgung erfasst werden. Es ist ein Drei-Element-Array, das gemäß den normalen HID-Regeln für physikalische Werte interpretiert wird, wie in Abschnitt 6.2.2.7 der HID-Spezifikation angegeben. Der gültige Bereich für jedes Element ist [–π, π] rad. Die Einheiten sind immer Bogenmaß.

Die Elemente werden so interpretiert: [rx, ry, rz], wobei [rx, ry, rz] ein Rotationsvektor ist, der die Transformation vom Referenz- zum Kopf-Frame darstellt. Die Amplitude muss im Bereich [0..π] liegen.

Der Referenzrahmen ist beliebig, aber in der Regel fixiert und muss rechtshänderisch sein. Eine geringe Abweichung ist akzeptabel. Die Kopfachsen sind:

  • X von links nach rechts am Ohr
  • Y vom Hinterkopf bis zur Nase (hinten nach vorne)
  • Z vom Hals bis zum Scheitel

Datenfeld: Benutzerdefinierter Wert 2 (0x0545)

Das Feld „Benutzerdefinierter Wert 2“ (0x0545) ist ein Eingabefeld, in dem die tatsächlichen Informationen zur Kopfverfolgung erfasst werden. Es ist ein Fixpunkt-Array mit drei Elementen, das gemäß den normalen HID-Regeln für physikalische Werte interpretiert wird. Die Einheiten sind immer Radian/Sekunde.

Die Elemente werden als [vx, vy, vz] interpretiert, wobei [vx, vy, vz] ein Drehvektor 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, mit dem Unterbrechungen im Referenzrahmen erfasst werden. Es ist eine skalare Ganzzahl mit 8 Bit. Er muss jedes Mal vom Gerät inkrementiert (mit Überlauf) werden, wenn sich der Referenzrahmen ändert, z. B. wenn der Status eines Algorithmus zum Bestimmen der Ausrichtung zurückgesetzt wurde. Dieser Wert wird gemäß den normalen HID-Regeln für physische Werte interpretiert. Der tatsächliche 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 einem Genauigkeitsverlust bei der Umwandlung von logischen in physikalische Einheiten zu vermeiden, wird empfohlen, die Werte für „Physikalisches Minimum“, „Physikalisches Maximum“ und „Einheitsexponent“ für dieses Feld auf null festzulegen.

Berichtsstruktur

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

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

Eingabeberichte senden

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

  • Die Property „Power State“ ist auf „Full Power“ gesetzt.
  • Das Attribut „Berichtsstatus“ ist auf „Alle Ereignisse“ festgelegt.
  • Der Wert der Property „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 des Head-Trackers verwendet ein Versionierungsschema, 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 des Protokolls werden durch zwei Zahlen identifiziert, Haupt- und Nebenversion, die unterschiedliche Bedeutungen haben, wie in den folgenden Abschnitten beschrieben.

Die von einem Gerät unterstützten Versionen können anhand des Attributs „Sensorbeschreibung“ (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 Minorversion ignoriert der Host zusätzliche Datenfelder und -eigenschaften. Ein Gerät mit der Protokollversion 1.6 ist beispielsweise mit einem Host kompatibel, der die 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 zur Interoperabilität mit alten und neuen Hosts zu unterstützen, können Geräte in ihren Berichtsbeschreibungen 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 vom Gerät beworbenen App-Sammlungen auflisten, die Sensorbeschreibungseigenschaft prüfen, um die jeweils implementierten Protokollversionen zu ermitteln, und dann die neueste Protokollversion auswählen, die der Host unterstützt. Wenn diese Option ausgewählt ist, arbeitet der Host mit dem einzelnen Protokoll, das für die gesamte Lebensdauer der Geräteverbindung ausgewählt wurde.

Anhang: Beispiel für einen HID-Beschreibungsblock

Das folgende Beispiel zeigt einen typischen gültigen HID-Deskriptor. Es verwendet die gängigen C-Makros, die in 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-Beschreibungsblock der Version 2.0

Das folgende Beispiel zeigt einen HID-Beschreibungsblock 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,
};