אתם יכולים להשתמש בפונקציונליות של מיקום Wi-Fi שסופקה על ידי Wi-Fi RTT (Round-Trip-Time) API כדי למדוד את המרחק לנקודות גישה ל-Wi-Fi בקרבת מקום שתומכות ב-RTT ולמכשירי Wi-Fi Aware דומים.
אם מודדים את המרחק לשלוש נקודות גישה או יותר, אפשר להשתמש באלגוריתם של מולטילטרציה כדי להעריך את מיקום המכשיר שהכי מתאים למדידות האלה. התוצאה בדרך כלל מדויקת בטווח של מטר עד שניים.
רמת הדיוק הזו מאפשרת לפתח שירותים מבוססי-מיקום ברזולוציה גבוהה, כמו ניווט בתוך מבנים, שליטה קולית מבוססת-הקשר (לדוגמה, "תדליק את האור הזה") ומידע מבוסס-מיקום (לדוגמה, "יש מבצעים מיוחדים על המוצר הזה?").
המכשיר ששולח את הבקשה לא צריך להתחבר לנקודות הגישה כדי למדוד את המרחק באמצעות Wi-Fi RTT. כדי לשמור על הפרטיות, רק המכשיר ששלח את הבקשה יכול לקבוע את המרחק לנקודת הגישה. לנקודות הגישה אין את המידע הזה. פעולות Wi-Fi RTT לא מוגבלות באפליקציות שפועלות בחזית, אבל הן מוגבלות באפליקציות שפועלות ברקע.
היכולות של Wi-Fi RTT ושל Fine-Time-Measurement (FTM) קשורות לתקן IEEE 802.11-2016. כדי להשתמש ב-Wi-Fi RTT, צריך את מדידת הזמן המדויקת שמספקת FTM, כי היא מחשבת את המרחק בין שני מכשירים על ידי מדידת הזמן שלוקח לחבילה לעשות נסיעה הלוך ושוב בין המכשירים, והכפלת הזמן הזה במהירות האור.
ב-Android 15 (רמת API 35) נוספה תמיכה בטווחים ללא טריגר (NTB) לפי תקן IEEE 802.11az.
הבדלים בהטמעה בהתאם לגרסת Android
התכונה Wi-Fi RTT הושקה ב-Android 9 (רמת API 28). כשמשתמשים בפרוטוקול הזה כדי לקבוע את מיקום המכשיר באמצעות מולטילטרציה עם מכשירים שפועלים עם Android 9, צריך שתהיה לכם גישה לנתוני מיקום של נקודת גישה (AP) שנקבעו מראש באפליקציה. אתם יכולים להחליט איך לאחסן את הנתונים האלה ולאחזר אותם.
במכשירים עם Android 10 (רמת API 29) ומעלה, אפשר לייצג את נתוני המיקום של נקודת הגישה כאובייקטים מסוג ResponderLocation, שכוללים קו רוחב, קו אורך וגובה. בנקודות גישה של Wi-Fi RTT שתומכות ב-Location Configuration Information/Location Civic Report (נתוני LCI/LCR), הפרוטוקול יחזיר אובייקט ResponderLocation במהלך תהליך הטווח.
התכונה הזו מאפשרת לאפליקציות לשלוח שאילתות לנקודות הגישה כדי לבקש מהן את המיקום שלהן באופן ישיר, במקום לאחסן את המידע הזה מראש. כך האפליקציה יכולה למצוא נקודות גישה ולקבוע את המיקום שלהן גם אם הן לא היו ידועות לפני כן, למשל כשמשתמש נכנס לבניין חדש.
התמיכה בטווח NTB של IEEE 802.11az זמינה במכשירים עם Android מגרסה 15 (רמת API 35) ואילך. כלומר, אם המכשיר תומך במצב יוזם NTB של IEEE 802.11az (מצוין על ידי WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR), האפליקציה יכולה למצוא גם נקודות גישה שתומכות ב-IEEE 802.11mc וגם נקודות גישה שתומכות ב-IEEE 802.11az באמצעות בקשת טווח אחת. הרחבנו את ה-API של RangingResult כדי לספק מידע על הערך המינימלי והמקסימלי שאפשר להשתמש בו למרווח בין מדידות הטווח, כך שהמרווח המדויק נשאר בשליטת האפליקציה.
דרישות
- החומרה של המכשיר שממנו נשלחת בקשת הטווח חייבת להטמיע את תקן 802.11-2016 FTM או את תקן 802.11az (טווח שלא מבוסס על טריגר).
- במכשיר שממנו נשלחת בקשת הטווח צריכה לפעול מערכת Android מגרסה 9 (רמת API 28) ואילך. מדידת טווח ללא טריגר לפי תקן IEEE 802.11az מופעלת במכשירים עם Android מגרסה 15 (רמת API 35) ומעלה.
- במכשיר ששולח את בקשת הטווח, שירותי המיקום וסריקת ה-Wi-Fi צריכים להיות מופעלים (בקטע הגדרות > מיקום).
- אם האפליקציה ששולחת את בקשת הטווח מטרגטת ל-Android 13 (רמת API 33) ומעלה, צריכה להיות לה ההרשאה
NEARBY_WIFI_DEVICES. אם האפליקציה מטרגטת גרסה קודמת של Android, צריכה להיות לה במקום זאת ההרשאהACCESS_FINE_LOCATION. - האפליקציה צריכה לשלוח שאילתה לגבי טווח נקודות הגישה כשהיא גלויה או כשהיא פועלת כשירות בחזית. האפליקציה לא יכולה לגשת לנתוני המיקום ברקע.
- נקודת הגישה חייבת להטמיע את תקן IEEE 802.11-2016 FTM או את תקן IEEE 802.11az (מדידת טווח שלא מבוססת על טריגר).
הגדרה
כדי להגדיר את האפליקציה לשימוש ב-Wi-Fi RTT, מבצעים את השלבים הבאים.
1. שליחת בקשה להרשאות
מבקשים את ההרשאות הבאות במניפסט של האפליקציה:
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.CHANGE_WIFI_STATE" />
<!-- If your app targets Android 13 (API level 33)
or higher, you must declare the NEARBY_WIFI_DEVICES permission. -->
<uses-permission android:name="android.permission.NEARBY_WIFI_DEVICES"
<!-- If your app derives location information from Wi-Fi APIs,
don't include the "usesPermissionFlags" attribute. -->
android:usesPermissionFlags="neverForLocation" />
<uses-permission android:name="android.permission.ACCESS_FINE_LOCATION"
<!-- If any feature in your app relies on precise location
information, don't include the "maxSdkVersion"
attribute. -->
android:maxSdkVersion="32" />
ההרשאות NEARBY_WIFI_DEVICES ו-ACCESS_FINE_LOCATION הן הרשאות מסוכנות, ולכן צריך לבקש אותן בזמן הריצה בכל פעם שהמשתמש רוצה לבצע פעולת סריקה של RTT. אם האפליקציה עדיין לא קיבלה את ההרשאה, היא תצטרך לבקש אותה מהמשתמש. מידע נוסף על הרשאות בתחילת ההפעלה זמין במאמר בקשת הרשאות שניתנות לאפליקציה.
2. בודקים אם המכשיר תומך ב-Wi-Fi RTT
כדי לבדוק אם המכשיר תומך ב-Wi-Fi RTT, משתמשים ב-API PackageManager:
Kotlin
context.packageManager.hasSystemFeature(PackageManager.FEATURE_WIFI_RTT)
Java
context.getPackageManager().hasSystemFeature(PackageManager.FEATURE_WIFI_RTT);
3. איך בודקים אם אפשר להשתמש ב-Wi-Fi RTT
יכול להיות שהמכשיר תומך ב-Wi-Fi RTT, אבל המשתמש השבית את ה-Wi-Fi ולכן התכונה לא זמינה. בהתאם ליכולות החומרה והקושחה, יכול להיות שמכשירים מסוימים לא יתמכו ב-Wi-Fi RTT אם נעשה שימוש ב-SoftAP או ב-Tethering. כדי לבדוק אם יש תמיכה ב-Wi-Fi RTT, צריך להפעיל את isAvailable().
הזמינות של Wi-Fi RTT יכולה להשתנות בכל שלב. האפליקציה צריכה לרשום
BroadcastReceiver
כדי לקבל
ACTION_WIFI_RTT_STATE_CHANGED,
שנשלח כשהזמינות משתנה. כשהאפליקציה מקבלת את ה-intent של השידור, היא צריכה לבדוק את מצב הזמינות הנוכחי ולשנות את ההתנהגות שלה בהתאם.
לדוגמה:
Kotlin
val filter = IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED) val myReceiver = object: BroadcastReceiver() { override fun onReceive(context: Context, intent: Intent) { if (wifiRttManager.isAvailable) { … } else { … } } } context.registerReceiver(myReceiver, filter)
Java
IntentFilter filter = new IntentFilter(WifiRttManager.ACTION_WIFI_RTT_STATE_CHANGED); BroadcastReceiver myReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (wifiRttManager.isAvailable()) { … } else { … } } }; context.registerReceiver(myReceiver, filter);
מידע נוסף זמין במאמר בנושא שידורים.
יצירת בקשה למדידת טווח
כדי ליצור בקשת מדידת טווח (RangingRequest), צריך לציין רשימה של נקודות גישה או עמיתים ב-Wi-Fi Aware שרוצים למדוד את הטווח אליהם. אפשר לציין כמה נקודות גישה או עמיתים ב-Wi-Fi Aware בבקשת טווח אחת. המרחקים לכל המכשירים נמדדים ומוחזרים.
לדוגמה, בקשה יכולה להשתמש ב-method addAccessPoint() כדי לציין נקודת גישה שאליה צריך למדוד את המרחק:
Kotlin
val req: RangingRequest = RangingRequest.Builder().run { addAccessPoint(ap1ScanResult) addAccessPoint(ap2ScanResult) build() }
Java
RangingRequest.Builder builder = new RangingRequest.Builder(); builder.addAccessPoint(ap1ScanResult); builder.addAccessPoint(ap2ScanResult); RangingRequest req = builder.build();
נקודת גישה מזוהה באמצעות האובייקט ScanResult, שאפשר לקבל על ידי קריאה ל-WifiManager.getScanResults().
אפשר להשתמש ב-addAccessPoints(List<ScanResult>) כדי להוסיף כמה נקודות גישה באצווה.
אובייקטים מסוג ScanResult יכולים להכיל גם נקודות גישה שתומכות ב-IEEE 802.11mc (is80211mcResponder()) וגם נקודות גישה שתומכות ב-IEEE 802.11az non-trigger based ranging (is80211azNtbResponder()). מכשירים שתומכים בטווח IEEE 802.11az NTB מבצעים טווח של 802.11mc או 802.11az בהתאם ליכולת של נקודת הגישה, כברירת מחדל ל-802.11az כשנקודת הגישה תומכת בשניהם. במכשירים שלא תומכים ב-IEEE 802.11az, כל מדידות הטווח מתבצעות באמצעות פרוטוקול IEEE 802.11mc.
באופן דומה, בקשת טווח יכולה להוסיף מכשיר מקביל ב-Wi-Fi Aware באמצעות כתובת ה-MAC שלו או באמצעות PeerHandle, באמצעות השיטות addWifiAwarePeer(MacAddress peer) ו-addWifiAwarePeer(PeerHandle peer), בהתאמה. מידע נוסף על זיהוי עמיתים ב-Wi-Fi Aware זמין במאמרי העזרה בנושא Wi-Fi Aware.
בקשת טווח
האפליקציה שולחת בקשה למדידת טווח באמצעות השיטה WifiRttManager.startRanging() ומספקת את הפרטים הבאים: RangingRequest כדי לציין את הפעולה, Executor כדי לציין את ההקשר של הקריאה החוזרת (callback) ו-RangingResultCallback כדי לקבל את התוצאות.
לדוגמה:
Kotlin
val mgr = context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE) as WifiRttManager val request: RangingRequest = myRequest mgr.startRanging(request, executor, object : RangingResultCallback() { override fun onRangingResults(results: List<RangingResult>) { … } override fun onRangingFailure(code: Int) { … } })
Java
WifiRttManager mgr = (WifiRttManager) Context.getSystemService(Context.WIFI_RTT_RANGING_SERVICE); RangingRequest request ...; mgr.startRanging(request, executor, new RangingResultCallback() { @Override public void onRangingFailure(int code) { … } @Override public void onRangingResults(List<RangingResult> results) { … } });
פעולת הטווח מתבצעת באופן אסינכרוני, ותוצאות הטווח מוחזרות באחת מהקריאות החוזרות (callbacks) של RangingResultCallback:
- אם כל פעולת הטווח נכשלת, הקריאה החוזרת (callback)
onRangingFailureמופעלת עם קוד סטטוס שמתואר ב-RangingResultCallback. כשל כזה יכול לקרות אם השירות לא יכול לבצע פעולת מדידת טווח באותו זמן – לדוגמה, כי ה-Wi-Fi מושבת, כי האפליקציה ביקשה יותר מדי פעולות מדידת טווח והיא מוגבלת, או בגלל בעיית הרשאה. - כשהפעולה של מדידת הטווח מסתיימת, מופעלת פונקציית ה-callback
onRangingResultsעם רשימת תוצאות שתואמת לרשימת הבקשות – תוצאה אחת לכל בקשה. סדר התוצאות לא בהכרח תואם לסדר הבקשות. חשוב לדעת שפעולת הטווח עשויה להסתיים, אבל כל תוצאה עדיין יכולה להצביע על כשל במדידה הספציפית הזו.
פירוש תוצאות הטווחים
כל אחת מהתוצאות שמוחזרות על ידי הקריאה החוזרת (callback) של onRangingResults מצוינת על ידי אובייקט RangingResult. בכל בקשה, מבצעים את הפעולות הבאות.
1. זיהוי הבקשה
מזהים את הבקשה לפי המידע שסופק כשנוצר RangingRequest: בדרך כלל כתובת MAC שסופקה ב-ScanResult שמזהה נקודת גישה. אפשר לקבל את כתובת ה-MAC מתוצאת המדידה באמצעות ה-method getMacAddress().
יכול להיות שרשימת התוצאות של טווח המדידה תהיה בסדר שונה מזה של נקודות הגישה שצוינו בבקשה לטווח המדידה, לכן צריך להשתמש בכתובת ה-MAC כדי לזהות את נקודת הגישה, ולא בסדר התוצאות.
2. לבדוק אם כל מדידה הצליחה
כדי לבדוק אם המדידה הצליחה, משתמשים ב-method getStatus(). כל ערך אחר חוץ מ-STATUS_SUCCESS מציין כישלון. אם הפעולה נכשלת, כל שאר השדות של התוצאה הזו (חוץ ממזהה הבקשה שלמעלה) לא תקינים, והשיטה get* המתאימה תיכשל עם חריגה מסוג IllegalStateException.
3. קבלת תוצאות לכל מדידה מוצלחת
לכל מדידה מוצלחת (RangingResult), אפשר לאחזר את ערכי התוצאות באמצעות השיטות המתאימות של get:
המרחק במילימטרים וסטיית התקן של המדידה:
RSSI של החבילות ששימשו למדידות:
הזמן באלפיות השנייה שבו בוצעה המדידה (מציין את הזמן שחלף מאז האתחול):
מספר המדידות שניסיתם לבצע ומספר המדידות שהצליחו (שעליהן מבוססות מדידות המרחק):
משך הזמן המינימלי והמקסימלי שמכשיר לקוח צריך להמתין בין מדידות 11az NTB:
הפונקציות
getMinTimeBetweenNtbMeasurementsMicros()ו-getMaxTimeBetweenNtbMeasurementsMicros()מחזירות את הזמן המינימלי והמקסימלי. אם הבקשה למדידת הטווח הבאה נשלחת לפני שחלף הזמן המינימלי, ה-API מחזיר את תוצאת הטווח שנשמרה במטמון. אם המדידה הבאה של הטווח מתבקשת אחרי שחלף הזמן המקסימלי, ה-API מסיים את סשן מדידת הטווח שלא מופעל ומנהל משא ומתן על סשן חדש של מדידת טווח עם התחנה המגיבה. כדאי להימנע מבקשה של סשן מדידת טווח חדש, כי זה מוסיף זמן למדידת הטווח. כדי לנצל את מלוא היתרונות של יעילות הטווח ללא טריגר של 802.11az, צריך להפעיל את בקשת הטווח הבאה בין זמן המדידה המינימלי למקסימלי שצוין במדידתRangingResultהקודמת.חזרות על שדה אימון ארוך (LTF) שבהן תחנות המשיב והיוזם השתמשו בהוראות המקדימות לתוצאת IEEE 802.11az NTB:
מספר הזרמים המרחביים-זמניים (STS) לשידור ולקליטה שבהם השתמשה התחנה היוזמת לתוצאת IEEE 802.11az NTB:
מכשירי Android שתומכים ב-Wi-Fi-RTT
בטבלאות הבאות מפורטים חלק מהטלפונים, נקודות הגישה והמכשירים בקמעונאות, במחסנים ובמרכזי הפצה שתומכים ב-Wi-Fi-RTT. אלה רק חלק מהדוגמאות. אנחנו ממליצים לכם לפנות אלינו כדי להוסיף לכאן את המוצרים שלכם שתומכים ב-RTT.
נקודות גישה (AP)
| היצרן והדגם | תאריך התמיכה | פרוטוקול |
|---|---|---|
| Nest Wifi Pro (Wi-Fi 6E) | נתמך | mc |
| Compulab WILD AP | נתמך | mc |
| Google Wi-Fi | נתמך | mc |
| נתב Google Nest Wi-Fi | נתמך | mc |
| נקודת Google Nest Wi-Fi | נתמך | mc |
| Aruba AP-635 | נתמך | mc |
| Cisco 9130 | נתמך | mc |
| Cisco 9136 | נתמך | mc |
| Cisco 9166 | נתמך | mc |
| Cisco 9164 | נתמך | mc |
| Cisco CW9172I | נתמך | mc/az |
| Cisco CW9172H | נתמך | mc/az |
| Cisco CW9176I | נתמך | mc/az |
| Cisco CW9178I | נתמך | mc/az |
| Aruba AP-505 | נתמך | mc |
| Aruba AP-515 | נתמך | mc |
| Aruba AP-575 | נתמך | mc |
| Aruba AP-518 | נתמך | mc |
| Aruba AP-505H | נתמך | mc |
| Aruba AP-565 | נתמך | mc |
| Aruba AP-535 | נתמך | mc |
| Aruba AP567 | נתמך | mc |
| Aruba AP577 | נתמך | mc |
| Aruba AP555 | נתמך | mc |
| Aruba AP635 | נתמך | mc |
| Aruba AP655 | נתמך | mc |
| Aruba AP615 | נתמך | mc |
| Aruba AP734 | נתמך | mc/az |
| Aruba AP735 | נתמך | mc/az |
| Aruba AP754 | נתמך | mc/az |
| Aruba AP755 | נתמך | mc/az |
טלפונים
| היצרן והדגם | גרסת Android |
|---|---|
| Google Pixel 9 Pro XL | 14+ |
| Google Pixel 9 | 14+ |
| Google Pixel 9 Pro | 14+ |
| Google Pixel 9 Pro XL | 14+ |
| Google Pixel 7a | 14+ |
| Google Pixel 7 | 14+ |
| Google Pixel 8 | 14+ |
| Google Pixel 8 Pro | 14+ |
| Google Pixel 8a | 14+ |
| Samsung SM-S918B | 14+ |
| Samsung SM-A515F | 14+ |
| Google Pixel 9 Pro | 14+ |
| Samsung SM-A546E | 14+ |
| Samsung SM-S928B | 14+ |
| Samsung SM-A217F | 14+ |
| Samsung SM-A715F | 14+ |
| Samsung SM-A528B | 14+ |
| Samsung SM-A135F | 14+ |
| Samsung SM-S911B | 14+ |
| Xiaomi 21091116AI | 14+ |
| Google Pixel 9 | 14+ |
| Samsung SM-A127F | 14+ |
| Google Pixel 7 Pro | 14+ |
| Samsung SM-A556E | 14+ |
| Pixel 6 | 9.0 ומעלה |
| Pixel 6 Pro | 9.0 ומעלה |
| Pixel 5 | 9.0 ומעלה |
| Pixel 5a | 9.0 ומעלה |
| Pixel 5a (5G) | 9.0 ומעלה |
| Xiaomi Mi 10 Pro | 9.0 ומעלה |
| Xiaomi Mi 10 | 9.0 ומעלה |
| Xiaomi Redmi Mi 9T Pro | 9.0 ומעלה |
| Xiaomi Mi 9T | 9.0 ומעלה |
| Xiaomi Mi 9 | 9.0 ומעלה |
| Xiaomi Mi Note 10 | 9.0 ומעלה |
| Xiaomi Mi Note 10 Lite | 9.0 ומעלה |
| Xiaomi Redmi Note 9S | 9.0 ומעלה |
| Xiaomi Redmi Note 9 Pro | 9.0 ומעלה |
| Xiaomi Redmi Note 8T | 9.0 ומעלה |
| Xiaomi Redmi Note 8 | 9.0 ומעלה |
| Xiaomi Redmi K30 Pro | 9.0 ומעלה |
| Xiaomi Redmi K20 Pro | 9.0 ומעלה |
| Xiaomi Redmi K20 | 9.0 ומעלה |
| Xiaomi Redmi Note 5 Pro | 9.0 ומעלה |
| Xiaomi Mi CC9 Pro | 9.0 ומעלה |
| LG G8X ThinQ | 9.0 ומעלה |
| LG V50S ThinQ | 9.0 ומעלה |
| LG V60 ThinQ | 9.0 ומעלה |
| LG V30 | 9.0 ומעלה |
| Samsung Galaxy Note 10+ 5G | 9.0 ומעלה |
| Samsung Galaxy S20+ 5G | 9.0 ומעלה |
| Samsung Galaxy S20+ | 9.0 ומעלה |
| Samsung Galaxy S20 5G | 9.0 ומעלה |
| Samsung Galaxy S20 Ultra 5G | 9.0 ומעלה |
| Samsung Galaxy S20 | 9.0 ומעלה |
| Samsung Galaxy Note 10+ | 9.0 ומעלה |
| Samsung Galaxy Note 10 5G | 9.0 ומעלה |
| Samsung Galaxy Note 10 | 9.0 ומעלה |
| Samsung A9 Pro | 9.0 ומעלה |
| Google Pixel 4 XL | 9.0 ומעלה |
| Google Pixel 4 | 9.0 ומעלה |
| Google Pixel 4a | 9.0 ומעלה |
| Google Pixel 3 XL | 9.0 ומעלה |
| Google Pixel 3 | 9.0 ומעלה |
| Google Pixel 3a XL | 9.0 ומעלה |
| Google Pixel 3a | 9.0 ומעלה |
| Google Pixel 2 XL | 9.0 ומעלה |
| Google Pixel 2 | 9.0 ומעלה |
| Google Pixel 1 XL | 9.0 ומעלה |
| Google Pixel 1 | 9.0 ומעלה |
| Poco X2 | 9.0 ומעלה |
| Sharp Aquos R3 SH-04L | 9.0 ומעלה |
מכשירים במרכזי קמעונאות, אחסון והפצה
| היצרן והדגם | גרסת Android |
|---|---|
| Zebra PS20 | 10.0 ומעלה |
| Zebra TC52/TC52HC | 10.0 ומעלה |
| Zebra TC57 | 10.0 ומעלה |
| Zebra TC72 | 10.0 ומעלה |
| Zebra TC77 | 10.0 ומעלה |
| Zebra MC93 | 10.0 ומעלה |
| Zebra TC8300 | 10.0 ומעלה |
| Zebra VC8300 | 10.0 ומעלה |
| Zebra EC30 | 10.0 ומעלה |
| Zebra ET51 | 10.0 ומעלה |
| Zebra ET56 | 10.0 ומעלה |
| Zebra L10 | 10.0 ומעלה |
| Zebra CC600/CC6000 | 10.0 ומעלה |
| Zebra MC3300x | 10.0 ומעלה |
| Zebra MC330x | 10.0 ומעלה |
| Zebra TC52x | 10.0 ומעלה |
| Zebra TC57x | 10.0 ומעלה |
| Zebra EC50 (LAN ו-HC) | 10.0 ומעלה |
| Zebra EC55 (WAN) | 10.0 ומעלה |
| Zebra WT6300 | 10.0 ומעלה |
| Skorpio X5 | 10.0 ומעלה |