xref: /aosp_15_r20/cts/apps/CameraITS/tests/scene0/test_jitter.py (revision b7c941bb3fa97aba169d73cee0bed2de8ac964bf)
1*b7c941bbSAndroid Build Coastguard Worker# Copyright 2014 The Android Open Source Project
2*b7c941bbSAndroid Build Coastguard Worker#
3*b7c941bbSAndroid Build Coastguard Worker# Licensed under the Apache License, Version 2.0 (the "License");
4*b7c941bbSAndroid Build Coastguard Worker# you may not use this file except in compliance with the License.
5*b7c941bbSAndroid Build Coastguard Worker# You may obtain a copy of the License at
6*b7c941bbSAndroid Build Coastguard Worker#
7*b7c941bbSAndroid Build Coastguard Worker#      http://www.apache.org/licenses/LICENSE-2.0
8*b7c941bbSAndroid Build Coastguard Worker#
9*b7c941bbSAndroid Build Coastguard Worker# Unless required by applicable law or agreed to in writing, software
10*b7c941bbSAndroid Build Coastguard Worker# distributed under the License is distributed on an "AS IS" BASIS,
11*b7c941bbSAndroid Build Coastguard Worker# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*b7c941bbSAndroid Build Coastguard Worker# See the License for the specific language governing permissions and
13*b7c941bbSAndroid Build Coastguard Worker# limitations under the License.
14*b7c941bbSAndroid Build Coastguard Worker"""CameraITS test to measure jitter in camera timestamps."""
15*b7c941bbSAndroid Build Coastguard Worker
16*b7c941bbSAndroid Build Coastguard Workerimport logging
17*b7c941bbSAndroid Build Coastguard Workerimport os.path
18*b7c941bbSAndroid Build Coastguard Worker
19*b7c941bbSAndroid Build Coastguard Workerfrom matplotlib import pyplot as plt
20*b7c941bbSAndroid Build Coastguard Workerfrom mobly import test_runner
21*b7c941bbSAndroid Build Coastguard Worker
22*b7c941bbSAndroid Build Coastguard Workerimport its_base_test
23*b7c941bbSAndroid Build Coastguard Workerimport camera_properties_utils
24*b7c941bbSAndroid Build Coastguard Workerimport capture_request_utils
25*b7c941bbSAndroid Build Coastguard Workerimport its_session_utils
26*b7c941bbSAndroid Build Coastguard Worker
27*b7c941bbSAndroid Build Coastguard Worker_NS_TO_MS = 1.0E-6
28*b7c941bbSAndroid Build Coastguard Worker_NAME = os.path.basename(__file__).split('.')[0]
29*b7c941bbSAndroid Build Coastguard Worker_NUM_FRAMES = 50
30*b7c941bbSAndroid Build Coastguard Worker_START_FRAME = 2  # 1 frame delay to allow faster latency to 1st frame
31*b7c941bbSAndroid Build Coastguard Worker_TEST_FPS = 30  # frames per second
32*b7c941bbSAndroid Build Coastguard Worker# PASS/FAIL thresholds
33*b7c941bbSAndroid Build Coastguard Worker_MIN_AVG_FRAME_DELTA = 30  # at least 30ms delta between frames
34*b7c941bbSAndroid Build Coastguard Worker_MAX_INIT_FRAME_DELTA = 100  # no more than 100ms between first 2 frames
35*b7c941bbSAndroid Build Coastguard Worker_MAX_VAR_FRAME_DELTA = 0.01  # variance of frame deltas
36*b7c941bbSAndroid Build Coastguard Worker_MAX_FRAME_DELTA_JITTER = 0.3  # max ms gap from the average frame delta
37*b7c941bbSAndroid Build Coastguard Worker
38*b7c941bbSAndroid Build Coastguard Worker
39*b7c941bbSAndroid Build Coastguard Workerclass JitterTest(its_base_test.ItsBaseTest):
40*b7c941bbSAndroid Build Coastguard Worker  """Measure jitter in camera timestamps."""
41*b7c941bbSAndroid Build Coastguard Worker
42*b7c941bbSAndroid Build Coastguard Worker  def test_jitter(self):
43*b7c941bbSAndroid Build Coastguard Worker    with its_session_utils.ItsSession(
44*b7c941bbSAndroid Build Coastguard Worker        device_id=self.dut.serial,
45*b7c941bbSAndroid Build Coastguard Worker        camera_id=self.camera_id,
46*b7c941bbSAndroid Build Coastguard Worker        hidden_physical_id=self.hidden_physical_id) as cam:
47*b7c941bbSAndroid Build Coastguard Worker      props = cam.get_camera_properties()
48*b7c941bbSAndroid Build Coastguard Worker      props = cam.override_with_hidden_physical_camera_props(props)
49*b7c941bbSAndroid Build Coastguard Worker      camera_properties_utils.skip_unless(
50*b7c941bbSAndroid Build Coastguard Worker          camera_properties_utils.manual_sensor(props) and
51*b7c941bbSAndroid Build Coastguard Worker          camera_properties_utils.sensor_fusion(props))
52*b7c941bbSAndroid Build Coastguard Worker
53*b7c941bbSAndroid Build Coastguard Worker      req, fmt = capture_request_utils.get_fastest_manual_capture_settings(
54*b7c941bbSAndroid Build Coastguard Worker          props)
55*b7c941bbSAndroid Build Coastguard Worker      req['android.control.aeTargetFpsRange'] = [_TEST_FPS, _TEST_FPS]
56*b7c941bbSAndroid Build Coastguard Worker      caps = cam.do_capture([req] * _NUM_FRAMES, [fmt])
57*b7c941bbSAndroid Build Coastguard Worker
58*b7c941bbSAndroid Build Coastguard Worker      # Log the millisecond delta between the start of each exposure
59*b7c941bbSAndroid Build Coastguard Worker      tstamps = [c['metadata']['android.sensor.timestamp'] for c in caps]
60*b7c941bbSAndroid Build Coastguard Worker      if (tstamps[1]-tstamps[0])*_NS_TO_MS > _MAX_INIT_FRAME_DELTA:
61*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError('Initial frame timestamp delta too great! '
62*b7c941bbSAndroid Build Coastguard Worker                             f'tstamp[1]: {tstamps[1]}ms, '
63*b7c941bbSAndroid Build Coastguard Worker                             f'tstamp[0]: {tstamps[0]}ms, '
64*b7c941bbSAndroid Build Coastguard Worker                             f'ATOL: {_MAX_INIT_FRAME_DELTA}ms')
65*b7c941bbSAndroid Build Coastguard Worker      deltas = [
66*b7c941bbSAndroid Build Coastguard Worker          tstamps[i] - tstamps[i-1] for i in range(_START_FRAME, len(tstamps))
67*b7c941bbSAndroid Build Coastguard Worker      ]
68*b7c941bbSAndroid Build Coastguard Worker      deltas_ms = [d * _NS_TO_MS for d in deltas]
69*b7c941bbSAndroid Build Coastguard Worker      avg = sum(deltas_ms) / len(deltas_ms)
70*b7c941bbSAndroid Build Coastguard Worker      var = sum([d * d for d in deltas_ms]) / len(deltas_ms) - avg * avg
71*b7c941bbSAndroid Build Coastguard Worker      range0 = min(deltas_ms) - avg
72*b7c941bbSAndroid Build Coastguard Worker      range1 = max(deltas_ms) - avg
73*b7c941bbSAndroid Build Coastguard Worker
74*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Average: %s', avg)
75*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Variance: %s', var)
76*b7c941bbSAndroid Build Coastguard Worker      logging.debug('Jitter range: %s to %s', range0, range1)
77*b7c941bbSAndroid Build Coastguard Worker
78*b7c941bbSAndroid Build Coastguard Worker      # Draw a plot.
79*b7c941bbSAndroid Build Coastguard Worker      plt.figure()
80*b7c941bbSAndroid Build Coastguard Worker      plt.plot(range(len(deltas_ms)), deltas_ms, '-bo')
81*b7c941bbSAndroid Build Coastguard Worker      plt.title(_NAME)
82*b7c941bbSAndroid Build Coastguard Worker      plt.xlabel('frame number')
83*b7c941bbSAndroid Build Coastguard Worker      plt.ylabel('jitter (ms)')
84*b7c941bbSAndroid Build Coastguard Worker      name_with_log_path = os.path.join(self.log_path, _NAME)
85*b7c941bbSAndroid Build Coastguard Worker      plt.savefig(f'{name_with_log_path}_deltas.png')
86*b7c941bbSAndroid Build Coastguard Worker
87*b7c941bbSAndroid Build Coastguard Worker      # Test for pass/fail.
88*b7c941bbSAndroid Build Coastguard Worker      if avg <= _MIN_AVG_FRAME_DELTA:
89*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(
90*b7c941bbSAndroid Build Coastguard Worker            f'avg: {avg:.4f}ms, ATOL: {_MIN_AVG_FRAME_DELTA}ms'
91*b7c941bbSAndroid Build Coastguard Worker        )
92*b7c941bbSAndroid Build Coastguard Worker      if var >= _MAX_VAR_FRAME_DELTA:
93*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(
94*b7c941bbSAndroid Build Coastguard Worker            f'var: {var:.4f}ms, ATOL: {_MAX_VAR_FRAME_DELTA}ms'
95*b7c941bbSAndroid Build Coastguard Worker        )
96*b7c941bbSAndroid Build Coastguard Worker      if (abs(range0) >= _MAX_FRAME_DELTA_JITTER or
97*b7c941bbSAndroid Build Coastguard Worker          abs(range1) >= _MAX_FRAME_DELTA_JITTER):
98*b7c941bbSAndroid Build Coastguard Worker        raise AssertionError(
99*b7c941bbSAndroid Build Coastguard Worker            f'range0: {range0:.4f}ms, range1: {range1:.4f}ms, '
100*b7c941bbSAndroid Build Coastguard Worker            f'ATOL: {_MAX_FRAME_DELTA_JITTER}'
101*b7c941bbSAndroid Build Coastguard Worker        )
102*b7c941bbSAndroid Build Coastguard Worker
103*b7c941bbSAndroid Build Coastguard Worker
104*b7c941bbSAndroid Build Coastguard Workerif __name__ == '__main__':
105*b7c941bbSAndroid Build Coastguard Worker  test_runner.main()
106