Utils { public static double findBestShift(double[] laserT, double[] touchT, double[] touchY) { int steps = 1500; double[] shiftSteps = new double[]{0.1, 0.01}; 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 double median(ArrayList<Double> arrList); static double mean(double[] x); static double[] interp(double[] x, double[] xp, double[] yp); static double stdev(double[] a); static double[] extract(int[] indicator, int value, double[] arr); static String array2string(double[] a, String format); static int argmax(double[] a); static int argmin(double[] a); static double findBestShift(double[] laserT, double[] touchT, double[] touchY);  }
@Test public void testFindBestShift() { Random rand = new Random(42); double latency = 12.34; double[] touchTimes = new double[4000]; for (int i = 0; i < touchTimes.length; i++) { touchTimes[i] = i + rand.nextDouble()*0.2 - 0.1; } double[] touchY = new double[touchTimes.length]; for (int i = 0; i < touchY.length; i++) { touchY[i] = 1000*Math.cos((touchTimes[i] - latency) * Math.PI/500) + rand.nextDouble()*0.02 - 0.01; } double[] laserTimes = new double[4]; int i = 0; for (int root = 0; root < 1000; root+=1000) { laserTimes[i++] = root + 250 - 10; laserTimes[i++] = root + 250 + 10; laserTimes[i++] = root + 750 - 10; laserTimes[i++] = root + 750 + 10; } assertEquals(latency, Utils.findBestShift(laserTimes, touchTimes, touchY), 1e-6); }