אפשר להשתמש בפונקציות של מיקום Wi-Fi שמספק Wi-Fi RTT (Round-Trip-Time) API כדי למדוד את המרחק מנקודות גישה בקרבת מקום עם תמיכה ב-RTT וממכשירים אחרים עם תמיכה ב-Wi-Fi Aware.
אם מודדים את המרחק לשלוש נקודות גישה או יותר, אפשר להשתמש באלגוריתם של טריאנגולציה כדי להעריך את המיקום של המכשיר שמתאים בצורה הטובה ביותר למדידות האלה. התוצאה בדרך כלל מדויקת בטווח של 1-2 מטרים.
בעזרת הדיוק הזה אפשר לפתח שירותים מבוססי-מיקום ברמת פירוט גבוהה, כמו ניווט בתוך מבנים, שליטה קולית עם הסרת סתירות (לדוגמה, 'הפעלת הנורה הזו') ומידע מבוסס-מיקום (לדוגמה, 'האם יש מבצעים מיוחדים על המוצר הזה?').
המכשיר המבקש לא צריך להתחבר לנקודות הגישה כדי למדוד את המרחק באמצעות Wi-Fi RTT. כדי לשמור על הפרטיות, רק המכשיר המבקש יכול לקבוע את המרחק לנקודת הגישה. לנקודות הגישה אין גישה למידע הזה. אין הגבלה על פעולות RTT ב-Wi-Fi באפליקציות בחזית, אבל הן מוגבלות באפליקציות ברקע.
זמן ה-RTT ב-Wi-Fi והיכולות הקשורות של מדידה מדויקת בזמן (FTM) מצוינים בתקן IEEE 802.11-2016. כדי לחשב את המרחק בין שני מכשירים ב-Wi-Fi RTT, צריך למדוד את הזמן המדויק של חבילה שמבצעת נסיעה הלוך ושוב בין המכשירים, ולהכפיל את הזמן הזה במהירות האור. לשם כך, נדרש מדידת הזמן המדויקת שמספקת FTM.
ב-Android 15 (רמת API 35) נוספה תמיכה ב-IEEE 802.11az למדידת מרחק ללא טריגר (NTB).
הבדלים בהטמעה בהתאם לגרסת Android
ה-RTT של Wi-Fi הוצג ב-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) ואילך. כלומר, אם המכשיר תומך במצב ה-NTB של IEEE 802.11az (הסימן הוא WifiRttManager.CHARACTERISTICS_KEY_BOOLEAN_NTB_INITIATOR
), האפליקציה יכולה למצוא נקודות AP שתומכות ב-IEEE 802.11mc וב-IEEE 802.11az באמצעות בקשת טווח אחת. הרחבנו את ממשק ה-API של RangingResult
כדי לספק מידע על הערך המינימלי והמקסימלי שאפשר להשתמש בו במרווח בין מדידות הטווח, כך שהמרווח המדויק יהיה בשליטת האפליקציה.
הדרישות
- החומרה של המכשיר שממנו נשלחת בקשת הטווח חייבת ליישם את תקן 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
כדי לבדוק אם המכשיר תומך ב-RTT ב-Wi-Fi, משתמשים ב-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
), מציינים רשימה של נקודות AP או מכשירי 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
יכולים להכיל נקודות AP נתמכות עם תמיכה ב-IEEE 802.11mc (is80211mcResponder()
) וב-IEEE 802.11az למדידת מרחק ללא טריגר (is80211azNtbResponder()
). במכשירים שתומכים ב-IEEE 802.11az NTB ranging, מתבצעת מדידת טווח של 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.
Request ranging
אפליקציה שולחת בקשת טווח באמצעות השיטה 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
:
- אם כל פעולת הטווח נכשלת, הפונקציה החוזרת (callback)
onRangingFailure
מופעלת עם קוד הסטטוס שמתואר בקטעRangingResultCallback
. כשל כזה יכול לקרות אם השירות לא יכול לבצע פעולת מדידה באותו זמן – למשל, כי ה-Wi-Fi מושבת, כי האפליקציה ביקשה יותר מדי פעולות מדידה והיא מוגבלת, או בגלל בעיה בהרשאות. - כשפעולת הטווח מסתיימת, מתבצעת הפעלה חוזרת של
onRangingResults
עם רשימת תוצאות שתואמת לרשימת הבקשות – תוצאה אחת לכל בקשה. סדר התוצאות לא תואם בהכרח לסדר הבקשות. חשוב לזכור שפעולה של מדידת מרחק עשויה להסתיים, אבל כל תוצאה עדיין עשויה להצביע על כשל במדידה הספציפית הזו.
פירוש תוצאות הטווח
כל אחת מהתוצאות שמוחזרות על ידי הפונקציה הלא סטטית onRangingResults
מוגדרת באמצעות אובייקט RangingResult
. בכל בקשה, מבצעים את הפעולות הבאות.
1. זיהוי הבקשה
מזהים את הבקשה על סמך המידע שסופק בזמן יצירת RangingRequest
: לרוב כתובת MAC שסופקה ב-ScanResult
שמזהה נקודת גישה. אפשר לקבל את כתובת ה-MAC מתוצאת הטווח באמצעות השיטה getMacAddress()
.
רשימת תוצאות הטווח עשויה להיות בסדר שונה מהצוותים (נקודות הגישה) שצוינו בבקשת הטווח, לכן צריך להשתמש בכתובת ה-MAC כדי לזהות את הצוות, ולא בסדר התוצאות.
2. איך בודקים אם כל מדידה בוצעה בהצלחה
כדי לבדוק אם המדידה בוצעה בהצלחה, משתמשים בשיטה getStatus()
. כל ערך שאינו STATUS_SUCCESS
מציין כשל. כשל פירושו שכל שדות התוצאה הזו (למעט מזהה הבקשה שלמעלה) לא חוקיים, וששיטה get*
המתאימה תיכשל עם חריגה מסוג IllegalStateException
.
3. הצגת תוצאות לכל מדידה מוצלחת
לכל מדידה מוצלחת (RangingResult
), אפשר לאחזר את ערכי התוצאה באמצעות השיטות המתאימות של get
:
המרחק, במילימטר, וסטיית התקן של המדידה:
ערך ה-RSSI של החבילות ששימשו למדידות:
הזמן באלפיות השנייה שבו בוצעה המדידה (הזמן שחלף מאז האתחול):
מספר המדידות שניסו לבצע ומספר המדידות שהצליחו (ועליהם מבוססות מדידות המרחק):
משך הזמן המינימלי והמקסימלי שצריך להמתין במכשיר הלקוח בין מדידות של NTB ב-11az:
הפונקציות
getMinTimeBetweenNtbMeasurementsMicros()
ו-getMaxTimeBetweenNtbMeasurementsMicros()
מחזירות את הזמן המינימלי והמקסימלי. אם הבקשה למדידת הטווח הבאה מתבצעת לפני שחלף הזמן המינימלי, ה-API מחזיר את תוצאת הטווח שנשמרה במטמון. אם מדידת הטווח הבאה מופיעה אחרי שחלף הזמן המקסימלי, ה-API מסיים את סשן הטווח ללא הטריגר ומנהל משא ומתן על סשן טווח חדש עם התחנה המגיבת. מומלץ להימנע מבקשה של סשן מדידה חדש, כי היא מוסיפה זמן יתר לזמן המדידה. כדי לנצל את מלוא היתרונות של יעילות הטווח של 802.11az ללא טריגר, צריך להפעיל את בקשת הטווח הבאה בין זמן המדידה המינימלי למקסימלי שצוין במדידה הקודמת שלRangingResult
.חזרות של שדה אימון ארוך (LTF) שתחנות המגיב והמפעיל השתמשו בהן בכותרת של תוצאת NTB ב-IEEE 802.11az:
מספר זרמי הזמן המרחביים (STS) לשידור ולקבלה שבהם התחנה המפעילה השתמשה כדי לקבל את התוצאה של NTB ב-IEEE 802.11az:
מכשירי Android שתומכים ב-WiFi-RTT
בטבלאות הבאות מפורטים חלק מהטלפונים, נקודות הגישה ומכשירים לקמעונאות, למחסנים ולמרכזי הפצה שתומכים ב-WiFi-RTT. הנתונים האלה רחוקים מלהיות מקיפים. אנחנו ממליצים לפנות אלינו כדי לרשום כאן את המוצרים שלכם שתומכים ב-RTT.
נקודות גישה
יצרן ודגם | תאריך התמיכה |
---|---|
Nest Wifi Pro (Wi-Fi 6E) | נתמך |
Compulab WILD AP | נתמך |
Google Wi-Fi | נתמך |
נתב Google Nest Wi-Fi | נתמך |
נקודת Google Nest Wi-Fi | נתמך |
Aruba AP-635 | נתמך |
Cisco 9130 | נתמך |
Cisco 9136 | נתמך |
Cisco 9166 | נתמך |
Cisco 9164 | נתמך |
Aruba AP-505 | נתמך |
Aruba AP-515 | נתמך |
Aruba AP-575 | נתמך |
Aruba AP-518 | נתמך |
Aruba AP-505H | נתמך |
Aruba AP-565 | נתמך |
Aruba AP-535 | נתמך |
טלפונים
יצרן ודגם | גרסת Android |
---|---|
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 ומעלה |