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

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

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

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

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

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

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

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

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

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

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

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

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

#AndroidHeadTracker#1.0

الإصدار 2.0 من جهاز تتبُّع الرأس (متوفّر في نظام التشغيل Android 15 أو أعلى)، والتي تشمل التوافق مع ميزة LE Audio:

#AndroidHeadTracker#2.0#x

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

  • 1: قائمة التحكّم بالوصول
  • 2: ISO
  • 3: ACL + ISO

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

وتعمل هذه الخاصية كأداة تمييز لتجنب الاصطدام مع أدوات الاستشعار المخصصة.

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

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

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

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

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

مرجع باستخدام عنوان MAC للبلوتوث

ثُمانِي واحد 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 الخاص بالبلوتوث

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

مرجع يستخدم المعرّف الفريد العالمي (UUID)

عند ضبط أهم وحدة بت (MSB) من الثمانيت 8 (≥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)

سمة LE Transport (0xF410) المحجوزة للمورّد هي سمة للقراءة/الكتابة تتضمن الدلالات العادية كما هو محدّد في مواصفات HID. المضيف يستخدم هذه الخاصية للإشارة إلى النقل المحدد (ACL أو 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) هو حقل إدخال يُستخدَم لإعداد تقارير عن معلومات تتبُّع حركة الرأس الفعلية. إنها مصفوفة النقاط الثابتة المكونة من ثلاثة عناصر، وفقًا لقواعد HID العادية للقيم الفيزيائية. تكون الوحدات دائمًا راديان/ثانية.

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

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

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

بنية التقرير

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

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

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

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

  • يتم ضبط خاصية Power State على "الطاقة الكاملة".
  • تم ضبط سمة حالة إعداد التقارير على "كل الأحداث".
  • تكون قيمة سمة "فترة إعداد التقارير" غير صفرية.

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

التوافق مع الإصدارات السابقة واللاحقة

يستخدم بروتوكول 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

يوضّح المثال التالي أداة وصفية للإصدار 2.0 HID من جهاز متوافق. نقل قائمة التحكّم في الوصول إلى البلوتوث (LE ACL) فقط.

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