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 math 19import os.path 20from mobly import test_runner 21import numpy as np 22 23import its_base_test 24import camera_properties_utils 25import error_util 26import its_session_utils 27 28_AWB_GAINS_LENGTH = 4 29_AWB_XFORM_LENGTH = 9 30_G_CHANNEL = 2 31_G_GAIN = 1.0 32_G_GAIN_TOL = 0.05 33_NAME = os.path.splitext(os.path.basename(__file__))[0] 34_THREE_A_STATES = {'AE': (True, False, True), 35 'AF': (False, True, True), 36 'FULL_3A': (True, True, True)} # note no AWB solo 37 38 39class SingleATest(its_base_test.ItsBaseTest): 40 """Test basic camera 3A behavior with AE and AF run individually. 41 42 To pass, 3A must converge. Check that returned 3A values are valid. 43 """ 44 45 def test_ae_af(self): 46 logging.debug('Starting %s', _NAME) 47 with its_session_utils.ItsSession( 48 device_id=self.dut.serial, 49 camera_id=self.camera_id, 50 hidden_physical_id=self.hidden_physical_id) as cam: 51 props = cam.get_camera_properties() 52 props = cam.override_with_hidden_physical_camera_props(props) 53 camera_properties_utils.skip_unless( 54 camera_properties_utils.read_3a(props)) 55 mono_camera = camera_properties_utils.mono_camera(props) 56 57 # Load chart for scene 58 its_session_utils.load_scene( 59 cam, props, self.scene, self.tablet, self.chart_distance) 60 61 # Do AE/AF/3A and evaluate outputs 62 for k, three_a_req in sorted(_THREE_A_STATES.items()): 63 logging.debug('Trying %s', k) 64 try: 65 s, e, awb_gains, awb_xform, fd = cam.do_3a(get_results=True, 66 do_ae=three_a_req[0], 67 do_af=three_a_req[1], 68 do_awb=three_a_req[2], 69 mono_camera=mono_camera) 70 71 except error_util.CameraItsError as e_util: 72 raise AssertionError(f'{k} did not converge.') from e_util 73 74 logging.debug('AWB gains: %s, xform: %s', str(awb_gains), 75 str(awb_xform)) 76 if three_a_req[0]: # can report None for AF only 77 if not e: 78 raise AssertionError('No valid exposure time returned for AE.') 79 if not s: 80 raise AssertionError('No valid sensitivity returned for AE.') 81 logging.debug('AE sensitivity: %d, exposure: %dns', s, e) 82 else: 83 logging.debug('AE sensitivity: None, exposure: None') 84 if three_a_req[1]: # fd can report None for AE only 85 logging.debug('AF fd: %.3f', fd) 86 else: 87 logging.debug('AF fd: None') 88 # check AWB values 89 if len(awb_gains) != _AWB_GAINS_LENGTH: 90 raise AssertionError(f'Problem with AWB gains: {awb_gains}') 91 for g in awb_gains: 92 if np.isnan(g): 93 raise AssertionError('Gain in AWB is NaN.') 94 if len(awb_xform) != _AWB_XFORM_LENGTH: 95 raise AssertionError(f'Problem with AWB transform: {awb_xform}') 96 for x in awb_xform: 97 if np.isnan(x): 98 raise AssertionError('Value in AWB transform is NaN.') 99 if not math.isclose( 100 awb_gains[_G_CHANNEL], _G_GAIN, rel_tol=_G_GAIN_TOL): 101 raise AssertionError( 102 f'AWB G channel mismatch. AWB(G): {awb_gains[_G_CHANNEL]}, ' 103 f'REF: {_G_GAIN}, TOL: {_G_GAIN_TOL}') 104 105 # check AE values 106 if k == 'full_3a' or k == 'ae': 107 if s < min(props['android.sensor.info.sensitivityRange']): 108 raise AssertionError(f'ISO is < minimum! ISO: {s}') 109 if e < min(props['android.sensor.info.exposureTimeRange']): 110 raise AssertionError(f'Exposure is < minimum! exp: {e}') 111 112 # check AF values 113 if k == 'full_3a' or k == 'af': 114 if fd < 0: 115 raise AssertionError(f'Focal distance is < 0! fd: {fd}') 116 117if __name__ == '__main__': 118 test_runner.main() 119 120