blob: 80a802856e60321da5e15cc7f2adab6fd9d566e6 [file] [log] [blame]
# Copyright (c) 2014 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
"""Provide interfaces to initialize and read Whale color sensor."""
import ast
import logging
import factory_common # pylint: disable=W0611
import cros.factory.test.fixture.bft_fixture as bft
# shortcut
BFT = bft.BFTFixture
class ColorSensor(object):
"""Whale color sensor."""
# mapping from color names in bft.conf to BFTFixture.LEDColor
_COLOR_NAMES = {
'red': BFT.LEDColor.RED,
'green': BFT.LEDColor.GREEN,
'yellow': BFT.LEDColor.YELLOW,
'blue': BFT.LEDColor.BLUE
}
# config names
_CONFIG_TIMING = 'color_sensor_timing'
_CONFIG_GAIN = 'color_sensor_gain'
_CONFIG_COLOR = 'color_sensor_color'
_REQUIRED_PARAMS = (_CONFIG_TIMING, _CONFIG_GAIN, _CONFIG_COLOR)
def __init__(self, servo, sensor_index, params):
"""Constructor.
Args:
servo: ServoClient object.
sensor_index: Index of color sensor (starting from 1).
params: Parameters loaded from bft.conf.
"""
# Verify parameters first because it's easy to make mistakes.
for config in self._REQUIRED_PARAMS:
if config not in params:
raise ValueError('Missing parameter %s' % config)
if sensor_index not in params[config]:
raise ValueError('Parameter %s does not contain sensor index %d' %
(config, sensor_index))
for color_name in params[self._CONFIG_COLOR][sensor_index]:
if color_name not in self._COLOR_NAMES:
raise ValueError('Unsupport color name %s in parameters' % color_name)
# Initialize the color sensor hardware.
if sensor_index == 1:
servo.whale_color1_timing = (
params[self._CONFIG_TIMING][sensor_index])
servo.whale_color1_gain = params[self._CONFIG_GAIN][sensor_index]
else:
raise ValueError('Sensor index %s is unsupported' % sensor_index)
self._servo = servo
self._sensor_index = sensor_index
self._color_params = params[self._CONFIG_COLOR][sensor_index]
@classmethod
def HasRequiredParams(cls, params):
"""Checks if params has required parameters.
Args:
params: a dict contains parameters
Returns:
True if params contains all required parameters.
"""
return all(p in params for p in cls._REQUIRED_PARAMS)
@staticmethod
def _CompareHue(hue1, hue2, tolerance):
"""Compares hue values.
Because hue value is cyclic from 0.0 to 1.0. It treats values close to 0.0
in similar way to values close to 1.0.
Args:
hue1: First hue value.
hue2: Second hue value.
tolerance: Allowed difference between hue[12].
Returns:
Whether hue1 and hue2 are close enough.
"""
return (abs(hue1 - hue2) <= tolerance or
abs(hue1 - hue2 - 1.0) <= tolerance or
abs(hue1 - hue2 + 1.0) <= tolerance)
def ReadColor(self):
"""Reads color value from sensor.
Returns:
BFTFixture.LEDColor; BFTFixture.LEDColor.OFF if no color is detected.
"""
if self._sensor_index == 1:
# Servo returns a string containing list expression.
read_h, read_s, read_v = ast.literal_eval(self._servo.whale_color1_HSV)
else:
raise ValueError('Sensor index %s is unsupported' % self._sensor_index)
logging.info('Read Hue=%.3f, Saturation=%.3f, Lightness=%.3f',
read_h, read_s, read_v)
for color_name in self._color_params:
thresholds = self._color_params[color_name]
color_hue = thresholds['hue']
hue_tolerance = thresholds['hue_tolerance']
min_saturation = thresholds['min_saturation']
min_lightness = thresholds['min_lightness']
if (self._CompareHue(read_h, color_hue, hue_tolerance) and
read_s >= min_saturation and read_v >= min_lightness):
logging.info('Match color %s with HSV criteria '
'(%.3f +/- %.3f, >= %.3f, >= %.3f)', color_name,
color_hue, hue_tolerance, min_saturation, min_lightness)
return self._COLOR_NAMES[color_name]
logging.info('Do not match any color')
return BFT.LEDColor.OFF