Wi-Fi网络评分机制04_ThroughputScorer(默认评分器)

ThroughputScorer是目前真正使用的评分器,评分算法也相对复杂,会用到协议以及无线通信较多的术语。

详细评分流程

由于该评分器代码较为复杂,下面现分析完整的评分流程,最后再单独介绍下吞吐量估算的代码。

  • rssi和throughput评分: rssiAndThroughputScore

    int rssiBaseScore = calculateRssiScore(candidate);
    int throughputBonusScore = calculateThroughputBonusScore(candidate);
    int rssiAndThroughputScore = rssiBaseScore + throughputBonusScore;
    
  • 当前网络加分: currentNetworkBoost

    int currentNetworkBonusMin = mScoringParams.getCurrentNetworkBonusMin();
            int currentNetworkBonus = Math.max(currentNetworkBonusMin, rssiAndThroughputScore
                    * mScoringParams.getCurrentNetworkBonusPercent() / 100);
    int currentNetworkBoost = (candidate.isCurrentNetwork() && !unExpectedNoInternet)
                    ? currentNetworkBonus : 0;
    

    currentNetworkBonusMin默认值16,可通过 config_wifiFrameworkCurrentNetworkBonusMin 配置
    currentNetworkBonusPercent默认值20,可通过 config_wifiFrameworkCurrentNetworkBonusPercent 配置

  • 安全加分: securityAward

    int securityAward = candidate.isOpenNetwork()
            ? 0
            : mScoringParams.getSecureNetworkBonus();
    

    secureNetworkBonus 默认40分,可通过 config_wifiFrameworkSecureNetworkBonus 配置

  • 计量网络评分: unmeteredAward

    metered network(计量网络)通常是按照流量或时间计费。

    int unmeteredAward = candidate.isMetered()
            ? 0
            : mScoringParams.getUnmeteredNetworkBonus();
    

    对于非计量网络,加1000分。

    unmeteredNetworkBonus 默认1000分,可通过 config_wifiFrameworkUnmeteredNetworkBonus 配置

  • 临时网络评分: savedNetworkAward

    Ephemeral Network也就是临时网络,通常是短暂连接。

    int savedNetworkAward = candidate.isEphemeral() ? 0 : mScoringParams.getSavedNetworkBonus();
    

    非临时网络,加500分。

    savedNetworkBonus 默认500分,可通过 config_wifiFrameworkSavedNetworkBonus 配置

  • 可信网络评分: trustedAward

    int trustedAward = TRUSTED_AWARD; // 1000
    if (!candidate.isTrusted() || candidate.isRestricted()) {
        // Saved networks are not untrusted or restricted, but clear anyway
        savedNetworkAward = 0;
        unmeteredAward = 0; // Ignore metered for untrusted and restricted networks
        if (candidate.isCarrierOrPrivileged()) {
            trustedAward = HALF_TRUSTED_AWARD;
        } else if (candidate.getNominatorId() == NOMINATOR_ID_SCORED) {
            Log.e(TAG, "ScoredNetworkNominator is not carrier or privileged!");
            trustedAward = 0;
        } else {
            trustedAward = 0;
        }
    }
    

    如果是可信网络(trusted and unrestricted), 加1000分。

    对于非可信网络,作如下处理:

    • carrier WiFi or privileged app config

      加一半分, trustedAward = 500

    • 否则

      不加分, trustedAward = 0

  • OEM PAID:

    OEM 付费网络,一般是OEM与移动运营商或其他服务提供商合作,为其设备的用户提供特定的网络服务。

    int notOemPaidAward = NOT_OEM_PAID_AWARD; // 500
    if (candidate.isOemPaid()) {
        savedNetworkAward = 0; // Saved networks are not oem paid, but clear anyway
        unmeteredAward = 0; // Ignore metered for oem paid networks
        trustedAward = 0; // Ignore untrusted for oem paid networks.
        notOemPaidAward = 0;
    }
    
  • OEM PRIVATE:

    OEM 私有网络,通常并不会开放给普通用户,主要为设备的管理和维护提供了专用网络连接。OEM Private Network 可能用于设备的远程监控、诊断、升级或配置,由设备制造商或维护人员使用。

    int notOemPrivateAward = NOT_OEM_PRIVATE_AWARD; // 500
    if (candidate.isOemPrivate()) {
        savedNetworkAward = 0; // Saved networks are not oem paid, but clear anyway
        unmeteredAward = 0; // Ignore metered for oem paid networks
        trustedAward = 0; // Ignore untrusted for oem paid networks.
        notOemPaidAward = 0;
        notOemPrivateAward = 0;
    }
    
  • 计算总分

    先根据主要的字段计算出基础的分数

    int scoreToDetermineBucket = unmeteredAward + savedNetworkAward + trustedAward
                    + notOemPaidAward + notOemPrivateAward + securityAward;
    

    上面参与计算的参数除了securityAward是40分,其他都是500或1000。scoreToDetermineBucket主要用来确定一个大的分数范围,也就是决定在哪个bucket里面。

    下面是scoreWithinBucket

    int scoreWithinBucket = rssiBaseScore + throughputBonusScore + currentNetworkBoost
                    + bandSpecificBonus;
    

    最后通过scoreToDetermineBucketscoreWithinBucket进行第一次打分,桶内(within bucket)分数最高500分,由getScoringBucketStepSize指定

    int score = scoreToDetermineBucket
                    + Math.min(mScoringParams.getScoringBucketStepSize(), scoreWithinBucket);
    

    scoringBucketStepSize默认500,可以通过 config_wifiScoringBucketStepSize 配置

  • 根据当前网络状态调整分数:

    如果当前已连接网络可以访问互联网(currentNetworkHasInternet),但是candidate没有互联网访问能力,那么是不会选择它的,直接赋予0分。

    // do not select a network that has no internet when the current network has internet.
    if (currentNetworkHasInternet && !candidate.isCurrentNetwork() && unExpectedNoInternet) {
        score = 0;
    }
    
  • 根据最近选择调整分数:

    如果是recently-selected network,会打一个很高的分数(TOP_TIER_BASE_SCORE = 1000000),也就是说偏向于连接最近选择过的网络。

    如果多个网络都是recently-selected,那么可以通过rssi和throughtput再进行比较。

    if (candidate.getLastSelectionWeight() > 0.0) {
        // Put a recently-selected network in a tier above everything else,
        // but include rssi and throughput contributions for BSSID selection.
        score = TOP_TIER_BASE_SCORE + rssiBaseScore + throughputBonusScore;
    }
    

