1# Copyright 2014 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"""CameraITS test to measure jitter in camera timestamps.""" 15 16import logging 17import os.path 18 19from matplotlib import pyplot as plt 20from mobly import test_runner 21 22import its_base_test 23import camera_properties_utils 24import capture_request_utils 25import its_session_utils 26 27_NS_TO_MS = 1.0E-6 28_NAME = os.path.basename(__file__).split('.')[0] 29_NUM_FRAMES = 50 30_START_FRAME = 2 # 1 frame delay to allow faster latency to 1st frame 31_TEST_FPS = 30 # frames per second 32# PASS/FAIL thresholds 33_MIN_AVG_FRAME_DELTA = 30 # at least 30ms delta between frames 34_MAX_INIT_FRAME_DELTA = 100 # no more than 100ms between first 2 frames 35_MAX_VAR_FRAME_DELTA = 0.01 # variance of frame deltas 36_MAX_FRAME_DELTA_JITTER = 0.3 # max ms gap from the average frame delta 37 38 39class JitterTest(its_base_test.ItsBaseTest): 40 """Measure jitter in camera timestamps.""" 41 42 def test_jitter(self): 43 with its_session_utils.ItsSession( 44 device_id=self.dut.serial, 45 camera_id=self.camera_id, 46 hidden_physical_id=self.hidden_physical_id) as cam: 47 props = cam.get_camera_properties() 48 props = cam.override_with_hidden_physical_camera_props(props) 49 camera_properties_utils.skip_unless( 50 camera_properties_utils.manual_sensor(props) and 51 camera_properties_utils.sensor_fusion(props)) 52 53 req, fmt = capture_request_utils.get_fastest_manual_capture_settings( 54 props) 55 req['android.control.aeTargetFpsRange'] = [_TEST_FPS, _TEST_FPS] 56 caps = cam.do_capture([req] * _NUM_FRAMES, [fmt]) 57 58 # Log the millisecond delta between the start of each exposure 59 tstamps = [c['metadata']['android.sensor.timestamp'] for c in caps] 60 if (tstamps[1]-tstamps[0])*_NS_TO_MS > _MAX_INIT_FRAME_DELTA: 61 raise AssertionError('Initial frame timestamp delta too great! ' 62 f'tstamp[1]: {tstamps[1]}ms, ' 63 f'tstamp[0]: {tstamps[0]}ms, ' 64 f'ATOL: {_MAX_INIT_FRAME_DELTA}ms') 65 deltas = [ 66 tstamps[i] - tstamps[i-1] for i in range(_START_FRAME, len(tstamps)) 67 ] 68 deltas_ms = [d * _NS_TO_MS for d in deltas] 69 avg = sum(deltas_ms) / len(deltas_ms) 70 var = sum([d * d for d in deltas_ms]) / len(deltas_ms) - avg * avg 71 range0 = min(deltas_ms) - avg 72 range1 = max(deltas_ms) - avg 73 74 logging.debug('Average: %s', avg) 75 logging.debug('Variance: %s', var) 76 logging.debug('Jitter range: %s to %s', range0, range1) 77 78 # Draw a plot. 79 plt.figure() 80 plt.plot(range(len(deltas_ms)), deltas_ms, '-bo') 81 plt.title(_NAME) 82 plt.xlabel('frame number') 83 plt.ylabel('jitter (ms)') 84 name_with_log_path = os.path.join(self.log_path, _NAME) 85 plt.savefig(f'{name_with_log_path}_deltas.png') 86 87 # Test for pass/fail. 88 if avg <= _MIN_AVG_FRAME_DELTA: 89 raise AssertionError( 90 f'avg: {avg:.4f}ms, ATOL: {_MIN_AVG_FRAME_DELTA}ms' 91 ) 92 if var >= _MAX_VAR_FRAME_DELTA: 93 raise AssertionError( 94 f'var: {var:.4f}ms, ATOL: {_MAX_VAR_FRAME_DELTA}ms' 95 ) 96 if (abs(range0) >= _MAX_FRAME_DELTA_JITTER or 97 abs(range1) >= _MAX_FRAME_DELTA_JITTER): 98 raise AssertionError( 99 f'range0: {range0:.4f}ms, range1: {range1:.4f}ms, ' 100 f'ATOL: {_MAX_FRAME_DELTA_JITTER}' 101 ) 102 103 104if __name__ == '__main__': 105 test_runner.main() 106