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

يتيح بروتوكول جهاز الواجهة البشرية (HID) لتتبُّع حركة الرأس، المتوفّر للأجهزة التي تعمل بالإصدار 13 من نظام التشغيل Android والإصدارات الأحدث، توصيل جهاز تتبُّع حركة الرأس بجهاز Android من خلال منفذ USB أو البلوتوث، كما يتيح إمكانية الوصول إلى إطار عمل أجهزة الاستشعار وتطبيقاته. يُستخدم هذا البروتوكول للتحكّم في تأثير محاكاة الصوت (الصوت ثلاثي الأبعاد). تستخدم هذه الصفحة المصطلحَين جهاز ومضيف بالمعنى المتعارف عليهما في البلوتوث، حيث يشير الجهاز إلى جهاز تتبُّع الرأس، ويشير المضيف إلى مضيف 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) في الثمانية 8 (≥0x80)، يتم تفسير الحقل على أنّه معرّف فريد عالميًا (UUID)، كما هو محدّد في RFC-4122. يوفّر جهاز الصوت المقابل المعرّف الفريد العالمي نفسه، والذي يتم تسجيله في إطار عمل Android من خلال آلية غير محدّدة خاصة بنوع النقل المستخدَم.

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

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

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

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

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

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

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

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

السمة: النقل المحجوز لوكالات إنفاذ القانون من قِبل المورّد (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 من الأذن اليسرى إلى اليمنى
  • Y من مؤخرة الرأس إلى الأنف (من الخلف إلى الأمام)
  • 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 ليست صفرًا.

تحدّد السمة 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,
};