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

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

على الشركات المصنّعة للأجهزة ضبط أجهزة Android لإتاحة التوافق مع بروتوكول HID لجهاز تتبع الرأس. لمزيد من المعلومات التفصيلية حول التهيئة، يمكنك مراجعة دليل توضيحي لأجهزة الاستشعار الديناميكية

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

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

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

للاطّلاع على مثال كامل لرمز HID صالح، يُرجى الاطّلاع على الملحق 1: مثال على أداة وصف الواجهة البشرية (HID)

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

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

يوضِّح هذا القسم السمات وحقول البيانات في أحد التطبيقات. مجموعة من جهاز تتبع الرأس.

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

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

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

#AndroidHeadTracker#1.0

الإصدار 2.0 من أداة تتبُّع حركة الرأس (متاحة في الإصدار 15 من Android أو إصدار أحدث)، والذي يتضمّن تقنية 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 الخاص بالبلوتوث

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

الإشارة باستخدام معرّف UUID

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

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

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

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

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

خاصية Power State (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 لتتبُّع حركة الرأس مخطّط إصدارات يسمح بالحصول على التحديثات، مع السماح بإمكانية التشغيل التفاعلي بين المضيف والجهاز اللذَين يستخدمان إصدارات مختلفة من البروتوكول. يتم تحديد إصدارات البروتوكول برقمين، رئيسي وثانوي، لهما دلالات مختلفة كما هو описан في الأقسام التالية.

يمكن تحديد الإصدارات المتوافقة مع الجهاز من خلال فحص السمة Sensor Description (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,
};