xref: /aosp_15_r20/cts/apps/CameraITS/tests/scene0/test_jitter.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
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