以上就是ThroughputScorer的评分流程。下面再看一下计算rssiBaseScorethroughputBonusScore使用到的两个函数calculateRssiScore()calculateThroughputBonusScore()

calculateRssiScore()会针对6G网络的不同频宽,进行补偿加分,其他没什么特殊处理。

private int calculateRssiScore(Candidate candidate) {
    int rssiSaturationThreshold = mScoringParams.getSufficientRssi(candidate.getFrequency());
    int rssi = candidate.getScanRssi();
    if (mScoringParams.is6GhzBeaconRssiBoostEnabled()
            && ScanResult.is6GHz(candidate.getFrequency())) {
        switch (candidate.getChannelWidth()) {
            case ScanResult.CHANNEL_WIDTH_40MHZ:
                rssi += 3;
                break;
            case ScanResult.CHANNEL_WIDTH_80MHZ:
                rssi += 6;
                break;
            case ScanResult.CHANNEL_WIDTH_160MHZ:
                rssi += 9;
                break;
            case ScanResult.CHANNEL_WIDTH_320MHZ:
                rssi += 12;
                break;
            default:
                // do nothing
        }
    }

    rssi = Math.min(rssi, rssiSaturationThreshold);
    return (rssi + RSSI_SCORE_OFFSET) * RSSI_SCORE_SLOPE_IS_4;
}

calculateThroughputBonusScore()

吞吐量最高可加320分,由throughputBonusLimit指定,可以通过 config_wifiFrameworkThroughputBonusLimit 配置

以800Mbps为分界线,分别对小于800Mbps部分(throughputUpTo800Mbps)和大于800Mbps部分(throughputMoreThan800Mbps)进行计算后叠加。

