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