بروتوكول HID لتتبُّع حركة الرأس

يتيح بروتوكول جهاز الواجهة البشرية (HID) لتتبُّع حركة الرأس، المتوفّر للأجهزة التي تعمل بالإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، توصيل جهاز تتبُّع حركة الرأس بجهاز Android من خلال منفذ USB أو البلوتوث، كما يتيح إمكانية الوصول إلى إطار عمل أجهزة الاستشعار وتطبيقات Android. يُستخدم هذا البروتوكول للتحكّم في تأثير محاكاة الصوت (صوت ثلاثي الأبعاد). تستخدم هذه الصفحة المصطلحَين جهاز ومضيف بالمعنى المتعارف عليهما في البلوتوث، حيث يشير الجهاز إلى جهاز تتبُّع الرأس، بينما يشير المضيف إلى مضيف Android.

على الشركات المصنّعة للأجهزة ضبط أجهزة Android الخاصة بها لتفعيل التوافق مع بروتوكول HID الخاص بأداة تتبُّع حركة الرأس. للحصول على معلومات أكثر تفصيلاً حول عملية الضبط، يُرجى الاطّلاع على ملف Dynamic Sensors README.

تفترض هذه الصفحة معرفة الموارد التالية:

البنية ذات المستوى الأعلى

يحدِّد إطار عمل Android جهاز تتبُّع حركة الرأس على أنّه جهاز HID.

للاطّلاع على مثال كامل على واصف HID صالح، راجِع الملحق 1: مثال على واصف HID.

على المستوى الأعلى، جهاز تتبُّع حركة الرأس هو مجموعة تطبيقات تتضمّن صفحة Sensors (0x20) واستخدام Other: Custom (0xE1). تتضمّن هذه المجموعة عدة حقول بيانات (مدخلات) وسمات (ميزات).

السمات وحقول البيانات

يوضّح هذا القسم الخصائص وحقول البيانات في مجموعة تطبيقات لجهاز تتبُّع حركة الرأس.

الموقع: وصف المستشعر (0x0308)

سمة "وصف المستشعر" (Sensor Description) (0x0308) هي سمة سلسلة ASCII (8 بت) للقراءة فقط يجب أن تحتوي على القيم التالية:

الإصدار 1.0 من أداة تتبُّع حركة الرأس:

#AndroidHeadTracker#1.0

الإصدار 2.0 من ميزة "تتبُّع حركة الرأس" (المتوفّر في الإصدار 15 من نظام التشغيل Android أو الإصدارات الأحدث)، والذي يتضمّن توافقًا مع تقنية LE Audio:

#AndroidHeadTracker#2.0#x

x هو عدد صحيح (1، 2، 3) يشير إلى بروتوكول النقل المتوافق:

  • ‫1: ACL
  • ‫2: ISO
  • ‫3: قائمة التحكّم بالوصول (ACL) + معيار ISO

ولا يُتوقّع وجود حرف إنهاء فارغ، ما يعني أنّ الحجم الإجمالي لهذه السمة هو 23 حرفًا من 8 بت للإصدار 1.0.

تعمل هذه السمة كمميّز لتجنُّب التعارض مع أجهزة الاستشعار المخصّصة الأخرى.

السمة: المعرّف الفريد الثابت (0x0302)

سمة المعرّف الفريد الدائم (0x0302) هي عبارة عن مصفوفة للقراءة فقط تتألف من 16 عنصرًا، كل عنصر منها 8 بت (المجموع 128 بت). لا يُتوقّع وجود حرف إنهاء فارغ. هذه السمة اختيارية.

تتيح هذه السمة لأجهزة تتبُّع حركة الرأس المدمجة في أجهزة الصوت الإشارة إلى جهاز الصوت الذي تم ربطها به. تتوفّر المخططات التالية.

أداة تتبُّع الرأس المستقلة

إذا لم تكن السمة "المعرّف الفريد الدائم" (0x0302) متوفّرة أو تم ضبطها على أصفار، يعني ذلك أنّ جهاز تتبُّع حركة الرأس غير متصل بشكل دائم بجهاز صوتي ويمكن استخدامه بشكل منفصل، مثلاً من خلال السماح للمستخدم بربط جهاز تتبُّع حركة الرأس يدويًا بجهاز صوتي منفصل.

