1 /* 2 * Copyright 2019 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package com.android.server.wifi; 17 18 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.INVALID; 19 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MAX_CHANNEL_UTILIZATION; 20 import static com.android.server.wifi.util.InformationElementUtil.BssLoad.MIN_CHANNEL_UTILIZATION; 21 22 import android.annotation.NonNull; 23 import android.annotation.Nullable; 24 import android.content.Context; 25 import android.net.wifi.ScanResult; 26 import android.net.wifi.WifiAnnotations.WifiStandard; 27 import android.net.wifi.WifiInfo; 28 import android.net.wifi.nl80211.DeviceWiphyCapabilities; 29 import android.util.Log; 30 31 import com.android.wifi.resources.R; 32 33 /** 34 * A class that predicts network throughput based on RSSI, channel utilization, channel width, 35 * WiFi standard (PHY/MAC mode), Nss and other radio information. 36 */ 37 public class ThroughputPredictor { 38 private static final String TAG = "WifiThroughputPredictor"; 39 private boolean mVerboseLoggingEnabled = false; 40 41 // Default value of channel utilization at 2G when channel utilization is not available from 42 // BssLoad IE or from link layer stats 43 public static final int CHANNEL_UTILIZATION_DEFAULT_2G = MAX_CHANNEL_UTILIZATION * 6 / 16; 44 // Default value of channel utilization at 5G when channel utilization is not available from 45 // BssLoad IE or from link layer stats 46 public static final int CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G = MAX_CHANNEL_UTILIZATION / 16; 47 // Channel utilization boost when bluetooth is in the connected mode 48 public static final int CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G = MAX_CHANNEL_UTILIZATION / 4; 49 //TODO: b/145133625 Need to consider 6GHz 50 51 // Number of data tones per OFDM symbol 52 private static final int NUM_TONE_PER_SYM_LEGACY = 48; 53 private static final int NUM_TONE_PER_SYM_11N_20MHZ = 52; 54 private static final int NUM_TONE_PER_SYM_11N_40MHZ = 108; 55 private static final int NUM_TONE_PER_SYM_11AC_20MHZ = 52; 56 private static final int NUM_TONE_PER_SYM_11AC_40MHZ = 108; 57 private static final int NUM_TONE_PER_SYM_11AC_80MHZ = 234; 58 private static final int NUM_TONE_PER_SYM_11AC_160MHZ = 468; 59 private static final int NUM_TONE_PER_SYM_11AX_BE_20MHZ = 234; 60 private static final int NUM_TONE_PER_SYM_11AX_BE_40MHZ = 468; 61 private static final int NUM_TONE_PER_SYM_11AX_BE_80MHZ = 980; 62 private static final int NUM_TONE_PER_SYM_11AX_BE_160MHZ = 1960; 63 private static final int NUM_TONE_PER_SYM_11BE_320MHZ = 1960 * 2; 64 65 // 11ag OFDM symbol duration in ns 66 private static final int SYM_DURATION_LEGACY_NS = 4000; 67 // 11n OFDM symbol duration in ns with 0.4us guard interval 68 private static final int SYM_DURATION_11N_NS = 3600; 69 // 11ac OFDM symbol duration in ns with 0.4us guard interval 70 private static final int SYM_DURATION_11AC_NS = 3600; 71 // 11ax/be OFDM symbol duration in ns with 0.8us guard interval 72 private static final int SYM_DURATION_11AX_BE_NS = 13600; 73 private static final int MICRO_TO_NANO_RATIO = 1000; 74 75 // The scaling factor for integer representation of bitPerTone and MAX_BITS_PER_TONE_XXX 76 private static final int BIT_PER_TONE_SCALE = 1000; 77 private static final int MAX_BITS_PER_TONE_LEGACY = 78 (int) Math.round((6 * 3.0 * BIT_PER_TONE_SCALE) / 4.0); 79 private static final int MAX_BITS_PER_TONE_11N = 80 (int) Math.round((6 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 81 private static final int MAX_BITS_PER_TONE_11AC = 82 (int) Math.round((8 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 83 private static final int MAX_BITS_PER_TONE_11AX = 84 (int) Math.round((10 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 85 private static final int MAX_BITS_PER_TONE_11BE = 86 (int) Math.round((12 * 5.0 * BIT_PER_TONE_SCALE) / 6.0); 87 88 // snrDb-to-bitPerTone lookup table (LUT) used at low SNR 89 // snr = Math.pow(10.0, snrDb / 10.0); 90 // bitPerTone = (int) (Math.log10(1 + snr) / Math.log10(2.0) * BIT_PER_TONE_SCALE) 91 private static final int TWO_IN_DB = 3; 92 private static final int SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / TWO_IN_DB; 93 private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MIN = -10; // minimum snrDb supported by LUT 94 private static final int SNR_DB_TO_BIT_PER_TONE_LUT_MAX = 9; // maximum snrDb supported by LUT 95 private static final int[] SNR_DB_TO_BIT_PER_TONE_LUT = {0, 171, 212, 262, 323, 396, 484, 586, 96 706, 844, 1000, 1176, 1370, 1583, 1812, 2058, 2317, 2588, 2870, 3161}; 97 // Thermal noise floor power in dBm integrated over 20MHz with 5.5dB noise figure at 25C 98 private static final int NOISE_FLOOR_20MHZ_DBM = -96; 99 // A fudge factor to represent HW implementation margin in dB. 100 // Predicted throughput matches pretty well with OTA throughput with this fudge factor. 101 private static final int SNR_MARGIN_DB = 16; 102 private static final int MAX_NUM_SPATIAL_STREAM_11BE = 16; 103 private static final int MAX_NUM_SPATIAL_STREAM_11AX = 8; 104 private static final int MAX_NUM_SPATIAL_STREAM_11AC = 8; 105 private static final int MAX_NUM_SPATIAL_STREAM_11N = 4; 106 private static final int MAX_NUM_SPATIAL_STREAM_LEGACY = 1; 107 108 private static final int B_MODE_MAX_MBPS = 11; 109 private final Context mContext; 110 ThroughputPredictor(Context context)111 ThroughputPredictor(Context context) { 112 mContext = context; 113 } 114 115 /** 116 * Enable/Disable verbose logging. 117 * 118 * @param verbose true to enable and false to disable. 119 */ enableVerboseLogging(boolean verbose)120 public void enableVerboseLogging(boolean verbose) { 121 mVerboseLoggingEnabled = verbose; 122 } 123 124 /** 125 * Predict maximum Tx throughput supported by connected network at the highest RSSI 126 * with the lowest channel utilization 127 * @return predicted maximum Tx throughput in Mbps 128 */ predictMaxTxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities)129 public int predictMaxTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) { 130 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 131 capabilities.channelBandwidth, WifiInfo.MAX_RSSI, 132 capabilities.maxNumberTxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0, null); 133 } 134 135 /** 136 * Predict maximum Rx throughput supported by connected network at the highest RSSI 137 * with the lowest channel utilization 138 * @return predicted maximum Rx throughput in Mbps 139 */ predictMaxRxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities)140 public int predictMaxRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities) { 141 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 142 capabilities.channelBandwidth, WifiInfo.MAX_RSSI, 143 capabilities.maxNumberRxSpatialStreams, MIN_CHANNEL_UTILIZATION, 0, null); 144 } 145 146 /** 147 * Predict Tx throughput with current connection capabilities, RSSI and channel utilization 148 * @return predicted Tx throughput in Mbps 149 */ predictTxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization)150 public int predictTxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, 151 int rssiDbm, int frequency, int channelUtilization) { 152 int channelUtilizationFinal = getValidChannelUtilization(frequency, 153 INVALID, channelUtilization, false); 154 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 155 capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberTxSpatialStreams, 156 channelUtilizationFinal, frequency, null); 157 } 158 159 /** 160 * Predict Rx throughput with current connection capabilities, RSSI and channel utilization 161 * @return predicted Rx throughput in Mbps 162 */ predictRxThroughput(@onNull WifiNative.ConnectionCapabilities capabilities, int rssiDbm, int frequency, int channelUtilization)163 public int predictRxThroughput(@NonNull WifiNative.ConnectionCapabilities capabilities, 164 int rssiDbm, int frequency, int channelUtilization) { 165 int channelUtilizationFinal = getValidChannelUtilization(frequency, 166 INVALID, channelUtilization, false); 167 return predictThroughputInternal(capabilities.wifiStandard, capabilities.is11bMode, 168 capabilities.channelBandwidth, rssiDbm, capabilities.maxNumberRxSpatialStreams, 169 channelUtilizationFinal, frequency, null); 170 } 171 172 /** 173 * Predict network throughput given by the current channel condition and RSSI 174 * @param deviceCapabilities Phy Capabilities of the device 175 * @param wifiStandardAp the highest wifi standard supported by AP 176 * @param channelWidthAp the channel bandwidth of AP 177 * @param rssiDbm the scan RSSI in dBm 178 * @param frequency the center frequency of primary 20MHz channel 179 * @param maxNumSpatialStreamAp the maximum number of spatial streams supported by AP 180 * @param channelUtilizationBssLoad the channel utilization ratio indicated from BssLoad IE 181 * @param channelUtilizationLinkLayerStats the channel utilization ratio detected from scan 182 * @param isBluetoothConnected whether the bluetooth adaptor is in connected mode 183 * @param disabledSubchannelBitmap the disabled Subchannel Bitmap (2 bytes) from EHT 184 * Operation IE 185 * @return predicted throughput in Mbps 186 */ predictThroughput(DeviceWiphyCapabilities deviceCapabilities, @WifiStandard int wifiStandardAp, int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp, int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected, @Nullable byte[] disabledSubchannelBitmap)187 public int predictThroughput(DeviceWiphyCapabilities deviceCapabilities, 188 @WifiStandard int wifiStandardAp, 189 int channelWidthAp, int rssiDbm, int frequency, int maxNumSpatialStreamAp, 190 int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, 191 boolean isBluetoothConnected, @Nullable byte[] disabledSubchannelBitmap) { 192 193 if (deviceCapabilities == null) { 194 Log.e(TAG, "Null device capabilities passed to throughput predictor"); 195 return 0; 196 } 197 198 int maxNumSpatialStreamDevice = Math.min(deviceCapabilities.getMaxNumberTxSpatialStreams(), 199 deviceCapabilities.getMaxNumberRxSpatialStreams()); 200 201 if (mContext.getResources().getBoolean( 202 R.bool.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideEnable)) { 203 maxNumSpatialStreamDevice = mContext.getResources().getInteger( 204 R.integer.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideValue); 205 } 206 207 int maxNumSpatialStream = Math.min(maxNumSpatialStreamDevice, maxNumSpatialStreamAp); 208 209 // Get minimum standard support between device and AP 210 int wifiStandard; 211 switch (wifiStandardAp) { 212 case ScanResult.WIFI_STANDARD_11BE: 213 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)) { 214 wifiStandard = ScanResult.WIFI_STANDARD_11BE; 215 break; 216 } 217 //FALL THROUGH 218 case ScanResult.WIFI_STANDARD_11AX: 219 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AX)) { 220 wifiStandard = ScanResult.WIFI_STANDARD_11AX; 221 break; 222 } 223 //FALL THROUGH 224 case ScanResult.WIFI_STANDARD_11AC: 225 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AC)) { 226 wifiStandard = ScanResult.WIFI_STANDARD_11AC; 227 break; 228 } 229 //FALL THROUGH 230 case ScanResult.WIFI_STANDARD_11N: 231 if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11N)) { 232 wifiStandard = ScanResult.WIFI_STANDARD_11N; 233 break; 234 } 235 //FALL THROUGH 236 default: 237 wifiStandard = ScanResult.WIFI_STANDARD_LEGACY; 238 } 239 240 // Calculate channel width 241 int channelWidth; 242 switch (channelWidthAp) { 243 case ScanResult.CHANNEL_WIDTH_320MHZ: 244 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_320MHZ)) { 245 channelWidth = ScanResult.CHANNEL_WIDTH_320MHZ; 246 break; 247 } 248 // FALL THROUGH 249 case ScanResult.CHANNEL_WIDTH_160MHZ: 250 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_160MHZ)) { 251 channelWidth = ScanResult.CHANNEL_WIDTH_160MHZ; 252 break; 253 } 254 // FALL THROUGH 255 case ScanResult.CHANNEL_WIDTH_80MHZ: 256 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)) { 257 channelWidth = ScanResult.CHANNEL_WIDTH_80MHZ; 258 break; 259 } 260 // FALL THROUGH 261 case ScanResult.CHANNEL_WIDTH_40MHZ: 262 if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)) { 263 channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ; 264 break; 265 } 266 // FALL THROUGH 267 default: 268 channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ; 269 } 270 271 if (mVerboseLoggingEnabled) { 272 StringBuilder sb = new StringBuilder(); 273 Log.d(TAG, sb.append("AP Nss: ").append(maxNumSpatialStreamAp) 274 .append(", Device Nss: ").append(maxNumSpatialStreamDevice) 275 .append(", freq: ").append(frequency) 276 .toString()); 277 } 278 279 int channelUtilization = getValidChannelUtilization(frequency, 280 channelUtilizationBssLoad, 281 channelUtilizationLinkLayerStats, 282 isBluetoothConnected); 283 284 return predictThroughputInternal(wifiStandard, false/* is11bMode */, channelWidth, 285 rssiDbm, maxNumSpatialStream, channelUtilization, frequency, 286 disabledSubchannelBitmap); 287 } 288 predictThroughputInternal(@ifiStandard int wifiStandard, boolean is11bMode, int channelWidth, int rssiDbm, int maxNumSpatialStream, int channelUtilization, int frequency, @Nullable byte[] disabledSubchannelBitmap)289 private int predictThroughputInternal(@WifiStandard int wifiStandard, boolean is11bMode, 290 int channelWidth, int rssiDbm, int maxNumSpatialStream, int channelUtilization, 291 int frequency, @Nullable byte[] disabledSubchannelBitmap) { 292 293 // channel bandwidth in MHz = 20MHz * (2 ^ channelWidthFactor); 294 int channelWidthFactor; 295 int numTonePerSym; 296 int symDurationNs; 297 int maxBitsPerTone; 298 if (maxNumSpatialStream < 1) { 299 Log.e(TAG, "maxNumSpatialStream < 1 due to wrong implementation. Overridden to 1"); 300 maxNumSpatialStream = 1; 301 } 302 if (wifiStandard == ScanResult.WIFI_STANDARD_LEGACY) { 303 // For simplicity, use legacy OFDM parameters to predict 11b rate 304 numTonePerSym = NUM_TONE_PER_SYM_LEGACY; 305 channelWidthFactor = 0; 306 maxNumSpatialStream = MAX_NUM_SPATIAL_STREAM_LEGACY; 307 maxBitsPerTone = MAX_BITS_PER_TONE_LEGACY; 308 symDurationNs = SYM_DURATION_LEGACY_NS; 309 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11N) { 310 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 311 numTonePerSym = NUM_TONE_PER_SYM_11N_20MHZ; 312 channelWidthFactor = 0; 313 } else { 314 numTonePerSym = NUM_TONE_PER_SYM_11N_40MHZ; 315 channelWidthFactor = 1; 316 } 317 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11N); 318 maxBitsPerTone = MAX_BITS_PER_TONE_11N; 319 symDurationNs = SYM_DURATION_11N_NS; 320 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AC) { 321 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 322 numTonePerSym = NUM_TONE_PER_SYM_11AC_20MHZ; 323 channelWidthFactor = 0; 324 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { 325 numTonePerSym = NUM_TONE_PER_SYM_11AC_40MHZ; 326 channelWidthFactor = 1; 327 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { 328 numTonePerSym = NUM_TONE_PER_SYM_11AC_80MHZ; 329 channelWidthFactor = 2; 330 } else { 331 numTonePerSym = NUM_TONE_PER_SYM_11AC_160MHZ; 332 channelWidthFactor = 3; 333 } 334 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AC); 335 maxBitsPerTone = MAX_BITS_PER_TONE_11AC; 336 symDurationNs = SYM_DURATION_11AC_NS; 337 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11AX) { 338 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 339 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_20MHZ; 340 channelWidthFactor = 0; 341 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { 342 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_40MHZ; 343 channelWidthFactor = 1; 344 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { 345 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_80MHZ; 346 channelWidthFactor = 2; 347 } else { 348 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_160MHZ; 349 channelWidthFactor = 3; 350 } 351 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11AX); 352 maxBitsPerTone = MAX_BITS_PER_TONE_11AX; 353 symDurationNs = SYM_DURATION_11AX_BE_NS; 354 } else if (wifiStandard == ScanResult.WIFI_STANDARD_11BE) { 355 if (channelWidth == ScanResult.CHANNEL_WIDTH_20MHZ) { 356 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_20MHZ; 357 channelWidthFactor = 0; 358 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_40MHZ) { 359 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_40MHZ; 360 channelWidthFactor = 1; 361 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_80MHZ) { 362 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_80MHZ; 363 channelWidthFactor = 2; 364 } else if (channelWidth == ScanResult.CHANNEL_WIDTH_160MHZ) { 365 numTonePerSym = NUM_TONE_PER_SYM_11AX_BE_160MHZ; 366 channelWidthFactor = 3; 367 } else { 368 numTonePerSym = NUM_TONE_PER_SYM_11BE_320MHZ; 369 channelWidthFactor = 4; 370 } 371 int numPunctured20MhzSubChannel = 0; 372 if (disabledSubchannelBitmap != null && disabledSubchannelBitmap.length == 2) { 373 numPunctured20MhzSubChannel = Integer.bitCount( 374 (disabledSubchannelBitmap[1] << 8) | disabledSubchannelBitmap[0]); 375 } 376 if (numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ < numTonePerSym) { 377 numTonePerSym -= numPunctured20MhzSubChannel * NUM_TONE_PER_SYM_11AX_BE_20MHZ; 378 } 379 maxNumSpatialStream = Math.min(maxNumSpatialStream, MAX_NUM_SPATIAL_STREAM_11BE); 380 maxBitsPerTone = MAX_BITS_PER_TONE_11BE; 381 symDurationNs = SYM_DURATION_11AX_BE_NS; 382 } else { 383 return WifiInfo.LINK_SPEED_UNKNOWN; 384 } 385 386 // 6Ghz RSSI boost 387 if (mContext.getResources().getBoolean(R.bool.config_wifiEnable6GhzBeaconRssiBoost) 388 && ScanResult.is6GHz(frequency)) { 389 switch (channelWidth) { 390 case ScanResult.CHANNEL_WIDTH_40MHZ: 391 rssiDbm += 3; 392 break; 393 case ScanResult.CHANNEL_WIDTH_80MHZ: 394 rssiDbm += 6; 395 break; 396 case ScanResult.CHANNEL_WIDTH_160MHZ: 397 rssiDbm += 9; 398 break; 399 case ScanResult.CHANNEL_WIDTH_320MHZ: 400 rssiDbm += 12; 401 break; 402 default: 403 // do nothing 404 } 405 } 406 407 // noiseFloorDbBoost = 10 * log10 * (2 ^ channelWidthFactor) 408 int noiseFloorDbBoost = TWO_IN_DB * channelWidthFactor; 409 int noiseFloorDbm = NOISE_FLOOR_20MHZ_DBM + noiseFloorDbBoost + SNR_MARGIN_DB; 410 int snrDb = rssiDbm - noiseFloorDbm; 411 412 int bitPerTone = calculateBitPerTone(snrDb); 413 bitPerTone = Math.min(bitPerTone, maxBitsPerTone); 414 415 long bitPerToneTotal = bitPerTone * maxNumSpatialStream; 416 long numBitPerSym = bitPerToneTotal * numTonePerSym; 417 int phyRateMbps = (int) ((numBitPerSym * MICRO_TO_NANO_RATIO) 418 / (symDurationNs * BIT_PER_TONE_SCALE)); 419 420 int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor); 421 422 int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION; 423 424 if (is11bMode) { 425 throughputMbps = Math.min(throughputMbps, B_MODE_MAX_MBPS); 426 } 427 if (mVerboseLoggingEnabled) { 428 StringBuilder sb = new StringBuilder(); 429 Log.d(TAG, sb.append(" BW: ").append(channelWidth) 430 .append(" RSSI: ").append(rssiDbm) 431 .append(" Nss: ").append(maxNumSpatialStream) 432 .append(" Mode: ").append(wifiStandard) 433 .append(" symDur: ").append(symDurationNs) 434 .append(" snrDb ").append(snrDb) 435 .append(" bitPerTone: ").append(bitPerTone) 436 .append(" rate: ").append(phyRateMbps) 437 .append(" throughput: ").append(throughputMbps) 438 .toString()); 439 } 440 return throughputMbps; 441 } 442 443 // Calculate the number of bits per tone based on the input of SNR in dB 444 // The output is scaled up by BIT_PER_TONE_SCALE for integer representation calculateBitPerTone(int snrDb)445 private static int calculateBitPerTone(int snrDb) { 446 int bitPerTone; 447 if (snrDb <= SNR_DB_TO_BIT_PER_TONE_LUT_MAX) { 448 int lut_in_idx = Math.max(snrDb, SNR_DB_TO_BIT_PER_TONE_LUT_MIN) 449 - SNR_DB_TO_BIT_PER_TONE_LUT_MIN; 450 lut_in_idx = Math.min(lut_in_idx, SNR_DB_TO_BIT_PER_TONE_LUT.length - 1); 451 bitPerTone = SNR_DB_TO_BIT_PER_TONE_LUT[lut_in_idx]; 452 } else { 453 // bitPerTone = Math.log10(1+snr)/Math.log10(2) can be approximated as 454 // Math.log10(snr) / 0.3 = log10(10^(snrDb/10)) / 0.3 = snrDb / 3 455 // SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE = BIT_PER_TONE_SCALE / 3 456 bitPerTone = snrDb * SNR_DB_TO_BIT_PER_TONE_HIGH_SNR_SCALE; 457 } 458 return bitPerTone; 459 } 460 getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, int channelUtilizationLinkLayerStats, boolean isBluetoothConnected)461 private int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad, 462 int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) { 463 int channelUtilization; 464 boolean is2G = ScanResult.is24GHz(frequency); 465 if (isValidUtilizationRatio(channelUtilizationBssLoad)) { 466 channelUtilization = channelUtilizationBssLoad; 467 } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) { 468 channelUtilization = channelUtilizationLinkLayerStats; 469 } else { 470 channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G : 471 CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G; 472 } 473 474 if (is2G && isBluetoothConnected) { 475 channelUtilization += CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G; 476 channelUtilization = Math.min(channelUtilization, MAX_CHANNEL_UTILIZATION); 477 } 478 if (mVerboseLoggingEnabled) { 479 StringBuilder sb = new StringBuilder(); 480 Log.d(TAG, sb.append(" utilization (BssLoad) ").append(channelUtilizationBssLoad) 481 .append(" utilization (LLStats) ").append(channelUtilizationLinkLayerStats) 482 .append(" isBluetoothConnected: ").append(isBluetoothConnected) 483 .append(" final utilization: ").append(channelUtilization) 484 .toString()); 485 } 486 return channelUtilization; 487 } 488 489 /** 490 * Check if the channel utilization ratio is valid 491 */ isValidUtilizationRatio(int utilizationRatio)492 private static boolean isValidUtilizationRatio(int utilizationRatio) { 493 return (utilizationRatio <= MAX_CHANNEL_UTILIZATION 494 && utilizationRatio >= MIN_CHANNEL_UTILIZATION); 495 } 496 497 // Calculate the available airtime fraction value which is multiplied by 498 // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as 499 // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION calculateAirTimeFraction(int channelUtilization, int channelWidthFactor)500 private int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) { 501 int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization; 502 int airTimeFraction = airTimeFraction20MHz; 503 // For the cases of 40MHz or above, need to take 504 // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor) 505 // because channelUtilization is defined for primary 20MHz channel 506 for (int i = 1; i <= channelWidthFactor; ++i) { 507 airTimeFraction *= airTimeFraction; 508 airTimeFraction /= MAX_CHANNEL_UTILIZATION; 509 } 510 if (mVerboseLoggingEnabled) { 511 Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction); 512 } 513 return airTimeFraction; 514 } 515 } 516