/*
|
* Copyright (C) 2017 The Android Open Source Project
|
*
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
* you may not use this file except in compliance with the License.
|
* You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing, software
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
* See the License for the specific language governing permissions and
|
* limitations under the License.
|
*/
|
|
package com.android.server.wifi;
|
|
import android.net.wifi.WifiInfo;
|
|
import com.android.server.wifi.util.KalmanFilter;
|
import com.android.server.wifi.util.Matrix;
|
|
/**
|
* Class used to calculate scores for connected wifi networks and report it to the associated
|
* network agent.
|
*/
|
public class VelocityBasedConnectedScore extends ConnectedScore {
|
|
private final ScoringParams mScoringParams;
|
|
private int mFrequency = ScoringParams.BAND5;
|
private double mThresholdAdjustment;
|
private final KalmanFilter mFilter;
|
private long mLastMillis;
|
|
public VelocityBasedConnectedScore(ScoringParams scoringParams, Clock clock) {
|
super(clock);
|
mScoringParams = scoringParams;
|
mFilter = new KalmanFilter();
|
mFilter.mH = new Matrix(2, new double[]{1.0, 0.0});
|
mFilter.mR = new Matrix(1, new double[]{1.0});
|
}
|
|
/**
|
* Set the Kalman filter's state transition matrix F and process noise covariance Q given
|
* a time step.
|
*
|
* @param dt delta time, in seconds
|
*/
|
private void setDeltaTimeSeconds(double dt) {
|
mFilter.mF = new Matrix(2, new double[]{1.0, dt, 0.0, 1.0});
|
Matrix tG = new Matrix(1, new double[]{0.5 * dt * dt, dt});
|
double stda = 0.02; // standard deviation of modelled acceleration
|
mFilter.mQ = tG.dotTranspose(tG).dot(new Matrix(2, new double[]{
|
stda * stda, 0.0,
|
0.0, stda * stda}));
|
}
|
/**
|
* Reset the filter state.
|
*/
|
@Override
|
public void reset() {
|
mLastMillis = 0;
|
mThresholdAdjustment = 0;
|
mFilter.mx = null;
|
}
|
|
/**
|
* Updates scoring state using RSSI and measurement noise estimate
|
* <p>
|
* This is useful if an RSSI comes from another source (e.g. scan results) and the
|
* expected noise varies by source.
|
*
|
* @param rssi signal strength (dB).
|
* @param millis millisecond-resolution time.
|
* @param standardDeviation of the RSSI.
|
*/
|
@Override
|
public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
|
if (millis <= 0) return;
|
if (mLastMillis <= 0 || millis < mLastMillis || mFilter.mx == null) {
|
double initialVariance = 9.0 * standardDeviation * standardDeviation;
|
mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});
|
mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0});
|
} else {
|
double dt = (millis - mLastMillis) * 0.001;
|
mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
|
setDeltaTimeSeconds(dt);
|
mFilter.predict();
|
mFilter.update(new Matrix(1, new double[]{rssi}));
|
}
|
mLastMillis = millis;
|
mFilteredRssi = mFilter.mx.get(0, 0);
|
mEstimatedRateOfRssiChange = mFilter.mx.get(1, 0);
|
}
|
|
/**
|
* Updates the state.
|
*/
|
@Override
|
public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
|
int frequency = wifiInfo.getFrequency();
|
if (frequency != mFrequency) {
|
mLastMillis = 0; // Probably roamed; reset filter but retain threshold adjustment
|
// Consider resetting or partially resetting threshold adjustment
|
// Consider checking bssid
|
mFrequency = frequency;
|
}
|
updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation);
|
adjustThreshold(wifiInfo);
|
}
|
|
private double mFilteredRssi;
|
private double mEstimatedRateOfRssiChange;
|
|
/**
|
* Returns the most recently computed extimate of the RSSI.
|
*/
|
public double getFilteredRssi() {
|
return mFilteredRssi;
|
}
|
|
/**
|
* Returns the estimated rate of change of RSSI, in dB/second
|
*/
|
public double getEstimatedRateOfRssiChange() {
|
return mEstimatedRateOfRssiChange;
|
}
|
|
/**
|
* Returns the adjusted RSSI threshold
|
*/
|
public double getAdjustedRssiThreshold() {
|
return mScoringParams.getExitRssi(mFrequency) + mThresholdAdjustment;
|
}
|
|
private double mMinimumPpsForMeasuringSuccess = 2.0;
|
|
/**
|
* Adjusts the threshold if appropriate
|
* <p>
|
* If the (filtered) rssi is near or below the current effective threshold, and the
|
* rate of rssi change is small, and there is traffic, and the error rate is looking
|
* reasonable, then decrease the effective threshold to keep from dropping a perfectly good
|
* connection.
|
*
|
*/
|
private void adjustThreshold(WifiInfo wifiInfo) {
|
if (mThresholdAdjustment < -7) return;
|
if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return;
|
if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return;
|
double txSuccessPps = wifiInfo.txSuccessRate;
|
double rxSuccessPps = wifiInfo.rxSuccessRate;
|
if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return;
|
if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return;
|
double txBadPps = wifiInfo.txBadRate;
|
double txRetriesPps = wifiInfo.txRetriesRate;
|
double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps + txRetriesPps);
|
if (probabilityOfSuccessfulTx > 0.2) {
|
// May want this amount to vary with how close to threshold we are
|
mThresholdAdjustment -= 0.5;
|
}
|
}
|
|
/**
|
* Velocity scorer - predict the rssi a few seconds from now
|
*/
|
@Override
|
public int generateScore() {
|
if (mFilter.mx == null) return WIFI_TRANSITION_SCORE + 1;
|
double badRssi = getAdjustedRssiThreshold();
|
double horizonSeconds = mScoringParams.getHorizonSeconds();
|
Matrix x = new Matrix(mFilter.mx);
|
double filteredRssi = x.get(0, 0);
|
setDeltaTimeSeconds(horizonSeconds);
|
x = mFilter.mF.dot(x);
|
double forecastRssi = x.get(0, 0);
|
if (forecastRssi > filteredRssi) {
|
forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase
|
}
|
int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE;
|
return score;
|
}
|
}
|