الرجوع إلى عنوان MAC للبلوتوث

Octet 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
القيمة 0 0 0 0 0 0 0 0 B T عنوان MAC الخاص بالبلوتوث

في هذا المخطط، يجب أن تكون أول 8 وحدات ثمانية 0، ويجب أن تحتوي الوحدتان الثامنة والتاسعة على قيم ASCII B وT على التوالي، ويتم تفسير الوحدات الثمانية اللاحقة على أنّها عنوان MAC لبلوتوث، على افتراض أنّ تطبيق جهاز تتبُّع حركة الرأس ينطبق على أي جهاز صوتي يتضمّن عنوان MAC هذا. يجب أن يكون هذا العنوان هو عنوان التعريف، حتى إذا كان الجهاز يستخدم عنوان MAC عشوائيًا لإنشاء اتصالات. يجب أن تعرض الأجهزة ذات الوضع المزدوج التي تتصل عبر البلوتوث الكلاسيكي (تنسيق HID الإصدار 1.0) والبلوتوث المنخفض الطاقة (تنسيق HID الإصدار 2.0) وصفتَي HID لهما عنوان التعريف نفسه. يجب أن تعرض الأجهزة ذات الوضع المزدوج التي تتضمّن جهازَين منفصلَين أيسر وأيمن واجهة HID عبر البلوتوث منخفض الطاقة باستخدام الجهاز الأساسي ذي الوضع المزدوج بدلاً من الجهاز الثانوي الذي يعمل بالبلوتوث منخفض الطاقة فقط.

الرجوع إلى السمة باستخدام المعرّف الفريد العالمي (UUID)

عندما يتم ضبط وحدة البت الأكثر أهمية (MSB) في الثمانية الثامنة (≥0x80)، يتم تفسير الحقل على أنّه معرّف فريد عالميًا (UUID)، كما هو محدّد في RFC-4122. يوفّر جهاز الصوت المعنيّ المعرّف الفريد العالمي (UUID) نفسه، والذي يتم تسجيله في إطار عمل Android من خلال آلية غير محدّدة خاصة بنوع النقل المستخدَم.

السمة: حالة إعداد التقارير (0x0316)

السمة "حالة إعداد التقارير" (0x0316) هي سمة للقراءة والكتابة لها الدلالات العادية المحددة في مواصفات HID. يستخدم المضيف هذه السمة لتحديد الأحداث التي يجب إرسال تقارير بشأنها إلى الجهاز. يتم استخدام القيمتين "ما مِن أحداث" (0x0840) و"كل الأحداث" (0x0841) فقط.

يجب أن تكون القيمة الأولية لهذا الحقل هي "ما مِن أحداث"، ويجب ألا يعدّلها الجهاز أبدًا، بل المضيف فقط.

السمة: حالة الطاقة (0x0319)

سمة "حالة الطاقة" (0x0319) هي سمة للقراءة والكتابة لها الدلالات العادية المحددة في مواصفات HID. يستخدم المضيف هذه السمة لتحديد حالة الطاقة التي يجب أن يكون عليها الجهاز. يتم استخدام القيمتَين "الطاقة الكاملة" (0x0851) و"إيقاف التشغيل" (0x0855) فقط.

يتم تحديد القيمة الأولية لهذا الحقل بواسطة الجهاز ويجب ألا يتم تعديلها مطلقًا بواسطة الجهاز، بل بواسطة المضيف فقط.

الموقع: الفترة الزمنية للتقرير (0x030E)

السمة "الفاصل الزمني لإعداد التقارير" (0x030E) هي سمة للقراءة والكتابة لها الدلالات العادية المحددة في مواصفات HID. يستخدم المضيف هذه السمة لتحديد عدد المرات التي يجب أن يرسل فيها الجهاز قراءات البيانات. الوحدات هي ثوانٍ. يتم تحديد النطاق الصالح لهذه القيمة من خلال الجهاز ويتم وصفه باستخدام آلية الحد الأدنى/الحد الأقصى المادي. يجب أن يتوافق الجهاز مع معدّل إرسال بيانات لا يقل عن 50 هرتز، ويُقترَح أن يكون الحد الأقصى لمعدّل إرسال البيانات 100 هرتز. لذلك، يجب أن يكون الحد الأدنى لفاصل إرسال البيانات أقل من 20 مللي ثانية أو يساويها، ويُقترَح أن يكون أكبر من 10 مللي ثانية أو يساويها.

