מיקום Wi-Fi: טווח עם RTT

אפשר להשתמש בפונקציונליות של מיקום באמצעות 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:

  • המרחק במילימטרים וסטיית התקן של המדידה:

    getDistanceMm()

    getDistanceStdDevMm()

  • עוצמת האות (RSSI) של המנות שמשמשות למדידות:

    getRssi()

  • הזמן באלפיות השנייה שבו בוצעה המדידה (מציין את הזמן מאז האתחול):

    getRangingTimestampMillis()

  • מספר המדידות שבוצעו ומספר המדידות שהצליחו (שעליהן מבוססות מדידות המרחק):

    getNumAttemptedMeasurements()

    getNumSuccessfulMeasurements()

  • זמן המינימום והמקסימום שמכשיר לקוח צריך להמתין בין מדידות של 11az NTB:

    getMinTimeBetweenNtbMeasurementsMicros() ו-getMaxTimeBetweenNtbMeasurementsMicros() מחזירות את הזמן המינימלי והמקסימלי. אם מתבקשת מדידת טווח הבאה לפני שחלף הזמן המינימלי, ה-API מחזיר את תוצאת מדידת הטווח שנשמרה במטמון. אם הבקשה הבאה למדידת טווח נשלחת אחרי שחלף הזמן המקסימלי, ה-API מסיים את הסשן למדידת טווח שלא מופעל על ידי טריגר ומנהל משא ומתן על סשן חדש למדידת טווח עם התחנה המגיבה. מומלץ להימנע מבקשה של סשן חדש למדידת טווח, כי זה מוסיף תקורה לזמן המדידה של הטווח. כדי לנצל את היתרונות של טווח יעיל מבוסס-non-trigger של 802.11az, צריך להפעיל את בקשת הטווח הבאה בין זמן המדידה המינימלי לזמן המדידה המקסימלי שצוינו במדידה הקודמת של RangingResult.

  • חזרות של שדה אימון ארוך (LTF) שמשמשות תחנות משיב ותחנות יוזם בפתיח של תוצאת IEEE 802.11az NTB:

    get80211azResponderTxLtfRepetitionsCount()

    get80211azInitiatorTxLtfRepetitionsCount()

  • מספר הזרמים המרחביים של זמן השידור והקבלה (STS) שבהם השתמשה תחנת היוזם לתוצאת ה-NTB של IEEE 802.11az:

    get80211azNumberOfTxSpatialStreams()

    get80211azNumberOfRxSpatialStreams()

מכשירי 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 ומעלה