1bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray/*
2bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * Copyright (C) 2016 The Android Open Source Project
3bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray *
4bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * Licensed under the Apache License, Version 2.0 (the "License");
5bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * you may not use this file except in compliance with the License.
6bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * You may obtain a copy of the License at
7bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray *
8bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray *      http://www.apache.org/licenses/LICENSE-2.0
9bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray *
10bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * Unless required by applicable law or agreed to in writing, software
11bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * distributed under the License is distributed on an "AS IS" BASIS,
12bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * See the License for the specific language governing permissions and
14bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * limitations under the License.
15bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray */
16bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
17bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Raypackage com.android.internal.location.gnssmetrics;
18bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
19bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Rayimport android.os.SystemClock;
20d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Rayimport android.os.connectivity.GpsBatteryStats;
21bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
22d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Rayimport android.text.format.DateUtils;
23bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Rayimport android.util.Base64;
2478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Rayimport android.util.Log;
25bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Rayimport android.util.TimeUtils;
26bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
27168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Rayimport java.util.Arrays;
28168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray
2978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Rayimport com.android.internal.app.IBatteryStats;
30bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Rayimport com.android.internal.location.nano.GnssLogsProto.GnssLog;
31d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Rayimport com.android.internal.location.nano.GnssLogsProto.PowerMetrics;
32bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
33bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray/**
34bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * GnssMetrics: Is used for logging GNSS metrics
35bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray * @hide
36bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray */
37bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Raypublic class GnssMetrics {
38bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
3978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  private static final String TAG = GnssMetrics.class.getSimpleName();
4078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
4178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  /* Constant which indicates GPS signal quality is poor */
4278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  public static final int GPS_SIGNAL_QUALITY_POOR = 0;
4378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
4478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  /* Constant which indicates GPS signal quality is good */
4578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  public static final int GPS_SIGNAL_QUALITY_GOOD = 1;
4678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
4778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  /* Number of GPS signal quality levels */
4878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  public static final int NUM_GPS_SIGNAL_QUALITY_LEVELS = GPS_SIGNAL_QUALITY_GOOD + 1;
4978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
50bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /** Default time between location fixes (in millisecs) */
51bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private static final int DEFAULT_TIME_BETWEEN_FIXES_MILLISECS = 1000;
52bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
53bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /* The time since boot when logging started */
54bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private String logStartInElapsedRealTime;
55bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
5678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  /* GNSS power metrics */
5778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  private GnssPowerMetrics mGnssPowerMetrics;
5878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
59bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /** Constructor */
6078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  public GnssMetrics(IBatteryStats stats) {
6178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    mGnssPowerMetrics = new GnssPowerMetrics(stats);
62bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    locationFailureStatistics = new Statistics();
63bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    timeToFirstFixSecStatistics = new Statistics();
64bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    positionAccuracyMeterStatistics = new Statistics();
65168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    topFourAverageCn0Statistics = new Statistics();
66bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    reset();
67bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
68bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
69bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
70bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Logs the status of a location report received from the HAL
71bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   *
72bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @param isSuccessful
73bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
74bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public void logReceivedLocationStatus(boolean isSuccessful) {
75bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (!isSuccessful) {
76bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      locationFailureStatistics.addItem(1.0);
77bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      return;
78bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
79bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    locationFailureStatistics.addItem(0.0);
80bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return;
81bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
82bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
83bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
84bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Logs missed reports
85bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   *
86bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @param desiredTimeBetweenFixesMilliSeconds
87bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @param actualTimeBetweenFixesMilliSeconds
88bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
89bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public void logMissedReports(int desiredTimeBetweenFixesMilliSeconds,
90bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      int actualTimeBetweenFixesMilliSeconds) {
91bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    int numReportMissed = (actualTimeBetweenFixesMilliSeconds /
92bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        Math.max(DEFAULT_TIME_BETWEEN_FIXES_MILLISECS, desiredTimeBetweenFixesMilliSeconds)) - 1;
93bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (numReportMissed > 0) {
94bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      for (int i = 0; i < numReportMissed; i++) {
95bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        locationFailureStatistics.addItem(1.0);
96bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      }
97bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
98bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return;
99bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
100bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
101bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
102bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Logs time to first fix
103bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   *
104bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @param timeToFirstFixMilliSeconds
105bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
106bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public void logTimeToFirstFixMilliSecs(int timeToFirstFixMilliSeconds) {
107bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    timeToFirstFixSecStatistics.addItem((double) (timeToFirstFixMilliSeconds/1000));
108bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return;
109bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
110bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
111bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
112bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Logs position accuracy
113bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   *
114bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @param positionAccuracyMeters
115bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
116bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public void logPositionAccuracyMeters(float positionAccuracyMeters) {
117bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    positionAccuracyMeterStatistics.addItem((double) positionAccuracyMeters);
118bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return;
119bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
120bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
121168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  /*
122168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  * Logs CN0 when at least 4 SVs are available
123168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  *
124168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  */
125168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  public void logCn0(float[] cn0s, int numSv) {
12678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    if (numSv == 0 || cn0s == null || cn0s.length == 0 || cn0s.length < numSv) {
12778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      if (numSv == 0) {
12878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray         mGnssPowerMetrics.reportSignalQuality(null, 0);
12978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      }
130168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      return;
131168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    }
132168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    float[] cn0Array = Arrays.copyOf(cn0s, numSv);
133168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    Arrays.sort(cn0Array);
13478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    mGnssPowerMetrics.reportSignalQuality(cn0Array, numSv);
13578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    if (numSv < 4) {
13678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      return;
13778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    }
138168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    if (cn0Array[numSv - 4] > 0.0) {
139168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      double top4AvgCn0 = 0.0;
140168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      for (int i = numSv - 4; i < numSv; i++) {
141168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray        top4AvgCn0 += (double) cn0Array[i];
142168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      }
143168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      top4AvgCn0 /= 4;
144168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      topFourAverageCn0Statistics.addItem(top4AvgCn0);
145168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    }
146168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    return;
147168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  }
148168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray
149bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
150bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Dumps GNSS metrics as a proto string
151bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @return
152bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
153bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public String dumpGnssMetricsAsProtoString() {
154bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    GnssLog msg = new GnssLog();
155bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (locationFailureStatistics.getCount() > 0) {
156bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.numLocationReportProcessed = locationFailureStatistics.getCount();
157bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.percentageLocationFailure = (int) (100.0 * locationFailureStatistics.getMean());
158bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
159bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (timeToFirstFixSecStatistics.getCount() > 0) {
160bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.numTimeToFirstFixProcessed = timeToFirstFixSecStatistics.getCount();
161bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.meanTimeToFirstFixSecs = (int) timeToFirstFixSecStatistics.getMean();
162bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.standardDeviationTimeToFirstFixSecs
163bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          = (int) timeToFirstFixSecStatistics.getStandardDeviation();
164bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
165bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (positionAccuracyMeterStatistics.getCount() > 0) {
166bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.numPositionAccuracyProcessed = positionAccuracyMeterStatistics.getCount();
167bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.meanPositionAccuracyMeters = (int) positionAccuracyMeterStatistics.getMean();
168bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      msg.standardDeviationPositionAccuracyMeters
169bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          = (int) positionAccuracyMeterStatistics.getStandardDeviation();
170bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
171168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    if (topFourAverageCn0Statistics.getCount() > 0) {
172168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      msg.numTopFourAverageCn0Processed = topFourAverageCn0Statistics.getCount();
173168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      msg.meanTopFourAverageCn0DbHz = topFourAverageCn0Statistics.getMean();
174168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      msg.standardDeviationTopFourAverageCn0DbHz
175168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray          = topFourAverageCn0Statistics.getStandardDeviation();
176168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    }
177d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    msg.powerMetrics = mGnssPowerMetrics.buildProto();
178bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    String s = Base64.encodeToString(GnssLog.toByteArray(msg), Base64.DEFAULT);
179bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    reset();
180bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return s;
181bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
182bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
183bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
184bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Dumps GNSS Metrics as text
185bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   *
186bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * @return GNSS Metrics
187bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
188bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  public String dumpGnssMetricsAsText() {
189bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    StringBuilder s = new StringBuilder();
190bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("GNSS_KPI_START").append('\n');
191bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("  KPI logging start time: ").append(logStartInElapsedRealTime).append("\n");
192bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("  KPI logging end time: ");
193bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s);
194bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("\n");
195bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("  Number of location reports: ").append(
196bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        locationFailureStatistics.getCount()).append("\n");
197bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (locationFailureStatistics.getCount() > 0) {
198bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      s.append("  Percentage location failure: ").append(
199bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          100.0 * locationFailureStatistics.getMean()).append("\n");
200bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
201bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("  Number of TTFF reports: ").append(
202bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        timeToFirstFixSecStatistics.getCount()).append("\n");
203bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (timeToFirstFixSecStatistics.getCount() > 0) {
204bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      s.append("  TTFF mean (sec): ").append(timeToFirstFixSecStatistics.getMean()).append("\n");
205bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      s.append("  TTFF standard deviation (sec): ").append(
206bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          timeToFirstFixSecStatistics.getStandardDeviation()).append("\n");
207bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
208bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("  Number of position accuracy reports: ").append(
209bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        positionAccuracyMeterStatistics.getCount()).append("\n");
210bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    if (positionAccuracyMeterStatistics.getCount() > 0) {
211bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      s.append("  Position accuracy mean (m): ").append(
212bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          positionAccuracyMeterStatistics.getMean()).append("\n");
213bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      s.append("  Position accuracy standard deviation (m): ").append(
214bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray          positionAccuracyMeterStatistics.getStandardDeviation()).append("\n");
215bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
216168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    s.append("  Number of CN0 reports: ").append(
217168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray        topFourAverageCn0Statistics.getCount()).append("\n");
218168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    if (topFourAverageCn0Statistics.getCount() > 0) {
219168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      s.append("  Top 4 Avg CN0 mean (dB-Hz): ").append(
220168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray          topFourAverageCn0Statistics.getMean()).append("\n");
221168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray      s.append("  Top 4 Avg CN0 standard deviation (dB-Hz): ").append(
222168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray          topFourAverageCn0Statistics.getStandardDeviation()).append("\n");
223168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    }
224bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    s.append("GNSS_KPI_END").append("\n");
225d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
226d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    if (stats != null) {
227b30c0b7477d9faf3697f6b33b9cba4f900d2bc06Siddharth Ray      s.append("Power Metrics").append("\n");
228b30c0b7477d9faf3697f6b33b9cba4f900d2bc06Siddharth Ray      s.append("  Time on battery (min): "
229b30c0b7477d9faf3697f6b33b9cba4f900d2bc06Siddharth Ray          + stats.getLoggingDurationMs() / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
230d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      long[] t = stats.getTimeInGpsSignalQualityLevel();
231d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      if (t != null && t.length == NUM_GPS_SIGNAL_QUALITY_LEVELS) {
232d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        s.append("  Amount of time (while on battery) Top 4 Avg CN0 > " +
233d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray            Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) +
234d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray            " dB-Hz (min): ").append(t[1] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
235d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        s.append("  Amount of time (while on battery) Top 4 Avg CN0 <= " +
236d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray            Double.toString(GnssPowerMetrics.POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) +
237d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray            " dB-Hz (min): ").append(t[0] / ((double) DateUtils.MINUTE_IN_MILLIS)).append("\n");
238d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      }
239d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      s.append("  Energy consumed while on battery (mAh): ").append(
240d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray          stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS)).append("\n");
241d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    }
242bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return s.toString();
243bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
244bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
245bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   /** Class for storing statistics */
246bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private class Statistics {
247bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
248bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    /** Resets statistics */
249bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    public void reset() {
250bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      count = 0;
251bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      sum = 0.0;
252bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      sumSquare = 0.0;
253bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
254bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
255bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    /** Adds an item */
256bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    public void addItem(double item) {
257bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      count++;
258bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      sum += item;
259bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      sumSquare += item * item;
260bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
261bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
262bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    /** Returns number of items added */
263bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    public int getCount() {
264bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      return count;
265bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
266bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
267bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    /** Returns mean */
268bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    public double getMean() {
269bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      return sum/count;
270bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
271bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
272bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    /** Returns standard deviation */
273bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    public double getStandardDeviation() {
274bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      double m = sum/count;
275bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      m = m * m;
276bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      double v = sumSquare/count;
277bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      if (v > m) {
278bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray        return Math.sqrt(v - m);
279bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      }
280bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray      return 0;
281bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    }
282bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
283bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    private int count;
284bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    private double sum;
285bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    private double sumSquare;
286bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
287bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
288bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /** Location failure statistics */
289bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private Statistics locationFailureStatistics;
290bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
291bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /** Time to first fix statistics */
292bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private Statistics timeToFirstFixSecStatistics;
293bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
294bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /** Position accuracy statistics */
295bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private Statistics positionAccuracyMeterStatistics;
296bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray
297168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  /** Top 4 average CN0 statistics */
298168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray  private Statistics topFourAverageCn0Statistics;
299168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray
300bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  /**
301bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   * Resets GNSS metrics
302bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray   */
303bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  private void reset() {
304bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    StringBuilder s = new StringBuilder();
305bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    TimeUtils.formatDuration(SystemClock.elapsedRealtimeNanos() / 1000000L, s);
306bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    logStartInElapsedRealTime = s.toString();
307bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    locationFailureStatistics.reset();
308bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    timeToFirstFixSecStatistics.reset();
309bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    positionAccuracyMeterStatistics.reset();
310168f12a056f7ef3adb9f9d52aaa51977b4082213Siddharth Ray    topFourAverageCn0Statistics.reset();
311bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray    return;
312bb608c8958a96b450094417482ba05126ce0c8f0Siddharth Ray  }
31378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
31478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  /* Class for handling GNSS power related metrics */
31578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  private class GnssPowerMetrics {
31678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
31778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /* Threshold for Top Four Average CN0 below which GNSS signal quality is declared poor */
318d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    public static final double POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ = 20.0;
31978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
32078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /* Minimum change in Top Four Average CN0 needed to trigger a report */
32178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    private static final double REPORTING_THRESHOLD_DB_HZ = 1.0;
32278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
32378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /* BatteryStats API */
32478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    private final IBatteryStats mBatteryStats;
32578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
32678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /* Last reported Top Four Average CN0 */
32778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    private double mLastAverageCn0;
32878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
32978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    public GnssPowerMetrics(IBatteryStats stats) {
33078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      mBatteryStats = stats;
33178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      // Used to initialize the variable to a very small value (unachievable in practice) so that
33278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      // the first CNO report will trigger an update to BatteryStats
33378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      mLastAverageCn0 = -100.0;
33478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    }
33578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
33678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /**
337d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     * Builds power metrics proto buf. This is included in the gnss proto buf.
338d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     * @return PowerMetrics
339d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     */
340d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    public PowerMetrics buildProto() {
341d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      PowerMetrics p = new PowerMetrics();
342d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      GpsBatteryStats stats = mGnssPowerMetrics.getGpsBatteryStats();
343d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      if (stats != null) {
344d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        p.loggingDurationMs = stats.getLoggingDurationMs();
345d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        p.energyConsumedMah = stats.getEnergyConsumedMaMs() / ((double) DateUtils.HOUR_IN_MILLIS);
346d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        long[] t = stats.getTimeInGpsSignalQualityLevel();
347d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        p.timeInSignalQualityLevelMs = new long[t.length];
348d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        for (int i = 0; i < t.length; i++) {
349d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray          p.timeInSignalQualityLevelMs[i] = t[i];
350d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        }
351d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      }
352d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      return p;
353d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    }
354d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray
355d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    /**
356d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     * Returns the GPS power stats
357d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     * @return GpsBatteryStats
358d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray     */
359d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    public GpsBatteryStats getGpsBatteryStats() {
360d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      try {
361d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        return mBatteryStats.getGpsBatteryStats();
362d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      } catch (Exception e) {
363d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        Log.w(TAG, "Exception", e);
364d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray        return null;
365d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray      }
366d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    }
367d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray
368d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray    /**
36978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     * Reports signal quality to BatteryStats. Signal quality is based on Top four average CN0. If
37078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     * the number of SVs seen is less than 4, then signal quality is the average CN0.
37178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     * Changes are reported only if the average CN0 changes by more than REPORTING_THRESHOLD_DB_HZ.
37278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     */
37378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    public void reportSignalQuality(float[] ascendingCN0Array, int numSv) {
37478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      double avgCn0 = 0.0;
37578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      if (numSv > 0) {
37678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        for (int i = Math.max(0, numSv - 4); i < numSv; i++) {
37778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray          avgCn0 += (double) ascendingCN0Array[i];
37878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        }
37978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        avgCn0 /= Math.min(numSv, 4);
38078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      }
38178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      if (Math.abs(avgCn0 - mLastAverageCn0) < REPORTING_THRESHOLD_DB_HZ) {
38278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        return;
38378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      }
38478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      try {
38578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        mBatteryStats.noteGpsSignalQuality(getSignalLevel(avgCn0));
38678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        mLastAverageCn0 = avgCn0;
38778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      } catch (Exception e) {
38878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        Log.w(TAG, "Exception", e);
38978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      }
39078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      return;
39178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    }
39278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray
39378ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    /**
39478ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     * Obtains signal level based on CN0
39578ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray     */
39678ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    private int getSignalLevel(double cn0) {
39778ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      if (cn0 > POOR_TOP_FOUR_AVG_CN0_THRESHOLD_DB_HZ) {
39878ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray        return GnssMetrics.GPS_SIGNAL_QUALITY_GOOD;
39978ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      }
40078ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray      return GnssMetrics.GPS_SIGNAL_QUALITY_POOR;
40178ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray    }
40278ccaf5d287cbb6bd214913d72653aa74ecfef76Siddharth Ray  }
403d679a767b49ea4bd4aee83a4bc2425fdce67b950Siddharth Ray}
404