פרוטוקול ה-HID של מכשיר מעקב אחר תנועות הראש, שזמין במכשירים עם Android מגרסה 13 ואילך, מאפשר לחבר מכשיר מעקב אחר תנועות הראש למכשיר Android באמצעות USB או Bluetooth, ולהציג אותו למסגרת ולאפליקציות של Android דרך המסגרת sensors. הפרוטוקול הזה משמש לשליטה באפקט של וירטואליזציה של אודיו (אודיו תלת-ממדי). בדף הזה נעשה שימוש במונחים מכשיר ומארח במובן של Bluetooth, כאשר מכשיר הוא מכשיר מעקב אחר תנועות הראש ומארח הוא מארח Android.
יצרני המכשירים חייבים להגדיר את מכשירי Android שלהם כך שיאפשרו תמיכה בפרוטוקול HID של מכשיר מעקב אחר תנועות הראש. למידע מפורט יותר על ההגדרה, אפשר לעיין בקובץ ה-README של Dynamic Sensors.
בדף הזה אנחנו יוצאים מנקודת הנחה שאתם מכירים את המשאבים הבאים:
מבנה ברמה העליונה
מסגרת 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: ACL
- 2: ISO
- 3: ACL + ISO
לא צפוי סימן סיום של null, כלומר, הנפח הכולל של המאפיין הזה הוא 23 תווים של 8 ביט בגרסה 1.0.
המאפיין הזה משמש כמאפיין מבדיל כדי למנוע התנגשויות עם חיישנים מותאמים אישית אחרים.
מאפיין: מזהה ייחודי מתמיד (0x0302
)
מאפיין המזהה הייחודי הקבוע (0x0302
) הוא מערך לקריאה בלבד של 16
רכיבים נוספים, 8 סיביות לכל אחד (סה"כ 128 סיביות). לא צפוי סימן סיום null. הזה
הוא אופציונלי.
המאפיין הזה מאפשר למכשירי מעקב אחר תנועות הראש שמוטמעים במכשירי אודיו להפנות למכשיר האודיו שאליו הם מחוברים. יש תמיכה בסכמות הבאות.
מכשיר מעקב אחר תנועות הראש עצמאי
אם המאפיין 'מזהה ייחודי קבוע' (0x0302
) לא קיים או שהוא מוגדר כ'הכול'
אפסים, המשמעות היא שמכשיר המעקב אחר הראש לא מחובר באופן קבוע
התקן אודיו ואפשר להשתמש בו בנפרד, לדוגמה, על ידי מתן אפשרות למשתמש
לשייך באופן ידני את מכשיר המעקב הראש למכשיר אודיו נפרד.
הפניה באמצעות כתובת MAC של Bluetooth
אוקטט | 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, שרשום על
באמצעות מנגנון לא מוגדר שספציפי
סוג התעבורה שבו נעשה שימוש.
נכס: מצב הדיווח (0x0316
)
הנכס Reporting State (0x0316
) הוא נכס לקריאה/כתיבה עם סמנטיקה רגילה כפי שמוגדרת במפרט HID. המארח משתמש במאפיין הזה כדי לציין למכשיר על אילו אירועים לדווח. רק הערכים מסוג 'לא'
נעשה שימוש באירועים (0x0840
) וב'כל האירועים' (0x0841
).
הערך הראשוני עבור השדה הזה חייב להיות 'ללא אירועים', ולעולם לא להיות שונה על ידי המכשיר, רק על ידי המארח.
מאפיין: מצב חשמל (0x0319
)
המאפיין Power State (0x0319
) הוא מאפיין קריאה/כתיבה שיש לו את הסמנטיקה הרגילה כפי שמוגדרת במפרט HID. המארח משתמש במאפיין הזה כדי לציין למכשיר באיזה מצב צריך להיות המכשיר. רק
ערכים של 'הפעלה מלאה' (0x0851
) ו'כיבוי' (0x0855
).
הערך הראשוני בשדה הזה נקבע על ידי המכשיר, ולעולם לא צריך להיות שונה על ידי המכשיר, רק על ידי המארח.
מאפיין: מרווח בין דוחות (0x030E
)
המאפיין Report Interval (0x030E
) הוא מאפיין לקריאה/כתיבה שיש לו את הסמנטיקה הרגילה כפי שמוגדרת במפרט HID. המארח משתמש
כדי לציין למכשיר באיזו תדירות לדווח על קריאות הנתונים שלו.
היחידות הן שניות. הטווח החוקי של הערך הזה נקבע לפי המכשיר
ומתואר באמצעות מנגנון מינימום/מקסימום פיזית. צריכה להיות תמיכה בתדירות דיווח של 50 Hz לפחות, ותדירות הדיווח המקסימלית המומלצת היא 100 Hz. לכן, מרווח הדיווח המינימלי צריך להיות קטן מ-20ms או שווה לו, והוא מומלץ להיות גדול מ-10ms או שווה לו.
נכס: LE Transport בהזמנה של ספק (0xF410
)
המאפיין LE Transport (0xF410
) ששמור לשימוש היצרן הוא מאפיין קריאה/כתיבה שיש לו את הסמנטיקה הרגילה כפי שמוגדרת במפרט HID. המארח/ת.
משתמשת במאפיין הזה כדי לציין את התעבורה שנבחרה (ACL או ISO). רק
נעשה שימוש בערכי ACL (0xF800
) ו-ISO (0xF801
), וצריך לכלול את שניהם
באוסף הלוגי.
הנכס הזה מוגדר לפני מצב ההפעלה או הדיווח.
שדה נתונים: ערך מותאם אישית 1 (0x0544
)
השדה 'ערך מותאם אישית 1' (0x0544
) הוא שדה להזנת קלט שמשמש לדיווח על
מידע בפועל על מעקב אחר תנועות הראש. זוהי מערך של 3 רכיבים, שמתפרש בהתאם לכללי ה-HID הרגילים לערכים פיזיים כפי שמפורט בקטע 6.2.2.7 במפרט ה-HID. הטווח התקף לכל רכיב הוא [- cookie, Custom] רדי. יחידות
הם תמיד רדיאנים.
הרכיבים מפורשים כך: [rx, ry, rz]
, כך ש-[rx, ry, rz]
הוא
וקטור הסיבוב,
שמייצג את הטרנספורמציה ממסגרת העזר למסגרת הראש.
הערך של Magnitude חייב להיות בטווח [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) שליחת דוחות קלט כאשר כל התנאים הבאים מתקיימים:
- הנכס Power State מוגדר לערך Full Power.
- הערך של המאפיין Reporting State מוגדר ל-All Events (כל האירועים).
- הנכס 'מרווח הזמן לדיווח' אינו אפס.
הנכס 'מרווח דיווח' קובע את התדירות שבה הדוחות יישלחו. מתי אף אחד מהתנאים שלמעלה לא מתקיים, המכשיר לא יכול לשלוח דיווחים.
תאימות קדימה ואחורה
פרוטוקול ה-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, שימוש בחיישן ממשק אנושי (סעיף 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,
};