Home | History | Annotate | Download | only in wifi
      1 /*
      2  * Copyright (C) 2017 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 
     17 package com.android.server.wifi;
     18 
     19 import android.net.wifi.WifiInfo;
     20 
     21 import com.android.server.wifi.util.KalmanFilter;
     22 import com.android.server.wifi.util.Matrix;
     23 
     24 /**
     25  * Class used to calculate scores for connected wifi networks and report it to the associated
     26  * network agent.
     27  */
     28 public class VelocityBasedConnectedScore extends ConnectedScore {
     29 
     30     private final ScoringParams mScoringParams;
     31 
     32     private int mFrequency = ScoringParams.BAND5;
     33     private double mThresholdAdjustment;
     34     private final KalmanFilter mFilter;
     35     private long mLastMillis;
     36 
     37     public VelocityBasedConnectedScore(ScoringParams scoringParams, Clock clock) {
     38         super(clock);
     39         mScoringParams = scoringParams;
     40         mFilter = new KalmanFilter();
     41         mFilter.mH = new Matrix(2, new double[]{1.0, 0.0});
     42         mFilter.mR = new Matrix(1, new double[]{1.0});
     43     }
     44 
     45     /**
     46      * Set the Kalman filter's state transition matrix F and process noise covariance Q given
     47      * a time step.
     48      *
     49      * @param dt delta time, in seconds
     50      */
     51     private void setDeltaTimeSeconds(double dt) {
     52         mFilter.mF = new Matrix(2, new double[]{1.0, dt, 0.0, 1.0});
     53         Matrix tG = new Matrix(1, new double[]{0.5 * dt * dt, dt});
     54         double stda = 0.02; // standard deviation of modelled acceleration
     55         mFilter.mQ = tG.dotTranspose(tG).dot(new Matrix(2, new double[]{
     56                 stda * stda, 0.0,
     57                 0.0, stda * stda}));
     58     }
     59     /**
     60      * Reset the filter state.
     61      */
     62     @Override
     63     public void reset() {
     64         mLastMillis = 0;
     65         mThresholdAdjustment = 0;
     66         mFilter.mx = null;
     67     }
     68 
     69     /**
     70      * Updates scoring state using RSSI and measurement noise estimate
     71      * <p>
     72      * This is useful if an RSSI comes from another source (e.g. scan results) and the
     73      * expected noise varies by source.
     74      *
     75      * @param rssi              signal strength (dB).
     76      * @param millis            millisecond-resolution time.
     77      * @param standardDeviation of the RSSI.
     78      */
     79     @Override
     80     public void updateUsingRssi(int rssi, long millis, double standardDeviation) {
     81         if (millis <= 0) return;
     82         if (mLastMillis <= 0 || millis < mLastMillis || mFilter.mx == null) {
     83             double initialVariance = 9.0 * standardDeviation * standardDeviation;
     84             mFilter.mx = new Matrix(1, new double[]{rssi, 0.0});
     85             mFilter.mP = new Matrix(2, new double[]{initialVariance, 0.0, 0.0, 0.0});
     86         } else {
     87             double dt = (millis - mLastMillis) * 0.001;
     88             mFilter.mR.put(0, 0, standardDeviation * standardDeviation);
     89             setDeltaTimeSeconds(dt);
     90             mFilter.predict();
     91             mFilter.update(new Matrix(1, new double[]{rssi}));
     92         }
     93         mLastMillis = millis;
     94         mFilteredRssi = mFilter.mx.get(0, 0);
     95         mEstimatedRateOfRssiChange = mFilter.mx.get(1, 0);
     96     }
     97 
     98     /**
     99      * Updates the state.
    100      */
    101     @Override
    102     public void updateUsingWifiInfo(WifiInfo wifiInfo, long millis) {
    103         int frequency = wifiInfo.getFrequency();
    104         if (frequency != mFrequency) {
    105             mLastMillis = 0; // Probably roamed; reset filter but retain threshold adjustment
    106             // Consider resetting or partially resetting threshold adjustment
    107             // Consider checking bssid
    108             mFrequency = frequency;
    109         }
    110         updateUsingRssi(wifiInfo.getRssi(), millis, mDefaultRssiStandardDeviation);
    111         adjustThreshold(wifiInfo);
    112     }
    113 
    114     private double mFilteredRssi;
    115     private double mEstimatedRateOfRssiChange;
    116 
    117     /**
    118      * Returns the most recently computed extimate of the RSSI.
    119      */
    120     public double getFilteredRssi() {
    121         return mFilteredRssi;
    122     }
    123 
    124     /**
    125      * Returns the estimated rate of change of RSSI, in dB/second
    126      */
    127     public double getEstimatedRateOfRssiChange() {
    128         return mEstimatedRateOfRssiChange;
    129     }
    130 
    131     /**
    132      * Returns the adjusted RSSI threshold
    133      */
    134     public double getAdjustedRssiThreshold() {
    135         return mScoringParams.getExitRssi(mFrequency) + mThresholdAdjustment;
    136     }
    137 
    138     private double mMinimumPpsForMeasuringSuccess = 2.0;
    139 
    140     /**
    141      * Adjusts the threshold if appropriate
    142      * <p>
    143      * If the (filtered) rssi is near or below the current effective threshold, and the
    144      * rate of rssi change is small, and there is traffic, and the error rate is looking
    145      * reasonable, then decrease the effective threshold to keep from dropping a perfectly good
    146      * connection.
    147      *
    148      */
    149     private void adjustThreshold(WifiInfo wifiInfo) {
    150         if (mThresholdAdjustment < -7) return;
    151         if (mFilteredRssi >= getAdjustedRssiThreshold() + 2.0) return;
    152         if (Math.abs(mEstimatedRateOfRssiChange) >= 0.2) return;
    153         double txSuccessPps = wifiInfo.txSuccessRate;
    154         double rxSuccessPps = wifiInfo.rxSuccessRate;
    155         if (txSuccessPps < mMinimumPpsForMeasuringSuccess) return;
    156         if (rxSuccessPps < mMinimumPpsForMeasuringSuccess) return;
    157         double txBadPps = wifiInfo.txBadRate;
    158         double txRetriesPps = wifiInfo.txRetriesRate;
    159         double probabilityOfSuccessfulTx = txSuccessPps / (txSuccessPps + txBadPps + txRetriesPps);
    160         if (probabilityOfSuccessfulTx > 0.2) {
    161             // May want this amount to vary with how close to threshold we are
    162             mThresholdAdjustment -= 0.5;
    163         }
    164     }
    165 
    166     /**
    167      * Velocity scorer - predict the rssi a few seconds from now
    168      */
    169     @Override
    170     public int generateScore() {
    171         if (mFilter.mx == null) return WIFI_TRANSITION_SCORE + 1;
    172         double badRssi = getAdjustedRssiThreshold();
    173         double horizonSeconds = mScoringParams.getHorizonSeconds();
    174         Matrix x = new Matrix(mFilter.mx);
    175         double filteredRssi = x.get(0, 0);
    176         setDeltaTimeSeconds(horizonSeconds);
    177         x = mFilter.mF.dot(x);
    178         double forecastRssi = x.get(0, 0);
    179         if (forecastRssi > filteredRssi) {
    180             forecastRssi = filteredRssi; // Be pessimistic about predicting an actual increase
    181         }
    182         int score = (int) (Math.round(forecastRssi) - badRssi) + WIFI_TRANSITION_SCORE;
    183         return score;
    184     }
    185 }
    186