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