int throughputScoreRaw = (throughputUpTo800Mbps
        * mScoringParams.getThroughputBonusNumerator()					// 120
        / mScoringParams.getThroughputBonusDenominator())				// 433
        + (throughputMoreThan800Mbps
        * mScoringParams.getThroughputBonusNumeratorAfter800Mbps()		// 1
        / mScoringParams.getThroughputBonusDenominatorAfter800Mbps());	// 16

代入分子分母,简化后的代码如下

int throughputScoreRaw = (throughputUpTo800Mbps * 120/433 + throughputMoreThan800Mbps /16);

可以看到,800Mbps以上的占比较少,对评分结果的影响也就更小。

得到根据throughputScoreRawthroughputBonusLimit将评分限制在320分以内。

最后看一下是如何拿到吞吐量的int throughput = candidate.getPredictedThroughputMbps();

吞吐量预测

下面是预测吞吐量的方法,是现在WifiNetworkSelector.java

private int predictThroughput(@NonNull ScanDetail scanDetail) {
    if (scanDetail.getScanResult() == null || scanDetail.getNetworkDetail() == null) {
        return 0;
    }
    int channelUtilizationLinkLayerStats = BssLoad.INVALID;
    if (mWifiChannelUtilization != null) {
        channelUtilizationLinkLayerStats =
                mWifiChannelUtilization.getUtilizationRatio(
                        scanDetail.getScanResult().frequency);
    }
    ClientModeManager primaryManager =
            mWifiInjector.getActiveModeWarden().getPrimaryClientModeManager();
    return mThroughputPredictor.predictThroughput(
            primaryManager.getDeviceWiphyCapabilities(),
            scanDetail.getScanResult().getWifiStandard(),
            scanDetail.getScanResult().channelWidth,
            scanDetail.getScanResult().level,
            scanDetail.getScanResult().frequency,
            scanDetail.getNetworkDetail().getMaxNumberSpatialStreams(),
            scanDetail.getNetworkDetail().getChannelUtilization(),
            channelUtilizationLinkLayerStats,
            mWifiGlobals.isBluetoothConnected());
}

channelUtilizationLinkLayerStats 的计算方法目前是固定的:

  • 2G: 80
  • 5G: 15
  • 6G: 10

predictThroughput()

知道了channelUtilizationLinkLayerStats的计算方式,我们再来看看ThroughputPredictorpredictThroughput()方法,传入的参数如下:

  • DeviceWiphyCapabilities
  • wifiStandardAp
    wifi标准
  • channelWidthAp
    频宽,比如20Mhz, 40MHz, 80MHz, 160MHz.
  • rssiDbm
    信号强度
  • frequency
    频率
  • maxNumSpatialStreamAp
    最大支持的空间流数量
  • channelUtilizationBssLoad
    bssload中的channel Utilization字段,它代表了信道繁忙的时间(百分比)。
  • channelUtilizationLinkLayerStats
  • isBluetoothConnected
    蓝牙是否已连接