السمة: نقل بيانات LE المحجوزة من المورّد (0xF410)

السمة Vendor-reserved LE Transport (0xF410) هي سمة للقراءة والكتابة تتضمّن الدلالات العادية المحدّدة في مواصفات HID. يستخدم المضيف هذه السمة للإشارة إلى وسيلة النقل المحدّدة (قائمة التحكم في الوصول أو ISO). يتم استخدام قيم ACL (0xF800) وISO (0xF801) فقط، ويجب تضمين كلتيهما في المجموعة المنطقية.

يتم ضبط هذه السمة قبل حالات التشغيل أو إعداد التقارير.

حقل البيانات: القيمة المخصّصة 1 (0x0544)

الحقل "القيمة المخصّصة 1" (0x0544) هو حقل إدخال يُستخدَم لتسجيل معلومات تتبُّع حركة الرأس الفعلية. وهو عبارة عن مصفوفة من 3 عناصر يتم تفسيرها وفقًا لقواعد HID العادية الخاصة بالقيم المادية كما هو محدّد في القسم 6.2.2.7 من مواصفات HID. النطاق الصالح لكل عنصر هو [-π, π] راديان. الوحدات هي دائمًا راديان.

يتم تفسير العناصر على النحو التالي: [rx, ry, rz]، حيث [rx, ry, rz] هو متجه دوران، يمثّل عملية التحويل من الإطار المرجعي إلى إطار الرأس. يجب أن تكون القيمة المطلقة ضمن النطاق [0..π].

إطار الإحداثيات المرجعي اختياري، ولكنّه يكون ثابتًا بشكل عام ويجب أن يكون نظامًا أيمن. ويُسمح بحدوث انحراف بسيط. محاور الرأس هي:

  • X من الأذن اليسرى إلى اليمنى
  • المسافة من مؤخرة الرأس إلى الأنف (من الخلف إلى الأمام)
  • Z من الرقبة إلى أعلى الرأس

حقل البيانات: القيمة المخصّصة 2 (0x0545)

حقل "القيمة المخصّصة 2" (0x0545) هو حقل إدخال يُستخدَم لتسجيل معلومات تتبُّع حركة الرأس الفعلية. وهي عبارة عن مصفوفة ذات 3 عناصر بنقطة ثابتة، يتم تفسيرها وفقًا لقواعد HID العادية للقيم المادية. الوحدات هي دائمًا راديان/ثانية.

يتم تفسير العناصر على النحو التالي: [vx, vy, vz]، حيث [vx, vy, vz] هو متجه دوران، يمثّل السرعة الزاوية لإطار الرأس (بالنسبة إلى نفسه).

حقل البيانات: القيمة المخصّصة 3 (0x0546)

الحقل "القيمة المخصّصة 3" (0x0546) هو حقل إدخال يُستخدَم لتتبُّع حالات عدم الاستمرارية في الإطار المرجعي. وهو عدد صحيح عددي بحجم 8 بت. يجب أن يزيد الجهاز قيمته (مع الالتفاف) في كل مرة يتم فيها تغيير الإطار المرجعي، مثلاً، إذا تمت إعادة ضبط حالة خوارزمية فلترة الاتجاه المستخدَمة لتحديد الاتجاه. يتم تفسير هذه القيمة وفقًا لقواعد HID العادية للقيم المادية. ومع ذلك، لا تهم القيمة المادية والوحدات. المعلومات الوحيدة ذات الصلة بالمضيف هي القيمة التي تم تغييرها. لتجنُّب المشاكل الرقمية المتعلّقة بفقدان الدقة أثناء التحويل من وحدات منطقية إلى وحدات فعلية، ننصحك بضبط قيم الحد الأدنى والحد الأقصى للوحدات الفعلية وأس الوحدة على صفر لهذا الحقل.

