1# Copyright 2017 The Android Open Source Project
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7#      http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14"""Verifies AE and AF can run independently."""
15
16
17import logging
18import os.path
19from mobly import test_runner
20import numpy as np
21
22import its_base_test
23import camera_properties_utils
24import error_util
25import its_session_utils
26
27AWB_GAINS_LENGTH = 4
28AWB_XFORM_LENGTH = 9
29G_CHANNEL = 2
30G_GAIN = 1.0
31G_GAIN_TOL = 0.05
32NAME = os.path.splitext(os.path.basename(__file__))[0]
33THREE_A_STATES = {'AE': [True, False, True],
34                  'AF': [False, True, True],
35                  'FULL_3A': [True, True, True]}  # note no AWB solo
36
37
38class SingleATest(its_base_test.ItsBaseTest):
39  """Test basic camera 3A behavior with AE and AF run individually.
40
41  To pass, 3A must converge. Check that returned 3A values are valid.
42  """
43
44  def test_ae_af(self):
45    logging.debug('Starting %s', NAME)
46    with its_session_utils.ItsSession(
47        device_id=self.dut.serial,
48        camera_id=self.camera_id,
49        hidden_physical_id=self.hidden_physical_id) as cam:
50      props = cam.get_camera_properties()
51      props = cam.override_with_hidden_physical_camera_props(props)
52      camera_properties_utils.skip_unless(
53          camera_properties_utils.read_3a(props))
54      mono_camera = camera_properties_utils.mono_camera(props)
55
56      # Load chart for scene
57      its_session_utils.load_scene(
58          cam, props, self.scene, self.tablet, self.chart_distance)
59
60      # Do AE/AF/3A and evaluate outputs
61      for k, three_a_req in sorted(THREE_A_STATES.items()):
62        logging.debug('Trying %s', k)
63        try:
64          s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True,
65                                                     do_ae=three_a_req[0],
66                                                     do_af=three_a_req[1],
67                                                     do_awb=three_a_req[2],
68                                                     mono_camera=mono_camera)
69
70        except error_util.CameraItsError:
71          raise AssertionError(f'{k} did not converge.')
72
73        logging.debug('AWB gains: %s, xform: %s', str(awb_gains),
74                      str(awb_xform))
75        if three_a_req[0]:  # can report None for AF only
76          if not e:
77            raise AssertionError('No valid exposure time returned for AE.')
78          if not s:
79            raise AssertionError('No valid sensitivity returned for AE.')
80          logging.debug('AE sensitivity: %d, exposure: %dns', s, e)
81        else:
82          logging.debug('AE sensitivity: None, exposure: None')
83        if three_a_req[1]:  # fd can report None for AE only
84          logging.debug('AF fd: %.3f', fd)
85        else:
86          logging.debug('AF fd: None')
87        # check AWB values
88        if len(awb_gains) != AWB_GAINS_LENGTH:
89          raise AssertionError(f'Problem with AWB gains: {awb_gains}')
90        for g in awb_gains:
91          if np.isnan(g):
92            raise AssertionError('Gain in AWB is NaN.')
93        if len(awb_xform) != AWB_XFORM_LENGTH:
94          raise AssertionError(f'Problem with AWB transform: {awb_xform}')
95        for x in awb_xform:
96          if np.isnan(x):
97            raise AssertionError('Value in AWB transform is NaN.')
98        if not np.isclose(awb_gains[G_CHANNEL], G_GAIN, G_GAIN_TOL):
99          raise AssertionError(
100              f'AWB G channel mismatch. AWB(G): {awb_gains[G_CHANNEL]}, '
101              f'REF: {G_GAIN}, TOL: {G_GAIN_TOL}')
102
103        # check AE values
104        if k == 'full_3a' or k == 'ae':
105          if s < min(props['android.sensor.info.sensitivityRange']):
106            raise AssertionError(f'ISO is < minimum! ISO: {s}')
107          if e < min(props['android.sensor.info.exposureTimeRange']):
108            raise AssertionError(f'Exposure is < minimum! exp: {e}')
109
110        # check AF values
111        if k == 'full_3a' or k == 'af':
112          if fd < 0:
113            raise AssertionError(f'Focal distance is < 0! fd: {fd}')
114
115if __name__ == '__main__':
116  test_runner.main()
117
118