具体实现如下

  1. 获取当前设备支持的最大空间流数量

    // RX 和 TX 同步
    int maxNumSpatialStreamDevice = Math.min(deviceCapabilities.getMaxNumberTxSpatialStreams(),
            deviceCapabilities.getMaxNumberRxSpatialStreams());
    // 固定配置
    if (mContext.getResources().getBoolean(
            R.bool.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideEnable)) {
        maxNumSpatialStreamDevice = mContext.getResources().getInteger(
                R.integer.config_wifiFrameworkMaxNumSpatialStreamDeviceOverrideValue);
    }
    // 取固定配置和芯片支持的最小值
    int maxNumSpatialStream = Math.min(maxNumSpatialStreamDevice, maxNumSpatialStreamAp);
    
  2. wifi标准确定

    由于AP 和STA支持的WiFi标准一般是不一样的,所以需要同步,优先使用最高标准。

    // Get minimum standard support between device and AP
    int wifiStandard;
    switch (wifiStandardAp) {
        case ScanResult.WIFI_STANDARD_11BE:
            if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11BE)) {
                wifiStandard = ScanResult.WIFI_STANDARD_11BE;
                break;
            }
            //FALL THROUGH
        case ScanResult.WIFI_STANDARD_11AX:
            if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AX)) {
                wifiStandard = ScanResult.WIFI_STANDARD_11AX;
                break;
            }
            //FALL THROUGH
        case ScanResult.WIFI_STANDARD_11AC:
            if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11AC)) {
                wifiStandard = ScanResult.WIFI_STANDARD_11AC;
                break;
            }
            //FALL THROUGH
        case ScanResult.WIFI_STANDARD_11N:
            if (deviceCapabilities.isWifiStandardSupported(ScanResult.WIFI_STANDARD_11N)) {
                wifiStandard = ScanResult.WIFI_STANDARD_11N;
                break;
            }
            //FALL THROUGH
        default:
            wifiStandard = ScanResult.WIFI_STANDARD_LEGACY;
    }
    
  3. 频宽(channelWidth)确定

    和WiFi Standard一样,也需要看看本地设备是否支持对应的频宽,优先选择支持的最大频宽

    switch (channelWidthAp) {
        case ScanResult.CHANNEL_WIDTH_320MHZ:
            if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_320MHZ)) {
                channelWidth = ScanResult.CHANNEL_WIDTH_320MHZ;
                break;
            }
            // FALL THROUGH
        case ScanResult.CHANNEL_WIDTH_160MHZ:
            if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_160MHZ)) {
                channelWidth = ScanResult.CHANNEL_WIDTH_160MHZ;
                break;
            }
            // FALL THROUGH
        case ScanResult.CHANNEL_WIDTH_80MHZ:
            if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_80MHZ)) {
                channelWidth = ScanResult.CHANNEL_WIDTH_80MHZ;
                break;
            }
            // FALL THROUGH
        case ScanResult.CHANNEL_WIDTH_40MHZ:
            if (deviceCapabilities.isChannelWidthSupported(ScanResult.CHANNEL_WIDTH_40MHZ)) {
                channelWidth = ScanResult.CHANNEL_WIDTH_40MHZ;
                break;
            }
            // FALL THROUGH
        default:
            channelWidth = ScanResult.CHANNEL_WIDTH_20MHZ;
    }
    
  4. 信道利用率计算

    int channelUtilization = getValidChannelUtilization(frequency,
            channelUtilizationBssLoad,
            channelUtilizationLinkLayerStats,
            isBluetoothConnected);
    
    private int getValidChannelUtilization(int frequency, int channelUtilizationBssLoad,
            int channelUtilizationLinkLayerStats, boolean isBluetoothConnected) {
        int channelUtilization;
        boolean is2G = ScanResult.is24GHz(frequency);
        if (isValidUtilizationRatio(channelUtilizationBssLoad)) {
            channelUtilization = channelUtilizationBssLoad;
        } else if (isValidUtilizationRatio(channelUtilizationLinkLayerStats)) {
            channelUtilization = channelUtilizationLinkLayerStats;
        } else {
            channelUtilization = is2G ? CHANNEL_UTILIZATION_DEFAULT_2G :
                    CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G;
        }
    
        if (is2G && isBluetoothConnected) {
            channelUtilization += CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G;
            channelUtilization = Math.min(channelUtilization, MAX_CHANNEL_UTILIZATION);
        }
        if (mVerboseLoggingEnabled) {
            StringBuilder sb = new StringBuilder();
            Log.d(TAG, sb.append(" utilization (BssLoad) ").append(channelUtilizationBssLoad)
                    .append(" utilization (LLStats) ").append(channelUtilizationLinkLayerStats)
                    .append(" isBluetoothConnected: ").append(isBluetoothConnected)
                    .append(" final utilization: ").append(channelUtilization)
                    .toString());
        }
        return channelUtilization;
    }
    

    channelUtilization的值在0-255之间。

    如果AP没有提供channelUtilization,那么就是用系统默认的channelUtilizationLinkLayerStats;如果系统没有配置channelUtilizationLinkLayerStats,那么is2G ? CHANNEL_UTILIZATION_DEFAULT_2G : CHANNEL_UTILIZATION_DEFAULT_ABOVE_2G,2G是95,其他是15, 2.4G的信道利用率更高也就是更为繁忙。

    最后,2G情况下,如果连接了蓝牙,信道使用率会加上CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G。因为蓝牙也是在2.4G,与2.4G的wifi有竞争关系,这里粗略地估计出蓝牙对wifi的影响,大约占用1/4的信道使用。

    public static final int MAX_CHANNEL_UTILIZATION = 255;
    public static final int CHANNEL_UTILIZATION_BOOST_BT_CONNECTED_2G = MAX_CHANNEL_UTILIZATION / 4;
    

    至此,信道使用率已经计算完成,它的值在0-255之间。

