[go: up one dir, main page]

Menu

[a2dba3]: / calise / thmain.py  Maximize  Restore  History

Download this file

298 lines (249 with data), 12.6 kB

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
# Copyright (C) 2011-2014 Nicolo' Barbon
#
# This file is part of Calise.
#
# Calise is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published
# by the Free Software Foundation, either version 3 of the License,
# or any later version.
#
# Calise is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with Calise. If not, see <http://www.gnu.org/licenses/>.
modules = {}
for module in options.get_modules(): # TODO: should display enabled modules
modules[module['name']] = __import__(module['name'])
def sdev(values):
average = sum(values) / float(len(values))
dev = (sum([(x - average)**2 for x in values]) / float(len(values)))**.5
return dev
def sdev_list_processor(values, threshold=1):
''' Standard deviation list check
Perform a per-element standard deviation check, elements above or below
standard deviation's threshold (which can be at most '1') are removed.
'''
avg = sum(values) / float(len(values))
dev = sdev(values)
corrected_values = list()
for i in range(len(values)):
if not values[i] > avg + max(dev, threshold) and \
not values[i] < avg - max(dev, threshold):
corrected_values.append(values[i])
return corrected_values
class ServiceMainThread():
def init_thmodules(self):
self.threads = {
'input': None,
'correction': None,
'output': None,}
for module in options.get_modules()
self.threads[module['type']][module['name']] = \
modules[module['name']].MainThread(module['settings'])
def service_capture(self, number, sleeptime, threshold=1):
""" Service screen and camera capture function
This function gets values from the specified input module and
(if loaded/available) from 'screen' module.
<number> number of captures to be done
<sleeptime> time to wait between each capture
<threshold> max error (in /255) permitted in capture values
NOTE: To be sure that input values are trustworthy, after
'number' captures have been taken, values are compared
one-to-another upon standard deviation and, at each
passage, wrong ones are discarded.
If non-wrong values are less than 'number', another
capture cycle is started within the same capture session
(maintaining correct values from previous cycles) until
correct values are at least more than half of 'number'.
"""
# Only *one* input module is allowed per execution.
# TODO: More than one input module at the same time may improve
# value precision.
values = []
for name in list(self.threads['input'].keys()):
while not self.threads['input'][name].is_alive():
time.sleep(0.01)
while len(values) < number * .5:
# since $values is empty on the first execution of this cycle,
# the warning message below apply only on 2nd cycle execution
# onward.
if values:
logger.warning("Capture precision is too low, requesting "
"additional captures")
# inner camera cycle (capture)
while len(values) < number:
modules[name].GetEvent.set()
starttime = time.time()
modules[name].SendEvent.wait()
modules[name].SendEvent.clear()
temp_value = self.threads['input'][name].brightness
values.append(temp_value)
# Time between captures is calculated, removing the time
# needed to get the frame from the camera.
waittime = starttime + sleeptime - time.time()
if waittime > 0:
time.sleep(waittime)
logger.debug(
"raw: %s" %
' '.join(["%4.1f" % k for k in values]))
# After capture cycle ends, $number captures have been
# obtained. All values out of threshold range will be removed.
corrected_values = sdev_list_processor(values,threshold)
while values != corrected_values:
values = corrected_values
corrected_values = sdev_list_processor(values,threshold)
logger.debug(
"valid: %s" %
' '.join(["%4.1f" % k for k in values]))
# At this point $values has at least $number/2 entries and its
# data is reliable (max error < threshold).
input_value = float(sum(values)) / len(values)
logger.debug("ambient_brightness: %4.1f" % input_value)
modules[name].AbortEvent.set()
# If "screen" module is loaded, process the send request returned
# from the corresponding thread and get screen brightness value.
if 'screen' in list(self.threads['correction'].keys()):
modules['screen'].SendEvent.wait()
screen_value = self.threads['correction']['screen'].brightness
screen_multiplier = self.threads['correction']['screen'].multiplier
logger.debug("display_brightness: %4.1f" % screen_value)
logger.debug("display_multiplier: %.2f" % screen_multiplier)
modules['screen'].SendEvent.clear()
modules['screen'].AbortEvent.set()
# Cleanup phase.
for name in list(self.threads['input'].keys()):
while self.threads['input'][name].is_alive():
time.sleep(0.01)
waste = self.threads['input'].pop(name)
for name in list(self.threads['correction'].keys()):
while self.threads['correction'][name].is_alive():
time.sleep(0.01)
waste = self.threads['correction'].pop(name)
# > BLOCK #01
def get_brightness_percentage(camera_value, camera_min, camera_top):
''' Brightness percentage
Obtain a percentage value, for camera brightness value passed to
the fucntion, upon min and max values set by the user.
NOTE: any 'camera_value' correction (eg. backlight brightness
correction from function 'get_brightness_correction')
have to be applied BEFORE passing 'camera_value' to this
function.
'''
if camera_min > camera_value:
logger.warning("Camera brightness value (%.1f) is below minimum
level (%1f)" % (camera_value, camera_min))
camera_value = camera_min
percentage = 100 * ((camera_value - camera_min) / camera_top) ** 0.73
def get_brightness_correction(screen_value, screen_multiplier,
camera_value, camera_top, camera_min):
''' Backlight correction (in /255)
(screen module only) Returns the amount of correction (in /255)
to be applied to camera values in order to remove the
brightness coming from the screen in front of the user.
'fixed_camera_top' was the backlight intensity at which the
screen of first test machine matched MAX backlight output:
above that value (even if you couldn't see clearly) backlight
couldn't be increased any further.
NOTE: i -> backlight_intensity
s -> display_brightness
a -> ambient_brightness
m -> max_correction
l -> ambient_brightness_limit
'''
fixed_camera_top = 160.0
i = (backlight_step / backlight_steps)*(camera_top) / fixed_camera_top
s = screen_value / 255
a = amb * (255.0 - offset) / 255
m = 1.5 + 5 * (10*i - 1) * s**(3 - 1.3*i)
l = fixed_camera_top * (255.0 - offset) / 255
correction = m * (screen_multiplier ** 0.5) * ((l - a) / l)
# > BLOCK #02
def validate_backlight_device_path(path):
''' Validate given backlight path
Validate if given backlight able device path (which can be
either a directory path or a file path) is valid.
Valdation is carried out in 2 passages: at first directory
existence is checked and, if directory exists, search for 4 key
files that should exist in there.
'''
devdir_content = [
'brightness', 'max_brightness', 'bl_power', 'actual_brightness']
# directory existence
if os.path.isdir(path):
device_path = path
elif os.path.isdir(os.path.dirname(path)):
device_path = os.path.dirname(path)
else:
device_path = None
# basic directory validation
if device_path:
counter = 0
for filename in devdir_content:
if os.path.isfile(os.path.join(device_path, filename)):
counter += 1
if counter != len(devdir_content):
device_path = None
# if given backlight device path got through both validation
# passages, device_path is not None.
return device_path
def read_backlight(path, option):
''' Read one of backlight path values
Given a backlight path value like brightness, bl_power, ...
this function performs all necessary steps to get a clean
reading of that path.
(eg. read_backlight('/sys/class/backlight/acpi_video0', 'brightness')
NOTE: ValueError and IOError have been excepted to be able to
log them out, so that the user will know (and eventually
easily fix) what's wrong.
'''
content = None
devdir_content = [
'brightness', 'max_brightness', 'bl_power', 'actual_brightness']
device_path = validate_backlight_device_path(path)
if not devdir_content.count(option):
option = None
if device_path and option:
# check for path read permissions
try:
with open(os.path.join(device_path, option), 'r') as fp:
# check for path content (that should be a integer)
try:
content = int(fp.readline())
except ValueError:
logger.error(
"Choosen \'%s\' file (%s) is not valid\n" %
(option, device_path))
raise
except IOError as err:
if err.errno == errno.EACCES:
logger.error(
"Could not read from path \'%s\'. Please set at"
"least read permission for current user\n" %
os.path.join(device_path, option))
raise
return content
'''
TODO: MODULES
input: (float, o [0 > 1]) camera, light sensor, etc
correction: RESERVED
computation: RESERVED
output: (float, i [0 > 100]) baclight, etc
for module in input modules (at least 1) get input data
for module in correction modules get input data and > correct <
> computate <
for module in output modules do something with data
command-line arguments:
--enable/--disable [<module1>[,<module2>,<moduleN>]]
choose modules (among installed) on calibration
modules should have:
MainThread() # execution thread
should respond to:
GetEvent.set() > get brightness value
GetInfo()
Configure() # calibration per module (should be called on
'main' caibration if module is enabled and
must be independant.
'''