diff options
Diffstat (limited to 'android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java')
-rw-r--r-- | android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java | 187 |
1 files changed, 187 insertions, 0 deletions
diff --git a/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java b/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java new file mode 100644 index 0000000..19c7488 --- /dev/null +++ b/android/WALT/app/src/main/java/org/chromium/latency/walt/Utils.java @@ -0,0 +1,187 @@ +/* + * Copyright (C) 2015 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 org.chromium.latency.walt; + +import android.content.Context; +import android.content.SharedPreferences; +import android.preference.PreferenceManager; +import android.support.annotation.StringRes; + +import java.util.ArrayList; +import java.util.Collections; + +/** + * Kitchen sink for small utility functions + */ +public class Utils { + public static double median(ArrayList<Double> arrList) { + ArrayList<Double> lst = new ArrayList<>(arrList); + Collections.sort(lst); + int len = lst.size(); + if (len == 0) { + return Double.NaN; + } + + if (len % 2 == 1) { + return lst.get(len / 2); + } else { + return 0.5 * (lst.get(len / 2) + lst.get(len / 2 - 1)); + } + } + + public static double mean(double[] x) { + double s = 0; + for (double v: x) s += v; + return s / x.length; + } + + /** + * Linear interpolation styled after numpy.interp() + * returns values at points x interpolated using xp, yp data points + * Both x and xp must be monotonically increasing. + */ + public static double[] interp(double[] x, double[] xp, double[] yp) { + // assuming that x and xp are already sorted. + // go over x and xp as if we are merging them + double[] y = new double[x.length]; + int i = 0; + int ip = 0; + + // skip x points that are outside the data + while (i < x.length && x[i] < xp[0]) i++; + + while (ip < xp.length && i < x.length) { + // skip until we see an xp >= current x + while (ip < xp.length && xp[ip] < x[i]) ip++; + if (ip >= xp.length) break; + if (xp[ip] == x[i]) { + y[i] = yp[ip]; + } else { + double dy = yp[ip] - yp[ip-1]; + double dx = xp[ip] - xp[ip-1]; + y[i] = yp[ip-1] + dy/dx * (x[i] - xp[ip-1]); + } + i++; + } + return y; + } + + public static double stdev(double[] a) { + double m = mean(a); + double sumsq = 0; + for (double v : a) sumsq += (v-m)*(v-m); + return Math.sqrt(sumsq / a.length); + } + + /** + * Similar to numpy.extract() + * returns a shorter array with values taken from x at indices where indicator == value + */ + public static double[] extract(int[] indicator, int value, double[] arr) { + if (arr.length != indicator.length) { + throw new IllegalArgumentException("Length of arr and indicator must be the same."); + } + int newLen = 0; + for (int v: indicator) if (v == value) newLen++; + double[] newx = new double[newLen]; + + int j = 0; + for (int i=0; i<arr.length; i++) { + if (indicator[i] == value) { + newx[j] = arr[i]; + j++; + } + } + return newx; + } + + public static String array2string(double[] a, String format) { + StringBuilder sb = new StringBuilder(); + sb.append("array(["); + for (double x: a) { + sb.append(String.format(format, x)); + sb.append(", "); + } + sb.append("])"); + return sb.toString(); + } + + + public static int argmin(double[] a) { + int imin = 0; + for (int i=1; i<a.length; i++) if (a[i] < a[imin]) imin = i; + return imin; + } + + private static double getShiftError(double[] laserT, double[] touchT, double[] touchY, double shift) { + double[] T = new double[laserT.length]; + for (int j=0; j<T.length; j++) { + T[j] = laserT[j] + shift; + } + double [] laserY = Utils.interp(T, touchT, touchY); + // TODO: Think about throwing away a percentile of most distanced points for noise reduction + return Utils.stdev(laserY); + } + + /** + * Simplified Java re-implementation or py/qslog/minimization.py. + * This is very specific to the drag latency algorithm. + * + * tl;dr: Shift laser events by some time delta and see how well they fit on a horizontal line. + * Delta that results in the best looking straight line is the latency. + */ + public static double findBestShift(double[] laserT, double[] touchT, double[] touchY) { + int steps = 1500; + double[] shiftSteps = new double[]{0.1, 0.01}; // milliseconds + double[] stddevs = new double[steps]; + double bestShift = shiftSteps[0]*steps/2; + for (final double shiftStep : shiftSteps) { + for (int i = 0; i < steps; i++) { + stddevs[i] = getShiftError(laserT, touchT, touchY, bestShift + shiftStep * i - shiftStep * steps / 2); + } + bestShift = argmin(stddevs) * shiftStep + bestShift - shiftStep * steps / 2; + } + return bestShift; + } + + static byte[] char2byte(char c) { + return new byte[]{(byte) c}; + } + + static int getIntPreference(Context context, @StringRes int keyId, int defaultValue) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getInt(context.getString(keyId), defaultValue); + } + + static boolean getBooleanPreference(Context context, @StringRes int keyId, boolean defaultValue) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getBoolean(context.getString(keyId), defaultValue); + } + + static String getStringPreference(Context context, @StringRes int keyId, String defaultValue) { + SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(context); + return preferences.getString(context.getString(keyId), defaultValue); + } + + public enum ListenerState { + RUNNING, + STARTING, + STOPPED, + STOPPING + } + +} |