下面调用predictThroughputInternal()方法利用前面计算出的各种参数进行吞吐量的估算。

predictThroughputInternal()

该方法是真正实现吞吐量估算。现看看它的参数

private int predictThroughputInternal(@WifiStandard int wifiStandard, boolean is11bMode,
        int channelWidth, int rssiDbm, int maxNumSpatialStream,  int channelUtilization,
        int frequency) {
  • wifiStandard
  • is11bMode
    11b几乎已经看不到了,最大传输速率只有11Mbps。
  • channelWidth
  • rssiDbm
  • maxNumSpatialStream
  • channelUtilization
  • frequency
  1. 基础参数计算

    channelWidthFactor: 频宽因子,0(20MHz), 1(40MHz), 2(80MHz), 3(160MHz),4(320MHz)

    numTonePerSym:其实就是子载波数量。表示一次信号传输可用的子载波数量。

    symDurationNs: 一次传输的耗时,仅和具体的标准相关

    maxBitsPerTone: tone指的是子载波,也就是单个子载波一次传输可以携带的数据长度 ???

    maxNumSpatialStream: 空间流数量

    最终结果如下
    在这里插入图片描述

    maxBitsPerTone的计算单独看一下

    private static final int BIT_PER_TONE_SCALE = 1000;
    private static final int MAX_BITS_PER_TONE_LEGACY = (int) Math.round((6 * 3.0 * BIT_PER_TONE_SCALE) / 4.0);
    private static final int MAX_BITS_PER_TONE_11N = 	(int) Math.round((6 * 5.0 * BIT_PER_TONE_SCALE) / 6.0);
    private static final int MAX_BITS_PER_TONE_11AC = 	(int) Math.round((8 * 5.0 * BIT_PER_TONE_SCALE) / 6.0);
    private static final int MAX_BITS_PER_TONE_11AX = 	(int) Math.round((10 * 5.0 * BIT_PER_TONE_SCALE) / 6.0);
    private static final int MAX_BITS_PER_TONE_11BE = 	(int) Math.round((12 * 5.0 * BIT_PER_TONE_SCALE) / 6.0);
    

    它其实是通过码率和符号位长计算出来的:比如11AC支持的最大码率是5/6, 由于其调制方式最高支持64-QAM,所以位长为6bit

  2. RSSI && SNR

    第一步得到的信息只能计算出理论的最高速率,还需要将环境因素考虑进去才能近似估算出实际的速率。环境因素包括信号强度RSSI和信噪比SNR。

    如果配置了config_wifiEnable6GhzBeaconRssiBoost=true,那么需要对6G的RSSI往上微调一下,该功能可以通过配置关闭。这么做的原因是较宽的频宽虽然可以传输更多的数据,但同时也跟容易受到干扰而降低信号质量。主要还是为了提高估算的准确性。

    if (mContext.getResources().getBoolean(R.bool.config_wifiEnable6GhzBeaconRssiBoost)
            && ScanResult.is6GHz(frequency)) {
        switch (channelWidth) {
            case ScanResult.CHANNEL_WIDTH_40MHZ:
                rssiDbm += 3;
                break;
            case ScanResult.CHANNEL_WIDTH_80MHZ:
                rssiDbm += 6;
                break;
            case ScanResult.CHANNEL_WIDTH_160MHZ:
                rssiDbm += 9;
                break;
            case ScanResult.CHANNEL_WIDTH_320MHZ:
                rssiDbm += 12;
                break;
            default:
                // do nothing
        }
    }
    

    SNR估算

    噪声增益计算

    int noiseFloorDbBoost = TWO_IN_DB * channelWidthFactor; // TWO_IN_DB = 3
    

    channelWidthFactor是第一步中根据频宽计算出来的。所以这里应该是根据频宽来调整预期的噪声水平。

    噪声强度计算

    // Thermal noise floor power in dBm integrated over 20MHz with 5.5dB noise figure at 25C
    private static final int NOISE_FLOOR_20MHZ_DBM = -96;
    // A fudge factor to represent HW implementation margin in dB.
    // Predicted throughput matches pretty well with OTA throughput with this fudge factor.
    private static final int SNR_MARGIN_DB = 16;
    
    int noiseFloorDbm = NOISE_FLOOR_20MHZ_DBM + noiseFloorDbBoost + SNR_MARGIN_DB;
    

    NOISE_FLOOR_20MHZ_DBM = -96是20MHz下的噪声强度(以分贝毫瓦dbm为单位),它是室温状态下5.5dB的噪声系数所产生的热噪声强度;SNR_MARGIN_DB=16是为了考虑硬件差异和其他可能的干扰因素而引入的修正值,所以这里计算出的noiseFloorDbm会更加保守,相对更高一些。而noiseFloorDbBoost是前面计算得来用于调整高频宽下的信噪比。

    计算信噪比SNR(Signal-to-Noise Ratio)

    int snrDb  = rssiDbm - noiseFloorDbm;
    
  3. 估算实际速率

    计算实际的每个子载波传输的数据长度 bitPerTone

    int bitPerTone = calculateBitPerTone(snrDb);
    bitPerTone = Math.min(bitPerTone, maxBitsPerTone);
    long bitPerToneTotal = bitPerTone * maxNumSpatialStream;
    long numBitPerSym = bitPerToneTotal * numTonePerSym;
    int phyRateMbps =  (int) ((numBitPerSym * MICRO_TO_NANO_RATIO) / (symDurationNs * BIT_PER_TONE_SCALE));
    

    calculateBitPerTone()方法通过香农定理计算出单个子载波的速率(已经考虑了频宽的影响),然后再乘上空间流数量,子载波数量,最后得到了估算的传输速率phyRateMbps
    S N R = 1 0 s n r D b 10 SNR = 10^{\frac{snrDb}{10} } SNR=1010snrDb

    b p s = b a n d w i d t h × log ⁡ 2 ( 1 + S N R ) bps = bandwidth \times \log_{2}{(1+SNR)} bps=bandwidth×log2(1+SNR)

    最后考虑信道使用率(Channel Utilization)的影响,

    int airTimeFraction = calculateAirTimeFraction(channelUtilization, channelWidthFactor);
    int throughputMbps = (phyRateMbps * airTimeFraction) / MAX_CHANNEL_UTILIZATION;
    

    计算方法如下,channelWidthFactor用来处理非20MHz的情况,因为channelUtilization是为20MHz频宽定义的。

    // Calculate the available airtime fraction value which is multiplied by
    // MAX_CHANNEL_UTILIZATION for integer representation. It is calculated as
    // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) * MAX_CHANNEL_UTILIZATION
    private int calculateAirTimeFraction(int channelUtilization, int channelWidthFactor) {
        int airTimeFraction20MHz = MAX_CHANNEL_UTILIZATION - channelUtilization;
        int airTimeFraction = airTimeFraction20MHz;
        // For the cases of 40MHz or above, need to take
        // (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor)
        // because channelUtilization is defined for primary 20MHz channel
        for (int i = 1; i <= channelWidthFactor; ++i) {
            airTimeFraction *= airTimeFraction;
            airTimeFraction /= MAX_CHANNEL_UTILIZATION;
        }
        if (mVerboseLoggingEnabled) {
            Log.d(TAG, " airTime20: " + airTimeFraction20MHz + " airTime: " + airTimeFraction);
        }
        return airTimeFraction;
    }
    

    (1 - channelUtilization / MAX_CHANNEL_UTILIZATION) ^ (2 ^ channelWidthFactor)是用来计算大于20MHz情况的公式,因为更高的频宽相应的需要更多的信道使用时间。

总结

与其他评分器相比,ThroughputScorer更偏向于实际的传输速率,也更为合理。但是其评分过程涉及到一些吞吐量估算内容,需要一点无线通信的基础才能更好理解。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值