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.os.SystemClock; 20 21 import android.util.Base64; 22 import android.util.TimeUtils; 23 24 import java.util.Arrays; 25 26 import com.android.internal.location.nano.GnssLogsProto.GnssLog; 27 28 /** 29 * GnssMetrics: Is used for logging GNSS metrics 30 * @hide 31 */ 32 public class GnssMetrics { 33 34 /** Default time between location fixes (in millisecs) */ 35 private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000; 36 37 /* The time since boot when logging started */ 38 private String logStartInElapsedRealTime; 39 40 /** Constructor */ 41 public GnssMetrics() { 42 locationFailureStatistics = new Statistics(); 43 timeToFirstFixSecStatistics = new Statistics(); 44 positionAccuracyMeterStatistics = new Statistics(); 45 topFourAverageCn0Statistics = new Statistics(); 46 reset(); 47 } 48 49 /** 50 * Logs the status of a location report received from the HAL 51 * 52 * @param isSuccessful 53 */ 54 public void logReceivedLocationStatus(boolean isSuccessful) { 55 if (!isSuccessful) { 56 locationFailureStatistics.addItem(1.0); 57 return; 58 } 59 locationFailureStatistics.addItem(0.0); 60 return; 61 } 62 63 /** 64 * Logs missed reports 65 * 66 * @param desiredTimeBetweenFixesMilliSeconds 67 * @param actualTimeBetweenFixesMilliSeconds 68 */ 69 public void logMissedReports(int desiredTimeBetweenFixesMilliSeconds, 70 int actualTimeBetweenFixesMilliSeconds) { 71 int numReportMissed = (actualTimeBetweenFixesMilliSeconds / 72 Math.max(DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1; 73 if (numReportMissed > 0) { 74 for (int i = 0; i < numReportMissed; i++) { 75 locationFailureStatistics.addItem(1.0); 76 } 77 } 78 return; 79 } 80 81 /** 82 * Logs time to first fix 83 * 84 * @param timeToFirstFixMilliSeconds 85 */ 86 public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) { 87 timeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds/1000)); 88 return; 89 } 90 91 /** 92 * Logs position accuracy 93 * 94 * @param positionAccuracyMeters 95 */ 96 public void logPositionAccuracyMeters(float positionAccuracyMeters) { 97 positionAccuracyMeterStatistics.addItem((double) positionAccuracyMeters); 98 return; 99 } 100 101 /* 102 * Logs CN0 when at least 4 SVs are available 103 * 104 */ 105 public void logCn0(float[] cn0s, int numSv) { 106 if (numSv < 4) { 107 return; 108 } 109 float[] cn0Array = Arrays.copyOf(cn0s, numSv); 110 Arrays.sort(cn0Array); 111 if (cn0Array[numSv - 4] > 0.0) { 112 double top4AvgCn0 = 0.0; 113 for (int i = numSv - 4; i < numSv; i++) { 114 top4AvgCn0 += (double) cn0Array[i]; 115 } 116 top4AvgCn0 /= 4; 117 topFourAverageCn0Statistics.addItem(top4AvgCn0); 118 } 119 return; 120 } 121 122 /** 123 * Dumps GNSS metrics as a proto string 124 * @return 125 */ 126 public String dumpGnssMetricsAsProtoString() { 127 GnssLog msg = new GnssLog(); 128 if (locationFailureStatistics.getCount() > 0) { 129 msg.numLocationReportProcessed = locationFailureStatistics.getCount(); 130 msg.percentageLocationFailure = (int) (100.0 * locationFailureStatistics.getMean()); 131 } 132 if (timeToFirstFixSecStatistics.getCount() > 0) { 133 msg.numTimeToFirstFixProcessed = timeToFirstFixSecStatistics.getCount(); 134 msg.meanTimeToFirstFixSecs = (int) timeToFirstFixSecStatistics.getMean(); 135 msg.standardDeviationTimeToFirstFixSecs 136 = (int) timeToFirstFixSecStatistics.getStandardDeviation(); 137 } 138 if (positionAccuracyMeterStatistics.getCount() > 0) { 139 msg.numPositionAccuracyProcessed = positionAccuracyMeterStatistics.getCount(); 140 msg.meanPositionAccuracyMeters = (int) positionAccuracyMeterStatistics.getMean(); 141 msg.standardDeviationPositionAccuracyMeters 142 = (int) positionAccuracyMeterStatistics.getStandardDeviation(); 143 } 144 if (topFourAverageCn0Statistics.getCount() > 0) { 145 msg.numTopFourAverageCn0Processed = topFourAverageCn0Statistics.getCount(); 146 msg.meanTopFourAverageCn0DbHz = topFourAverageCn0Statistics.getMean(); 147 msg.standardDeviationTopFourAverageCn0DbHz 148 = topFourAverageCn0Statistics.getStandardDeviation(); 149 } 150 String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT); 151 reset(); 152 return s; 153 } 154 155 /** 156 * Dumps GNSS Metrics as text 157 * 158 * @return GNSS Metrics 159 */ 160 public String dumpGnssMetricsAsText() { 161 StringBuilder s = new StringBuilder(); 162 s.append("GNSS_KPI_START").append('\n'); 163 s.append(" KPI logging start time: ").append(logStartInElapsedRealTime).append("\n"); 164 s.append(" KPI logging end time: "); 165 TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s); 166 s.append("\n"); 167 s.append(" Number of location reports: ").append( 168 locationFailureStatistics.getCount()).append("\n"); 169 if (locationFailureStatistics.getCount() > 0) { 170 s.append(" Percentage location failure: ").append( 171 100.0 * locationFailureStatistics.getMean()).append("\n"); 172 } 173 s.append(" Number of TTFF reports: ").append( 174 timeToFirstFixSecStatistics.getCount()).append("\n"); 175 if (timeToFirstFixSecStatistics.getCount() > 0) { 176 s.append(" TTFF mean (sec): ").append(timeToFirstFixSecStatistics.getMean()).append("\n"); 177 s.append(" TTFF standard deviation (sec): ").append( 178 timeToFirstFixSecStatistics.getStandardDeviation()).append("\n"); 179 } 180 s.append(" Number of position accuracy reports: ").append( 181 positionAccuracyMeterStatistics.getCount()).append("\n"); 182 if (positionAccuracyMeterStatistics.getCount() > 0) { 183 s.append(" Position accuracy mean (m): ").append( 184 positionAccuracyMeterStatistics.getMean()).append("\n"); 185 s.append(" Position accuracy standard deviation (m): ").append( 186 positionAccuracyMeterStatistics.getStandardDeviation()).append("\n"); 187 } 188 s.append(" Number of CN0 reports: ").append( 189 topFourAverageCn0Statistics.getCount()).append("\n"); 190 if (topFourAverageCn0Statistics.getCount() > 0) { 191 s.append(" Top 4 Avg CN0 mean (dB-Hz): ").append( 192 topFourAverageCn0Statistics.getMean()).append("\n"); 193 s.append(" Top 4 Avg CN0 standard deviation (dB-Hz): ").append( 194 topFourAverageCn0Statistics.getStandardDeviation()).append("\n"); 195 } 196 s.append("GNSS_KPI_END").append("\n"); 197 return s.toString(); 198 } 199 200 /** Class for storing statistics */ 201 private class Statistics { 202 203 /** Resets statistics */ 204 public void reset() { 205 count = 0; 206 sum = 0.0; 207 sumSquare = 0.0; 208 } 209 210 /** Adds an item */ 211 public void addItem(double item) { 212 count++; 213 sum += item; 214 sumSquare += item * item; 215 } 216 217 /** Returns number of items added */ 218 public int getCount() { 219 return count; 220 } 221 222 /** Returns mean */ 223 public double getMean() { 224 return sum/count; 225 } 226 227 /** Returns standard deviation */ 228 public double getStandardDeviation() { 229 double m = sum/count; 230 m = m * m; 231 double v = sumSquare/count; 232 if (v > m) { 233 return Math.sqrt(v - m); 234 } 235 return 0; 236 } 237 238 private int count; 239 private double sum; 240 private double sumSquare; 241 } 242 243 /** Location failure statistics */ 244 private Statistics locationFailureStatistics; 245 246 /** Time to first fix statistics */ 247 private Statistics timeToFirstFixSecStatistics; 248 249 /** Position accuracy statistics */ 250 private Statistics positionAccuracyMeterStatistics; 251 252 /** Top 4 average CN0 statistics */ 253 private Statistics topFourAverageCn0Statistics; 254 255 /** 256 * Resets GNSS metrics 257 */ 258 private void reset() { 259 StringBuilder s = new StringBuilder(); 260 TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s); 261 logStartInElapsedRealTime = s.toString(); 262 locationFailureStatistics.reset(); 263 timeToFirstFixSecStatistics.reset(); 264 positionAccuracyMeterStatistics.reset(); 265 topFourAverageCn0Statistics.reset(); 266 return; 267 } 268 }