אפשר להשתמש בפונקציונליות של מיקום באמצעות Wi-Fi שמופיעה ב-Wi-Fi RTT (Round-Trip-Time) API כדי למדוד את המרחק מנקודות גישה סמוכות ל-Wi-Fi עם תמיכה ב-RTT וממכשירי Wi-Fi Aware.
אם מודדים את המרחק לשלוש נקודות גישה או יותר, אפשר להשתמש באלגוריתם מולטילטרציה כדי להעריך את מיקום המכשיר שמתאים הכי טוב למדידות האלה. התוצאה בדרך כלל מדויקת בטווח של מטר עד שני מטרים.
רמת הדיוק הזו מאפשרת לכם לפתח שירותים מבוססי-מיקום ברמת דיוק גבוהה, כמו ניווט בתוך מבנים, שליטה קולית ללא דו-משמעות (לדוגמה, "הפעל את האור הזה") ומידע מבוסס-מיקום (לדוגמה, "יש מבצעים מיוחדים על המוצר הזה?").
המכשיר ששולח את הבקשה לא צריך להתחבר לנקודות הגישה כדי למדוד את המרחק באמצעות Wi-Fi RTT. כדי לשמור על הפרטיות, רק המכשיר ששולח את הבקשה יכול לקבוע את המרחק מנקודת הגישה. נקודות הגישה לא מקבלות את המידע הזה. הפעולות של Wi-Fi RTT לא מוגבלות לאפליקציות בחזית, אבל הן מוגבלות לאפליקציות ברקע.
היכולות של Wi-Fi RTT ושל מדידת זמן מדויקת (FTM) שקשורות אליו מוגדרות בתקן IEEE 802.11-2016. כדי לחשב את המרחק בין שני מכשירים, צריך למדוד את הזמן שלוקח למנה להשלים הלוך ושוב בין המכשירים, ולהכפיל את הזמן הזה במהירות האור. לכן, כדי להשתמש ב-Wi-Fi RTT, צריך את מדידת הזמן המדויקת שמספק FTM.
ב-Android 15 (רמת API 35) נוספה תמיכה בטווח IEEE 802.11az שאינו מבוסס על טריגר (NTB).
הבדלים בהטמעה בהתאם לגרסת Android
התכונה Wi-Fi RTT הושקה ב-Android 9 (רמת API 28). כשמשתמשים בפרוטוקול הזה כדי לקבוע את מיקום המכשיר באמצעות מולטילטרציה עם מכשירים שמריצים Android 9, צריך שתהיה לאפליקציה גישה לנתוני מיקום של נקודות גישה (AP) שנקבעו מראש. אתם מחליטים איך לאחסן את הנתונים האלה ואיך לאחזר אותם.
במכשירים עם Android 10 (רמת API 29) ומעלה, נתוני המיקום של נקודת הגישה יכולים להיות מיוצגים כאובייקטים של ResponderLocation
, שכוללים קו רוחב, קו אורך וגובה. עבור נקודות גישה ל-Wi-Fi RTT שתומכות בנתוני מיקום של LCI/LCR, הפרוטוקול יחזיר אובייקט ResponderLocation
במהלך תהליך המדידה.
התכונה הזו מאפשרת לאפליקציות לשלוח שאילתות לנקודות גישה כדי לקבל מהן את המיקום שלהן ישירות, במקום לאחסן את המידע הזה מראש. כך האפליקציה יכולה למצוא נקודות גישה ולקבוע את המיקומים שלהן גם אם נקודות הגישה לא היו מוכרות קודם, למשל כשמשתמש נכנס לבניין חדש.
תמיכה בטווח IEEE 802.11az NTB זמינה במכשירים עם Android מגרסה 15 (רמת API 35) ומעלה. כלומר, אם המכשיר תומך במצב יוזם של IEEE 802.11az NTB (מסומן ב-WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR
), האפליקציה יכולה למצוא נקודות גישה (AP) עם יכולות IEEE 802.11mc ו-IEEE 802.11az באמצעות בקשת טווח אחת. הרחבנו את RangingResult
API כדי לספק מידע על הערך המינימלי והמקסימלי שאפשר להשתמש בו למרווח בין מדידות טווח, כך שהמרווח המדויק יישאר בשליטת האפליקציה שלכם.
דרישות
- החומרה של המכשיר ששולח את בקשת המדידה חייבת ליישם את תקן FTM 802.11-2016 או את תקן 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 או בשיתוף אינטרנט. כדי לבדוק אם RTT ב-Wi-Fi זמין, מתקשרים אל isAvailable()
.
הזמינות של Wi-Fi RTT עשויה להשתנות בכל שלב. האפליקציה שלכם צריכה לרשום BroadcastReceiver
כדי לקבל ACTION_WIFI_RTT_STATE_CHANGED
, שנשלח כשחל שינוי בזמינות. כשהאפליקציה מקבלת את שידור הכוונות, היא צריכה לבדוק את מצב הזמינות הנוכחי ולשנות את ההתנהגות שלה בהתאם.
לדוגמה:
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 בבקשת מדידה אחת. המרחקים לכל המכשירים נמדדים ומוחזרים.
לדוגמה, בקשה יכולה להשתמש בשיטה 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()
) וגם נקודות גישה (AP) שתומכות ב-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
כדי לציין את הקשר של הקריאה החוזרת ו-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) { … } });
פעולת הטווח מתבצעת באופן אסינכרוני, והתוצאות של הטווח מוחזרות באחת מהתקשרות החוזרות של RangingResultCallback
:
- אם כל פעולת הטווח נכשלת, מתבצעת הפעלה חוזרת של
onRangingFailure
עם קוד סטטוס שמתואר ב-RangingResultCallback
. הכשל הזה יכול לקרות אם השירות לא יכול לבצע פעולת מדידה בזמן מסוים – למשל, כי ה-Wi-Fi מושבת, כי האפליקציה ביקשה יותר מדי פעולות מדידה והיא מוגבלת, או בגלל בעיית הרשאה. - כשהפעולה של הגדרת הטווח מסתיימת, מופעלת פונקציית הקריאה החוזרת
onRangingResults
עם רשימת תוצאות שתואמת לרשימת הבקשות – תוצאה אחת לכל בקשה. סדר התוצאות לא בהכרח זהה לסדר הבקשות. שימו לב: יכול להיות שפעולת המדידה תסתיים, אבל כל תוצאה עדיין תציין שהמדידה הספציפית הזו נכשלה.
פירוש תוצאות המדידה
כל אחת מהתוצאות שמוחזרות על ידי הקריאה החוזרת onRangingResults
מוגדרת על ידי אובייקט RangingResult
. בכל בקשה, מבצעים את הפעולות הבאות.
1. זיהוי הבקשה
מזהים את הבקשה לפי המידע שסופק כשיוצרים את RangingRequest
: לרוב זו כתובת MAC שסופקה ב-ScanResult
ומזהה נקודת גישה. אפשר לקבל את כתובת ה-MAC מתוצאת הטווח באמצעות השיטה getMacAddress()
.
הסדר של התוצאות ברשימה יכול להיות שונה מהסדר של נקודות הגישה שצוינו בבקשה, ולכן צריך להשתמש בכתובת ה-MAC כדי לזהות את נקודת הגישה ולא בסדר של התוצאות.
2. קובעים אם כל מדידה הצליחה
כדי לבדוק אם המדידה הצליחה, משתמשים בשיטה getStatus()
: כל ערך אחר מלבד STATUS_SUCCESS
מציין שהפעולה נכשלה. אם התוצאה היא 'כשל', המשמעות היא שכל השדות האחרים בתוצאה הזו (חוץ ממזהה הבקשה שצוין למעלה) לא תקינים, והשיטה המתאימה get*
תיכשל עם החריגה IllegalStateException
.
3. קבלת תוצאות לכל מדידה מוצלחת
לכל מדידה מוצלחת (RangingResult
), אפשר לאחזר ערכי תוצאות באמצעות השיטות המתאימות get
:
המרחק במילימטרים וסטיית התקן של המדידה:
עוצמת האות (RSSI) של המנות שמשמשות למדידות:
הזמן באלפיות השנייה שבו בוצעה המדידה (מציין את הזמן מאז האתחול):
מספר המדידות שבוצעו ומספר המדידות שהצליחו (שעליהן מבוססות מדידות המרחק):
זמן המינימום והמקסימום שמכשיר לקוח צריך להמתין בין מדידות של 11az NTB:
getMinTimeBetweenNtbMeasurementsMicros()
ו-getMaxTimeBetweenNtbMeasurementsMicros()
מחזירות את הזמן המינימלי והמקסימלי. אם מתבקשת מדידת טווח הבאה לפני שחלף הזמן המינימלי, ה-API מחזיר את תוצאת מדידת הטווח שנשמרה במטמון. אם הבקשה הבאה למדידת טווח נשלחת אחרי שחלף הזמן המקסימלי, ה-API מסיים את הסשן למדידת טווח שלא מופעל על ידי טריגר ומנהל משא ומתן על סשן חדש למדידת טווח עם התחנה המגיבה. מומלץ להימנע מבקשה של סשן חדש למדידת טווח, כי זה מוסיף תקורה לזמן המדידה של הטווח. כדי לנצל את היתרונות של טווח יעיל מבוסס-non-trigger של 802.11az, צריך להפעיל את בקשת הטווח הבאה בין זמן המדידה המינימלי לזמן המדידה המקסימלי שצוינו במדידה הקודמת שלRangingResult
.חזרות של שדה אימון ארוך (LTF) שמשמשות תחנות משיב ותחנות יוזם בפתיח של תוצאת IEEE 802.11az NTB:
מספר הזרמים המרחביים של זמן השידור והקבלה (STS) שבהם השתמשה תחנת היוזם לתוצאת ה-NTB של IEEE 802.11az:
מכשירי Android שתומכים ב-WiFi-RTT
בטבלאות הבאות מפורטים כמה טלפונים, נקודות גישה ומכשירים בחנויות קמעונאיות, במחסנים ובמרכזי הפצה שתומכים ב-WiFi-RTT. הם לא מקיפים. מומלץ לפנות אלינו כדי להוסיף לכאן את המוצרים שלכם עם תמיכה ב-RTT.
נקודות גישה
היצרן והדגם | תאריך התמיכה | פרוטוקול |
---|---|---|
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+ |
6 Pixel | 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 ומעלה |