פרוטוקול מכשיר ממשק אנושי (HID) למעקב אחרי תנועות הראש, שזמין למכשירים עם Android מגרסה 13 ואילך, מאפשר לחבר מכשיר למעקב אחרי תנועות הראש למכשיר Android באמצעות USB או Bluetooth, ולחשוף אותו למסגרת Android ולאפליקציות דרך מסגרת החיישנים. הפרוטוקול הזה משמש לשליטה באפקט של וירטואליזציה של אודיו (אודיו תלת-ממדי). בדף הזה נעשה שימוש במונחים מכשיר ומארח בהקשר של Bluetooth, כאשר מכשיר מתייחס למכשיר למעקב אחרי תנועות הראש ומארח מתייחס למארח Android.
יצרני מכשירים צריכים להגדיר את מכשירי Android שלהם כך שיתאפשר שימוש בפרוטוקול HID של מעקב אחרי תנועות הראש. מידע מפורט יותר על ההגדרה זמין בקובץ ה-README של Dynamic Sensors.
בדף הזה אנחנו מניחים שאתם מכירים את המשאבים הבאים:
מבנה ברמה העליונה
מסגרת Android מזהה את מכשיר המעקב אחר תנועות הראש כמכשיר HID.
דוגמה מלאה של מתאר HID תקין מופיעה בנספח 1: דוגמה של מתאר HID.
ברמה העליונה, מכשיר המעקב אחר תנועות הראש הוא אוסף אפליקציות עם הדף Sensors
(0x20
) והשימוש Other: Custom
(0xE1
). בתוך האוסף הזה יש כמה שדות נתונים (inputs) ומאפיינים (features).
מאפיינים ושדות נתונים
בקטע הזה מתוארים המאפיינים ושדות הנתונים באוסף האפליקציות של מכשיר למעקב אחר תנועות הראש.
מאפיין: תיאור החיישן (0x0308
)
המאפיין Sensor Description (תיאור החיישן) (0x0308
) הוא מאפיין מחרוזת ASCII (8 ביט) לקריאה בלבד, שחייב להכיל את הערכים הבאים:
גרסה 1.0 של מעקב הראש:
#AndroidHeadTracker#1.0
גרסה 2.0 של מעקב אחרי תנועות הראש (זמינה ב-Android 15 ומעלה), שכוללת תמיכה ב-LE Audio:
#AndroidHeadTracker#2.0#x
הערך של x
הוא מספר שלם (1
, 2
, 3
) שמציין את אמצעי התחבורה שנתמך:
- 1: ACL
- 2: ISO
- 3: ACL + ISO
לא צפוי תו סיום null, כלומר הגודל הכולל של המאפיין הזה הוא 23 תווים של 8 ביט בגרסה 1.0.
המאפיין הזה משמש להבחנה בין חיישנים כדי למנוע התנגשויות עם חיישנים מותאמים אישית אחרים.
מאפיין: מזהה ייחודי קבוע (0x0302
)
המאפיין 'מזהה ייחודי קבוע' (0x0302
) הוא מערך לקריאה בלבד של 16 רכיבים, כל אחד בגודל 8 ביט (סה"כ 128 ביט). לא צפוי תו סיום null. המאפיין הזה הוא אופציונלי.
המאפיין הזה מאפשר למכשירים למעקב אחר תנועות הראש שמשולבים במכשירי אודיו להפנות למכשיר האודיו שאליו הם מחוברים. יש תמיכה בסכימות הבאות.
מעקב ראש עצמאי
אם המאפיין Persistent Unique ID (0x0302
) לא קיים או שהערך שלו הוא אפסים, המשמעות היא שמכשיר המעקב אחר תנועות הראש לא מחובר באופן קבוע למכשיר אודיו, ואפשר להשתמש בו בנפרד. לדוגמה, המשתמש יכול לשייך באופן ידני את מכשיר המעקב אחר תנועות הראש למכשיר אודיו נפרד.
הפניה באמצעות כתובת MAC של Bluetooth
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 של Bluetooth |
בסכימה הזו, 8 האוקטטות הראשונות צריכות להיות 0
, אוקטטות 8 ו-9 צריכות להכיל את ערכי ה-ASCII B
ו-T
בהתאמה, ו-6 האוקטטות הבאות מתפרשות ככתובת MAC של Bluetooth, בהנחה שאפליקציית המעקב אחר תנועות הראש חלה על כל מכשיר אודיו עם כתובת ה-MAC הזו. הכתובת הזו חייבת להיות כתובת הזהות, גם אם המכשיר משתמש בכתובת MAC רנדומלית כדי ליצור חיבורים. מכשירים עם מצב כפול שמתחברים באמצעות Bluetooth classic (פורמט HID v1.0) ו-Bluetooth LE (פורמט HID v2.0) צריכים לחשוף שני מתארי HID עם אותה כתובת זהות. במכשירים עם מצב כפול שבהם יש מכשיר נפרד לכל אוזן, צריך לחשוף את Bluetooth LE HID באמצעות המכשיר הראשי עם מצב כפול ולא באמצעות המכשיר המשני שהוא LE בלבד.
הפניה באמצעות מזהה ייחודי אוניברסלי (UUID)
אם הסיבית הגבוהה ביותר (MSB) של אוקטט 8 מוגדרת (≥0x80
), השדה מתפרש כ-UUID, כפי שמצוין ב-RFC-4122. מכשיר האודיו המתאים מספק את אותו UUID, שרשום ב-Android framework, באמצעות מנגנון לא ספציפי שייחודי לסוג ההעברה שנעשה בה שימוש.
נכס: מצב הדיווח (0x0316
)
המאפיין Reporting State (0x0316
) הוא מאפיין לקריאה ולכתיבה, עם סמנטיקה רגילה כפי שמוגדר במפרט HID. המארח משתמש במאפיין הזה כדי לציין למכשיר אילו אירועים לדווח. הערכים היחידים שבהם נעשה שימוש הם No Events (0x0840
) ו-All Events (0x0841
).
הערך הראשוני של השדה הזה חייב להיות No Events (אין אירועים), והמכשיר לא יכול לשנות אותו, רק המארח.
מאפיין: מצב הסוללה (0x0319
)
המאפיין Power State (מצב הפעלה) (0x0319
) הוא מאפיין לקריאה ולכתיבה, עם הסמנטיקה הרגילה כפי שהיא מוגדרת במפרט HID. המארח משתמש במאפיין הזה כדי לציין למכשיר באיזה מצב צריכת חשמל הוא צריך להיות. הערכים היחידים שבהם נעשה שימוש הם Full Power (הפעלה מלאה) (0x0851
) ו-Power Off (כיבוי) (0x0855
).
הערך הראשוני של השדה הזה נקבע על ידי המכשיר, ואסור לשנות אותו במכשיר. רק המארח יכול לשנות אותו.
מאפיין: מרווח הזמן בדוח (0x030E
)
המאפיין Report Interval (0x030E
) הוא מאפיין לקריאה ולכתיבה, עם הסמנטיקה הרגילה כפי שמוגדרת במפרט HID. המארח משתמש במאפיין הזה כדי לציין למכשיר באיזו תדירות לדווח על קריאות הנתונים שלו.
היחידות הן שניות. הטווח התקין של הערך הזה נקבע על ידי המכשיר ומתואר באמצעות מנגנון המינימום והמקסימום הפיזיים. צריך לתמוך בתדירות דיווח של לפחות 50 הרץ, ותדירות הדיווח המקסימלית המומלצת היא 100 הרץ. לכן, מרווח הדיווח המינימלי צריך להיות 20 אלפיות השנייה או פחות, ומומלץ שיהיה 10 אלפיות השנייה או יותר.
נכס: Vendor-reserved LE Transport (0xF410
)
המאפיין Vendor-reserved LE Transport (0xF410
) הוא מאפיין לקריאה ולכתיבה
עם הסמנטיקה הסטנדרטית שמוגדרת במפרט HID. המארח משתמש במאפיין הזה כדי לציין את אמצעי התחבורה שנבחר (ACL או ISO). המערכת משתמשת רק בערכים ACL (0xF800
) ו-ISO (0xF801
), ושניהם צריכים להיכלל באוסף הלוגי.
הנכס הזה מוגדר לפני מצבי ההפעלה או הדיווח.
שדה נתונים: ערך מותאם אישית 1 (0x0544
)
השדה Custom Value 1 (0x0544
) הוא שדה קלט שמשמש לדיווח על נתוני מעקב הראש בפועל. מדובר במערך עם 3 רכיבים, שמתפרש בהתאם לכללי ה-HID הרגילים לגבי ערכים פיזיים, כפי שמופיע בקטע 6.2.2.7 במפרט ה-HID. הטווח התקף של כל רכיב הוא [-π, π] rad. היחידות
הן תמיד רדיאנים.
האלמנטים מפורשים כך: [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 הרגילים לגבי ערכים פיזיים. עם זאת,
הערך הפיזי והיחידות לא משנים. המידע היחיד שרלוונטי למארח הוא ערך שהשתנה. כדי למנוע בעיות מספריות שקשורות לאובדן דיוק במהלך המרה מיחידות לוגיות ליחידות פיזיות, מומלץ להגדיר את הערכים של המינימום הפיזי, המקסימום הפיזי ומעריך היחידה לאפס בשדה הזה.
מבנה הדוח
הקיבוץ של נכסים בדוחות (לפי הקצאה של מזהי דוחות) הוא גמיש. כדי לשפר את היעילות, מומלץ להפריד בין נכסים לקריאה בלבד לבין נכסים לקריאה ולכתיבה.
לגבי שדות הנתונים, השדות Custom Value 1, 2 ו-3 צריכים להיות באותו דוח, וכל אחד מהם צריך להופיע רק בדוח אחד עבור מכשיר נתון (איסוף נתונים באפליקציה).
שליחת דוחות על קלט
המכשיר צריך לשלוח באופן תקופתי ואסינכרוני (באמצעות הודעות קלט של HID) דוחות קלט כשכל התנאים הבאים מתקיימים:
- המאפיין Power State (מצב הפעלה) מוגדר כ-Full Power (הפעלה מלאה).
- המאפיין 'מצב הדיווח' מוגדר לערך 'כל האירועים'.
- המאפיין Reporting Interval (מרווח הדיווח) הוא לא אפס.
המאפיין Reporting Interval (מרווח הדיווח) קובע את התדירות שבה הדוחות נשלחים. אם לא מתקיים אף אחד מהתנאים שלמעלה, המכשיר לא יכול לשלוח דוחות.
תאימות קדימה ותאימות לאחור
פרוטוקול ה-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 Sensor Usages (סעיף 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,
};