1 /* 2 * Copyright (C) 2016 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.internal.location.gnssmetrics; 18 19 import android.location.GnssStatus; 20 import android.os.SystemClock; 21 import android.os.SystemProperties; 22 import android.os.connectivity.GpsBatteryStats; 23 import android.server.location.ServerLocationProtoEnums; 24 import android.text.format.DateUtils; 25 import android.util.Base64; 26 import android.util.Log; 27 import android.util.StatsLog; 28 import android.util.TimeUtils; 29 30 import com.android.internal.app.IBatteryStats; 31 import com.android.internal.location.nano.GnssLogsProto.GnssLog; 32 import com.android.internal.location.nano.GnssLogsProto.PowerMetrics; 33 34 import java.util.ArrayList; 35 import java.util.Arrays; 36 import java.util.Collections; 37 38 /** 39 * GnssMetrics: Is used for logging GNSS metrics 40 * 41 * @hide 42 */ 43 public class GnssMetrics { 44 45 private static final String TAG = GnssMetrics.class.getSimpleName(); 46 47 /* Constant which indicates GPS signal quality is as yet unknown */ 48 public static final int GPS_SIGNAL_QUALITY_UNKNOWN = 49 ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_UNKNOWN; // -1 50 51 /* Constant which indicates GPS signal quality is poor */ 52 public static final int GPS_SIGNAL_QUALITY_POOR = 53 ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_POOR; // 0 54 55 /* Constant which indicates GPS signal quality is good */ 56 public static final int GPS_SIGNAL_QUALITY_GOOD = 57 ServerLocationProtoEnums.GPS_SIGNAL_QUALITY_GOOD; // 1 58 59 /* Number of GPS signal quality levels */ 60 public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1; 61 62 /** Default time between location fixes (in millisecs) */ 63 private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000; 64 65 /** The number of hertz in one MHz */ 66 private static final double HZ_PER_MHZ = 1e6; 67 68 /* The time since boot when logging started */ 69 private String logStartInElapsedRealTime; 70 71 /* GNSS power metrics */ 72 private GnssPowerMetrics mGnssPowerMetrics; 73 74 /** Frequency range of GPS L5, Galileo E5a, QZSS J5 frequency band */ 75 private static final double L5_CARRIER_FREQ_RANGE_LOW_HZ = 1164 * HZ_PER_MHZ; 76 private static final double L5_CARRIER_FREQ_RANGE_HIGH_HZ = 1189 * HZ_PER_MHZ; 77 78 /** 79 * A boolean array indicating whether the constellation types have been used in fix. 80 */ 81 private boolean[] mConstellationTypes; 82 83 /** Constructor */ GnssMetrics(IBatteryStats stats)84 public GnssMetrics(IBatteryStats stats) { 85 mGnssPowerMetrics = new GnssPowerMetrics(stats); 86 locationFailureStatistics = new Statistics(); 87 timeToFirstFixSecStatistics = new Statistics(); 88 positionAccuracyMeterStatistics = new Statistics(); 89 topFourAverageCn0Statistics = new Statistics(); 90 mTopFourAverageCn0StatisticsL5 = new Statistics(); 91 mNumSvStatus = 0; 92 mNumL5SvStatus = 0; 93 mNumSvStatusUsedInFix = 0; 94 mNumL5SvStatusUsedInFix = 0; 95 reset(); 96 } 97 98 /** 99 * Logs the status of a location report received from the HAL 100 * 101 * @param isSuccessful 102 */ logReceivedLocationStatus(boolean isSuccessful)103 public void logReceivedLocationStatus(boolean isSuccessful) { 104 if (!isSuccessful) { 105 locationFailureStatistics.addItem(1.0); 106 return; 107 } 108 locationFailureStatistics.addItem(0.0); 109 return; 110 } 111 112 /** 113 * Logs missed reports 114 * 115 * @param desiredTimeBetweenFixesMilliSeconds 116 * @param actualTimeBetweenFixesMilliSeconds 117 */ logMissedReports(int desiredTimeBetweenFixesMilliSeconds, int actualTimeBetweenFixesMilliSeconds)118 public void logMissedReports(int desiredTimeBetweenFixesMilliSeconds, 119 int actualTimeBetweenFixesMilliSeconds) { 120 int numReportMissed = (actualTimeBetweenFixesMilliSeconds / 121 Math.max(DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1; 122 if (numReportMissed > 0) { 123 for (int i = 0; i < numReportMissed; i++) { 124 locationFailureStatistics.addItem(1.0); 125 } 126 } 127 return; 128 } 129 130 /** 131 * Logs time to first fix 132 * 133 * @param timeToFirstFixMilliSeconds 134 */ logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds)135 public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) { 136 timeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds/1000)); 137 return; 138 } 139 140 /** 141 * Logs position accuracy 142 * 143 * @param positionAccuracyMeters 144 */ logPositionAccuracyMeters(float positionAccuracyMeters)145 public void logPositionAccuracyMeters(float positionAccuracyMeters) { 146 positionAccuracyMeterStatistics.addItem((double) positionAccuracyMeters); 147 return; 148 } 149 150 /** 151 * Logs CN0 when at least 4 SVs are available 152 * 153 * @param cn0s 154 * @param numSv 155 * @param svCarrierFreqs 156 */ logCn0(float[] cn0s, int numSv, float[] svCarrierFreqs)157 public void logCn0(float[] cn0s, int numSv, float[] svCarrierFreqs) { 158 // Calculate L5 Cn0 159 logCn0L5(numSv, cn0s, svCarrierFreqs); 160 if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) { 161 if (numSv == 0) { 162 mGnssPowerMetrics.reportSignalQuality(null, 0); 163 } 164 return; 165 } 166 float[] cn0Array = Arrays.copyOf(cn0s, numSv); 167 Arrays.sort(cn0Array); 168 mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv); 169 if (numSv < 4) { 170 return; 171 } 172 if (cn0Array[numSv - 4] > 0.0) { 173 double top4AvgCn0 = 0.0; 174 for (int i = numSv - 4; i < numSv; i++) { 175 top4AvgCn0 += (double) cn0Array[i]; 176 } 177 top4AvgCn0 /= 4; 178 topFourAverageCn0Statistics.addItem(top4AvgCn0); 179 } 180 return; 181 } 182 183 /* Helper function to check if a SV is L5 */ isL5Sv(float carrierFreq)184 private static boolean isL5Sv(float carrierFreq) { 185 return (carrierFreq >= L5_CARRIER_FREQ_RANGE_LOW_HZ 186 && carrierFreq <= L5_CARRIER_FREQ_RANGE_HIGH_HZ); 187 } 188 189 /** 190 * Logs sv status data 191 * 192 * @param svCount 193 * @param svidWithFlags 194 * @param svCarrierFreqs 195 */ logSvStatus(int svCount, int[] svidWithFlags, float[] svCarrierFreqs)196 public void logSvStatus(int svCount, int[] svidWithFlags, float[] svCarrierFreqs) { 197 boolean isL5 = false; 198 // Calculate SvStatus Information 199 for (int i = 0; i < svCount; i++) { 200 if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_HAS_CARRIER_FREQUENCY) != 0) { 201 mNumSvStatus++; 202 isL5 = isL5Sv(svCarrierFreqs[i]); 203 if (isL5) { 204 mNumL5SvStatus++; 205 } 206 if ((svidWithFlags[i] & GnssStatus.GNSS_SV_FLAGS_USED_IN_FIX) != 0) { 207 mNumSvStatusUsedInFix++; 208 if (isL5) { 209 mNumL5SvStatusUsedInFix++; 210 } 211 } 212 } 213 } 214 return; 215 } 216 217 /** 218 * Logs CN0 when at least 4 SVs are available L5 Only 219 * 220 * @param svCount 221 * @param cn0s 222 * @param svCarrierFreqs 223 */ logCn0L5(int svCount, float[] cn0s, float[] svCarrierFreqs)224 private void logCn0L5(int svCount, float[] cn0s, float[] svCarrierFreqs) { 225 if (svCount == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < svCount 226 || svCarrierFreqs == null || svCarrierFreqs.length == 0 227 || svCarrierFreqs.length < svCount) { 228 return; 229 } 230 // Create array list of all L5 satellites in report. 231 ArrayList<Float> CnoL5Array = new ArrayList(); 232 for (int i = 0; i < svCount; i++) { 233 if (isL5Sv(svCarrierFreqs[i])) { 234 CnoL5Array.add(cn0s[i]); 235 } 236 } 237 if (CnoL5Array.size() == 0 || CnoL5Array.size() < 4) { 238 return; 239 } 240 int numSvL5 = CnoL5Array.size(); 241 Collections.sort(CnoL5Array); 242 if (CnoL5Array.get(numSvL5 - 4) > 0.0) { 243 double top4AvgCn0 = 0.0; 244 for (int i = numSvL5 - 4; i < numSvL5; i++) { 245 top4AvgCn0 += (double) CnoL5Array.get(i); 246 } 247 top4AvgCn0 /= 4; 248 mTopFourAverageCn0StatisticsL5.addItem(top4AvgCn0); 249 } 250 return; 251 } 252 253 254 /** 255 * Logs that a constellation type has been observed. 256 */ logConstellationType(int constellationType)257 public void logConstellationType(int constellationType) { 258 if (constellationType >= mConstellationTypes.length) { 259 Log.e(TAG, "Constellation type " + constellationType + " is not valid."); 260 return; 261 } 262 mConstellationTypes[constellationType] = true; 263 } 264 265 /** 266 * Dumps GNSS metrics as a proto string 267 * @return 268 */ dumpGnssMetricsAsProtoString()269 public String dumpGnssMetricsAsProtoString() { 270 GnssLog msg = new GnssLog(); 271 if (locationFailureStatistics.getCount() > 0) { 272 msg.numLocationReportProcessed = locationFailureStatistics.getCount(); 273 msg.percentageLocationFailure = (int) (100.0 * locationFailureStatistics.getMean()); 274 } 275 if (timeToFirstFixSecStatistics.getCount() > 0) { 276 msg.numTimeToFirstFixProcessed = timeToFirstFixSecStatistics.getCount(); 277 msg.meanTimeToFirstFixSecs = (int) timeToFirstFixSecStatistics.getMean(); 278 msg.standardDeviationTimeToFirstFixSecs 279 = (int) timeToFirstFixSecStatistics.getStandardDeviation(); 280 } 281 if (positionAccuracyMeterStatistics.getCount() > 0) { 282 msg.numPositionAccuracyProcessed = positionAccuracyMeterStatistics.getCount(); 283 msg.meanPositionAccuracyMeters = (int) positionAccuracyMeterStatistics.getMean(); 284 msg.standardDeviationPositionAccuracyMeters 285 = (int) positionAccuracyMeterStatistics.getStandardDeviation(); 286 } 287 if (topFourAverageCn0Statistics.getCount() > 0) { 288 msg.numTopFourAverageCn0Processed = topFourAverageCn0Statistics.getCount(); 289 msg.meanTopFourAverageCn0DbHz = topFourAverageCn0Statistics.getMean(); 290 msg.standardDeviationTopFourAverageCn0DbHz 291 = topFourAverageCn0Statistics.getStandardDeviation(); 292 } 293 if (mNumSvStatus > 0) { 294 msg.numSvStatusProcessed = mNumSvStatus; 295 } 296 if (mNumL5SvStatus > 0) { 297 msg.numL5SvStatusProcessed = mNumL5SvStatus; 298 } 299 if (mNumSvStatusUsedInFix > 0) { 300 msg.numSvStatusUsedInFix = mNumSvStatusUsedInFix; 301 } 302 if (mNumL5SvStatusUsedInFix > 0) { 303 msg.numL5SvStatusUsedInFix = mNumL5SvStatusUsedInFix; 304 } 305 if (mTopFourAverageCn0StatisticsL5.getCount() > 0) { 306 msg.numL5TopFourAverageCn0Processed = mTopFourAverageCn0StatisticsL5.getCount(); 307 msg.meanL5TopFourAverageCn0DbHz = mTopFourAverageCn0StatisticsL5.getMean(); 308 msg.standardDeviationL5TopFourAverageCn0DbHz = 309 mTopFourAverageCn0StatisticsL5.getStandardDeviation(); 310 } 311 msg.powerMetrics = mGnssPowerMetrics.buildProto(); 312 msg.hardwareRevision = SystemProperties.get("ro.boot.revision", ""); 313 String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT); 314 reset(); 315 return s; 316 } 317 318 /** 319 * Dumps GNSS Metrics as text 320 * 321 * @return GNSS Metrics 322 */ dumpGnssMetricsAsText()323 public String dumpGnssMetricsAsText() { 324 StringBuilder s = new StringBuilder(); 325 s.append("GNSS_KPI_START").append('\n'); 326 s.append(" KPI logging start time: ").append(logStartInElapsedRealTime).append("\n"); 327 s.append(" KPI logging end time: "); 328 TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s); 329 s.append("\n"); 330 s.append(" Number of location reports: ").append( 331 locationFailureStatistics.getCount()).append("\n"); 332 if (locationFailureStatistics.getCount() > 0) { 333 s.append(" Percentage location failure: ").append( 334 100.0 * locationFailureStatistics.getMean()).append("\n"); 335 } 336 s.append(" Number of TTFF reports: ").append( 337 timeToFirstFixSecStatistics.getCount()).append("\n"); 338 if (timeToFirstFixSecStatistics.getCount() > 0) { 339 s.append(" TTFF mean (sec): ").append(timeToFirstFixSecStatistics.getMean()).append("\n"); 340 s.append(" TTFF standard deviation (sec): ").append( 341 timeToFirstFixSecStatistics.getStandardDeviation()).append("\n"); 342 } 343 s.append(" Number of position accuracy reports: ").append( 344 positionAccuracyMeterStatistics.getCount()).append("\n"); 345 if (positionAccuracyMeterStatistics.getCount() > 0) { 346 s.append(" Position accuracy mean (m): ").append( 347 positionAccuracyMeterStatistics.getMean()).append("\n"); 348 s.append(" Position accuracy standard deviation (m): ").append( 349 positionAccuracyMeterStatistics.getStandardDeviation()).append("\n"); 350 } 351 s.append(" Number of CN0 reports: ").append( 352 topFourAverageCn0Statistics.getCount()).append("\n"); 353 if (topFourAverageCn0Statistics.getCount() > 0) { 354 s.append(" Top 4 Avg CN0 mean (dB-Hz): ").append( 355 topFourAverageCn0Statistics.getMean()).append("\n"); 356 s.append(" Top 4 Avg CN0 standard deviation (dB-Hz): ").append( 357 topFourAverageCn0Statistics.getStandardDeviation()).append("\n"); 358 } 359 s.append(" Total number of sv status messages processed: ").append( 360 mNumSvStatus).append("\n"); 361 s.append(" Total number of L5 sv status messages processed: ").append( 362 mNumL5SvStatus).append("\n"); 363 s.append(" Total number of sv status messages processed, where sv is used in fix: ").append( 364 mNumSvStatusUsedInFix).append("\n"); 365 s.append(" Total number of L5 sv status messages processed, where sv is used in fix: ").append( 366 mNumL5SvStatusUsedInFix).append("\n"); 367 s.append(" Number of L5 CN0 reports: ").append( 368 mTopFourAverageCn0StatisticsL5.getCount()).append("\n"); 369 if (mTopFourAverageCn0StatisticsL5.getCount() > 0) { 370 s.append(" L5 Top 4 Avg CN0 mean (dB-Hz): ").append( 371 mTopFourAverageCn0StatisticsL5.getMean()).append("\n"); 372 s.append(" L5 Top 4 Avg CN0 standard deviation (dB-Hz): ").append( 373 mTopFourAverageCn0StatisticsL5.getStandardDeviation()).append("\n"); 374 } 375 s.append(" Used-in-fix constellation types: "); 376 for (int i = 0; i < mConstellationTypes.length; i++) { 377 if (mConstellationTypes[i]) { 378 s.append(GnssStatus.constellationTypeToString(i)).append(" "); 379 } 380 } 381 s.append("\n"); 382 s.append("GNSS_KPI_END").append("\n"); 383 GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats(); 384 if (stats != null) { 385 s.append("Power Metrics").append("\n"); 386 s.append(" Time on battery (min): " 387 + stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); 388 long[] t = stats.getTimeInGpsSignalQualityLevel(); 389 if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) { 390 s.append(" Amount of time (while on battery) Top 4 Avg CN0 > " + 391 Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) + 392 " dB-Hz (min): ").append(t[1] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); 393 s.append(" Amount of time (while on battery) Top 4 Avg CN0 <= " + 394 Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) + 395 " dB-Hz (min): ").append(t[0] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n"); 396 } 397 s.append(" Energy consumed while on battery (mAh): ").append( 398 stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS)).append("\n"); 399 } 400 s.append("Hardware Version: " + SystemProperties.get("ro.boot.revision", "")).append("\n"); 401 return s.toString(); 402 } 403 404 /** Class for storing statistics */ 405 private class Statistics { 406 407 /** Resets statistics */ reset()408 public void reset() { 409 count = 0; 410 sum = 0.0; 411 sumSquare = 0.0; 412 } 413 414 /** Adds an item */ addItem(double item)415 public void addItem(double item) { 416 count++; 417 sum += item; 418 sumSquare += item * item; 419 } 420 421 /** Returns number of items added */ getCount()422 public int getCount() { 423 return count; 424 } 425 426 /** Returns mean */ getMean()427 public double getMean() { 428 return sum/count; 429 } 430 431 /** Returns standard deviation */ getStandardDeviation()432 public double getStandardDeviation() { 433 double m = sum/count; 434 m = m * m; 435 double v = sumSquare/count; 436 if (v > m) { 437 return Math.sqrt(v - m); 438 } 439 return 0; 440 } 441 442 private int count; 443 private double sum; 444 private double sumSquare; 445 } 446 447 /** Location failure statistics */ 448 private Statistics locationFailureStatistics; 449 450 /** Time to first fix statistics */ 451 private Statistics timeToFirstFixSecStatistics; 452 453 /** Position accuracy statistics */ 454 private Statistics positionAccuracyMeterStatistics; 455 456 /** Top 4 average CN0 statistics */ 457 private Statistics topFourAverageCn0Statistics; 458 459 /** Top 4 average CN0 statistics L5 */ 460 private Statistics mTopFourAverageCn0StatisticsL5; 461 462 /** Total number of sv status messages processed */ 463 private int mNumSvStatus; 464 465 /** Total number of L5 sv status messages processed */ 466 private int mNumL5SvStatus; 467 468 /** Total number of sv status messages processed, where sv is used in fix */ 469 private int mNumSvStatusUsedInFix; 470 471 /** Total number of L5 sv status messages processed, where sv is used in fix */ 472 private int mNumL5SvStatusUsedInFix; 473 474 /** 475 * Resets GNSS metrics 476 */ reset()477 private void reset() { 478 StringBuilder s = new StringBuilder(); 479 TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s); 480 logStartInElapsedRealTime = s.toString(); 481 locationFailureStatistics.reset(); 482 timeToFirstFixSecStatistics.reset(); 483 positionAccuracyMeterStatistics.reset(); 484 topFourAverageCn0Statistics.reset(); 485 mTopFourAverageCn0StatisticsL5.reset(); 486 mNumSvStatus = 0; 487 mNumL5SvStatus = 0; 488 mNumSvStatusUsedInFix = 0; 489 mNumL5SvStatusUsedInFix = 0; 490 resetConstellationTypes(); 491 return; 492 } 493 494 /** Resets {@link #mConstellationTypes} as an all-false boolean array. */ resetConstellationTypes()495 public void resetConstellationTypes() { 496 mConstellationTypes = new boolean[GnssStatus.CONSTELLATION_COUNT]; 497 } 498 499 /* Class for handling GNSS power related metrics */ 500 private class GnssPowerMetrics { 501 502 /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */ 503 public static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0; 504 505 /* Minimum change in Top Four Average CN0 needed to trigger a report */ 506 private static final double REPORTING_THRESHOLD_DB_HZ = 1.0; 507 508 /* BatteryStats API */ 509 private final IBatteryStats mBatteryStats; 510 511 /* Last reported Top Four Average CN0 */ 512 private double mLastAverageCn0; 513 514 /* Last reported signal quality bin (based on Top Four Average CN0) */ 515 private int mLastSignalLevel; 516 GnssPowerMetrics(IBatteryStats stats)517 public GnssPowerMetrics(IBatteryStats stats) { 518 mBatteryStats = stats; 519 // Used to initialize the variable to a very small value (unachievable in practice) so that 520 // the first CNO report will trigger an update to BatteryStats 521 mLastAverageCn0 = -100.0; 522 mLastSignalLevel = GPS_SIGNAL_QUALITY_UNKNOWN; 523 } 524 525 /** 526 * Builds power metrics proto buf. This is included in the gnss proto buf. 527 * @return PowerMetrics 528 */ buildProto()529 public PowerMetrics buildProto() { 530 PowerMetrics p = new PowerMetrics(); 531 GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats(); 532 if (stats != null) { 533 p.loggingDurationMs = stats.getLoggingDurationMs(); 534 p.energyConsumedMah = stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS); 535 long[] t = stats.getTimeInGpsSignalQualityLevel(); 536 p.timeInSignalQualityLevelMs = new long[t.length]; 537 for (int i = 0; i < t.length; i++) { 538 p.timeInSignalQualityLevelMs[i] = t[i]; 539 } 540 } 541 return p; 542 } 543 544 /** 545 * Returns the GPS power stats 546 * @return GpsBatteryStats 547 */ getGpsBatteryStats()548 public GpsBatteryStats getGpsBatteryStats() { 549 try { 550 return mBatteryStats.getGpsBatteryStats(); 551 } catch (Exception e) { 552 Log.w(TAG, "Exception", e); 553 return null; 554 } 555 } 556 557 /** 558 * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If 559 * the number of SVs seen is less than 4, then signal quality is the average CN0. 560 * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ. 561 */ reportSignalQuality(float[] ascendingCN0Array, int numSv)562 public void reportSignalQuality(float[] ascendingCN0Array, int numSv) { 563 double avgCn0 = 0.0; 564 if (numSv > 0) { 565 for (int i = Math.max(0, numSv - 4); i < numSv; i++) { 566 avgCn0 += (double) ascendingCN0Array[i]; 567 } 568 avgCn0 /= Math.min(numSv, 4); 569 } 570 if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) { 571 return; 572 } 573 int signalLevel = getSignalLevel(avgCn0); 574 if (signalLevel != mLastSignalLevel) { 575 StatsLog.write(StatsLog.GPS_SIGNAL_QUALITY_CHANGED, signalLevel); 576 mLastSignalLevel = signalLevel; 577 } 578 try { 579 mBatteryStats.noteGpsSignalQuality(signalLevel); 580 mLastAverageCn0 = avgCn0; 581 } catch (Exception e) { 582 Log.w(TAG, "Exception", e); 583 } 584 return; 585 } 586 587 /** 588 * Obtains signal level based on CN0 589 */ getSignalLevel(double cn0)590 private int getSignalLevel(double cn0) { 591 if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) { 592 return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD; 593 } 594 return GnssMetrics.GPS_SIGNAL_QUALITY_POOR; 595 } 596 } 597 } 598