بنية التقرير

إنّ تجميع المواقع في التقارير (من خلال تعيين معرّفات التقارير) هو أمر مرن. لتحقيق الكفاءة، ننصحك بفصل السمات للقراءة فقط عن السمات للقراءة والكتابة.

بالنسبة إلى حقول البيانات، يجب أن تكون حقول "القيمة المخصّصة 1" و"القيمة المخصّصة 2" و"القيمة المخصّصة 3" في التقرير نفسه، ويجب أن تكون في تقرير واحد فقط لجهاز معيّن (مجموعة التطبيقات).

إرسال تقارير الإدخال

يجب أن يرسل الجهاز تقارير الإدخال بشكل دوري وغير متزامن (من خلال رسائل HID INPUT) عند استيفاء جميع الشروط التالية:

  • تم ضبط السمة "حالة الطاقة" على "طاقة كاملة".
  • يتم ضبط السمة Reporting State على "جميع الأحداث".
  • قيمة السمة Reporting Interval ليست صفرًا.

تحدّد السمة "الفاصل الزمني لإعداد التقارير" عدد مرات إرسال التقارير. في حال عدم استيفاء أي من الشروط أعلاه، يجب ألا يرسل الجهاز أي تقارير.

التوافق مع الإصدارات الأحدث والأقدم

يستخدم بروتوكول HID لتتبُّع حركة الرأس نظامًا لتحديد الإصدارات يتيح إجراء التحديثات، مع السماح بالتشغيل التفاعلي بين جهاز مضيف وجهاز يستخدمان إصدارات مختلفة من البروتوكول. يتم تحديد إصدارات البروتوكول برقمَين، أحدهما رئيسي والآخر ثانوي، ولهما دلالات مميزة كما هو موضح في الأقسام التالية.

يمكن تحديد الإصدارات المتوافقة مع الجهاز من خلال فحص الخاصية "وصف أداة الاستشعار" (0x0308).

التوافق مع أرقام الإصدارات الثانوية

تكون التغييرات التي يتم إجراؤها على رقم الإصدار الثانوي متوافقة مع الإصدارات الثانوية السابقة التي تستند إلى رقم الإصدار الرئيسي نفسه. في التحديثات التي يتم إجراؤها على الإصدار الثانوي، يتجاهل المضيف حقول البيانات والخصائص الإضافية. على سبيل المثال، يكون الجهاز الذي يستخدم الإصدار 1.6 من البروتوكول متوافقًا مع مضيف يتوافق مع الإصدار 1.x من البروتوكول، بما في ذلك الإصدار 1.5.

التوافق مع أرقام الإصدارات الرئيسية

يُسمح بإجراء تغييرات غير متوافقة مع الإصدارات السابقة عند إجراء تغييرات على الإصدارات الرئيسية. لإتاحة استخدام إصدارات رئيسية متعددة لتحقيق التشغيل التفاعلي مع المضيفين القدامى والجدد، يمكن للأجهزة تحديد مجموعات تطبيقات متعددة في أوصاف التقارير. على سبيل المثال:

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

في هذه الحالة، يمكن للمضيف تعداد جميع مجموعات التطبيقات المختلفة التي يعلن عنها الجهاز، وفحص السمة "وصف المستشعر" لتحديد إصدارات البروتوكول التي ينفّذها كل منها، ثم اختيار أحدث إصدار من البروتوكول الذي يتيحه المضيف. عند اختيار هذا الخيار، سيعمل الجهاز المضيف مع البروتوكول الفردي الذي تم اختياره طوال مدة اتصال الجهاز.

الملحق: مثال على واصف HID

يوضّح المثال التالي واصف HID صالحًا نموذجيًا. ويستخدم وحدات ماكرو C الشائعة الاستخدام، والموضّحة في استخدامات أجهزة الاستشعار HID (الفقرة 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,
};

الملحق 2: مثال على واصف HID الإصدار 2.0

يوضّح المثال التالي واصف HID الإصدار 2.0 لجهاز يتيح فقط نقل بيانات ACL عبر Bluetooth LE.

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