2 * Functions for auto gain.
4 * Copyright (C) 2010-2011 Hans de Goede <hdegoede@redhat.com>
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21 #ifdef WANT_REGULAR_AUTOGAIN
22 /* auto gain and exposure algorithm based on the knee algorithm described here:
23 http://ytse.tricolour.net/docs/LowLightOptimization.html
25 Returns 0 if no changes were made, 1 if the gain and or exposure settings
27 static inline int auto_gain_n_exposure(
28 struct gspca_dev *gspca_dev,
35 struct sd *sd = (struct sd *) gspca_dev;
36 int i, steps, gain, orig_gain, exposure, orig_exposure;
39 orig_gain = gain = sd->ctrls[GAIN].val;
40 orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
42 /* If we are of a multiple of deadzone, do multiple steps to reach the
43 desired lumination fast (with the risc of a slight overshoot) */
44 steps = abs(desired_avg_lum - avg_lum) / deadzone;
46 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
47 avg_lum, desired_avg_lum, steps);
49 for (i = 0; i < steps; i++) {
50 if (avg_lum > desired_avg_lum) {
53 else if (exposure > exposure_knee)
55 else if (gain > sd->ctrls[GAIN].def)
57 else if (exposure > sd->ctrls[EXPOSURE].min)
59 else if (gain > sd->ctrls[GAIN].min)
64 if (gain < sd->ctrls[GAIN].def)
66 else if (exposure < exposure_knee)
68 else if (gain < gain_knee)
70 else if (exposure < sd->ctrls[EXPOSURE].max)
72 else if (gain < sd->ctrls[GAIN].max)
79 if (gain != orig_gain) {
80 sd->ctrls[GAIN].val = gain;
84 if (exposure != orig_exposure) {
85 sd->ctrls[EXPOSURE].val = exposure;
86 setexposure(gspca_dev);
91 PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",
97 #ifdef WANT_COARSE_EXPO_AUTOGAIN
98 /* Autogain + exposure algorithm for cameras with a coarse exposure control
99 (usually this means we can only control the clockdiv to change exposure)
100 As changing the clockdiv so that the fps drops from 30 to 15 fps for
101 example, will lead to a huge exposure change (it effectively doubles),
102 this algorithm normally tries to only adjust the gain (between 40 and
103 80 %) and if that does not help, only then changes exposure. This leads
104 to a much more stable image then using the knee algorithm which at
105 certain points of the knee graph will only try to adjust exposure,
106 which leads to oscilating as one exposure step is huge.
108 Note this assumes that the sd struct for the cam in question has
109 exp_too_low_cnt and exp_too_high_cnt int members for use by this function.
111 Returns 0 if no changes were made, 1 if the gain and or exposure settings
113 static inline int coarse_grained_expo_autogain(
114 struct gspca_dev *gspca_dev,
119 struct sd *sd = (struct sd *) gspca_dev;
120 int steps, gain, orig_gain, exposure, orig_exposure;
121 int gain_low, gain_high;
124 orig_gain = gain = sd->ctrls[GAIN].val;
125 orig_exposure = exposure = sd->ctrls[EXPOSURE].val;
127 gain_low = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 2;
128 gain_low += sd->ctrls[GAIN].min;
129 gain_high = (sd->ctrls[GAIN].max - sd->ctrls[GAIN].min) / 5 * 4;
130 gain_high += sd->ctrls[GAIN].min;
132 /* If we are of a multiple of deadzone, do multiple steps to reach the
133 desired lumination fast (with the risc of a slight overshoot) */
134 steps = (desired_avg_lum - avg_lum) / deadzone;
136 PDEBUG(D_FRAM, "autogain: lum: %d, desired: %d, steps: %d",
137 avg_lum, desired_avg_lum, steps);
139 if ((gain + steps) > gain_high &&
140 exposure < sd->ctrls[EXPOSURE].max) {
142 sd->exp_too_low_cnt++;
143 sd->exp_too_high_cnt = 0;
144 } else if ((gain + steps) < gain_low &&
145 exposure > sd->ctrls[EXPOSURE].min) {
147 sd->exp_too_high_cnt++;
148 sd->exp_too_low_cnt = 0;
151 if (gain > sd->ctrls[GAIN].max)
152 gain = sd->ctrls[GAIN].max;
153 else if (gain < sd->ctrls[GAIN].min)
154 gain = sd->ctrls[GAIN].min;
155 sd->exp_too_high_cnt = 0;
156 sd->exp_too_low_cnt = 0;
159 if (sd->exp_too_high_cnt > 3) {
161 sd->exp_too_high_cnt = 0;
162 } else if (sd->exp_too_low_cnt > 3) {
164 sd->exp_too_low_cnt = 0;
167 if (gain != orig_gain) {
168 sd->ctrls[GAIN].val = gain;
172 if (exposure != orig_exposure) {
173 sd->ctrls[EXPOSURE].val = exposure;
174 setexposure(gspca_dev);
179 PDEBUG(D_FRAM, "autogain: changed gain: %d, expo: %d",