1e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#!/usr/bin/python
2e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
3e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger'''
4e27eefc4844477cee5d32f51ab45ff62020cdb36Derek SollenbergerCopyright 2013 Google Inc.
5e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
6e27eefc4844477cee5d32f51ab45ff62020cdb36Derek SollenbergerUse of this source code is governed by a BSD-style license that can be
7e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerfound in the LICENSE file.
8e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger'''
9e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
10e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerimport math
11e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerimport pprint
12e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
13e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerdef withinStdDev(n):
14e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  """Returns the percent of samples within n std deviations of the normal."""
15e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  return math.erf(n / math.sqrt(2))
16e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
17e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerdef withinStdDevRange(a, b):
18e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  """Returns the percent of samples within the std deviation range a, b"""
19e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if b < a:
20e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    return 0;
21e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
22e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if a < 0:
23e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if b < 0:
24e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      return (withinStdDev(-a) - withinStdDev(-b)) / 2;
25e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    else:
26e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      return (withinStdDev(-a) + withinStdDev(b)) / 2;
27e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  else:
28e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    return (withinStdDev(b) - withinStdDev(a)) / 2;
29e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
30e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
31e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#We have a bunch of smudged samples which represent the average coverage of a range.
32e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#We have a 'center' which may not line up with those samples.
33e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#From the 'center' we want to make a normal approximation where '5' sample width out we're at '3' std deviations.
34e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#The first and last samples may not be fully covered.
35e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
36e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#This is the sub-sample shift for each set of FIR coefficients (the centers of the lcds in the samples)
37e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#Each subpxl takes up 1/3 of a pixel, so they are centered at x=(i/n+1/2n), or 1/6, 3/6, 5/6 of a pixel.
38e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#Each sample takes up 1/4 of a pixel, so the results fall at (x*4)%1, or 2/3, 0, 1/3 of a sample.
39e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergersamples_per_pixel = 4
40e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergersubpxls_per_pixel = 3
41e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#sample_offsets is (frac, int) in sample units.
42e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergersample_offsets = [math.modf((float(subpxl_index)/subpxls_per_pixel + 1.0/(2.0*subpxls_per_pixel))*samples_per_pixel) for subpxl_index in range(subpxls_per_pixel)]
43e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
44e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#How many samples to consider to the left and right of the subpxl center.
45e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergersample_units_width = 5
46e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
47e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#The std deviation at sample_units_width.
48e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerstd_dev_max = 3
49e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
50e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#The target sum is in some fixed point representation.
51e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger#Values larger the 1 in fixed point simulate ink spread.
52e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergertarget_sum = 0x110
53e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
54e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenbergerfor sample_offset, sample_align in sample_offsets:
55e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  coeffs = []
56e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  coeffs_rounded = []
57e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
58e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  #We start at sample_offset - sample_units_width
59e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  current_sample_left = sample_offset - sample_units_width
60e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  current_std_dev_left = -std_dev_max
61e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
62e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  done = False
63e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  while not done:
64e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    current_sample_right = math.floor(current_sample_left + 1)
65e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    if current_sample_right > sample_offset + sample_units_width:
66e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      done = True
67e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      current_sample_right = sample_offset + sample_units_width
68e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    current_std_dev_right = current_std_dev_left + ((current_sample_right - current_sample_left) / sample_units_width) * std_dev_max
69e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
70e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coverage = withinStdDevRange(current_std_dev_left, current_std_dev_right)
71e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coeffs.append(coverage * target_sum)
72e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coeffs_rounded.append(int(round(coverage * target_sum)))
73e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
74e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    current_sample_left = current_sample_right
75e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    current_std_dev_left = current_std_dev_right
76e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
77e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  # Now we have the numbers we want, but our rounding needs to add up to target_sum.
78e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  delta = 0
79e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  coeffs_rounded_sum = sum(coeffs_rounded)
80e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if coeffs_rounded_sum > target_sum:
81e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # The coeffs add up to too much. Subtract 1 from the ones which were rounded up the most.
82e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    delta = -1
83e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
84e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if coeffs_rounded_sum < target_sum:
85e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # The coeffs add up to too little. Add 1 to the ones which were rounded down the most.
86e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    delta = 1
87e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
88e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  if delta:
89e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    print "Initial sum is 0x%0.2X, adjusting." % (coeffs_rounded_sum,)
90e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coeff_diff = [(coeff_rounded - coeff) * delta
91e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger                  for coeff, coeff_rounded in zip(coeffs, coeffs_rounded)]
92e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
93e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    class IndexTracker:
94e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      def __init__(self, index, item):
95e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        self.index = index
96e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        self.item = item
97e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      def __lt__(self, other):
98e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        return self.item < other.item
99e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      def __repr__(self):
100e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger        return "arr[%d] == %s" % (self.index, repr(self.item))
101e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
102e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coeff_pkg = [IndexTracker(i, diff) for i, diff in enumerate(coeff_diff)]
103e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    coeff_pkg.sort()
104e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
105e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # num_elements_to_force_round had better be < (2 * sample_units_width + 1) or
106e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # * our math was wildy wrong
107e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # * an awful lot of the curve is out side our sample
108e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    # either is pretty bad, and probably means the results will not be useful.
109e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    num_elements_to_force_round = abs(coeffs_rounded_sum - target_sum)
110e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger    for i in xrange(num_elements_to_force_round):
111e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      print "Adding %d to index %d to force round %f." % (delta, coeff_pkg[i].index, coeffs[coeff_pkg[i].index])
112e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger      coeffs_rounded[coeff_pkg[i].index] += delta
113e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
114e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  print "Prepending %d 0x00 for allignment." % (sample_align,)
115e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  coeffs_rounded_aligned = ([0] * int(sample_align)) + coeffs_rounded
116e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger
117e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  print ', '.join(["0x%0.2X" % coeff_rounded for coeff_rounded in coeffs_rounded_aligned])
118e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  print sum(coeffs), hex(sum(coeffs_rounded))
119e27eefc4844477cee5d32f51ab45ff62020cdb36Derek Sollenberger  print
120