"""Independent MDF 4.10 check of artifacts exported by Mf4RecordingRegressionTest. Set DCT_RECORDING_FIXTURES_DIR when running the JUnit test, then run this script with that directory. Requires asammdf; it is not an Android/build dependency. """ import argparse import csv from datetime import datetime from pathlib import Path import numpy as np from asammdf import MDF def verify(directory): checks = [] with MDF(directory / 'relative.mf4') as recording: assert recording.version == '4.10' assert len(recording.groups) == 1 assert recording.groups[0].channel_group.cycles_nr == 2 temperature = recording.get('온도') assert temperature.unit == '°C' np.testing.assert_array_equal(temperature.samples, [-12.5, 3.75]) np.testing.assert_allclose(temperature.timestamps, [0.125, 0.5], atol=1e-6) np.testing.assert_array_equal(recording.get('Speed').samples, [87.25, 88.0]) assert np.isnan(recording.get('Missing').samples).all() checks.append('relative time, names, units, negative/fractional values, NaN') for name, channels in [('empty.mf4', 2), ('time_only.mf4', 1)]: with MDF(directory / name) as recording: group = recording.groups[0] assert group.channel_group.cycles_nr == 0 assert len(group.channels) == channels assert len(recording.get_master(0)) == 0 checks.append(name) with MDF(directory / 'wide.mf4') as recording: assert len(recording.groups[0].channels) == 4940 assert recording.groups[0].channel_group.cycles_nr == 1 assert recording.get('DBC_0').samples.tolist() == [123.5] assert np.isnan(recording.get('DBC_4938').samples).all() np.testing.assert_allclose(recording.get_master(0), [0.25], atol=1e-6) checks.append('4939-signal DBC width and missing tail') with MDF(directory / 'restart.mf4') as recording: assert recording.get('Signal').samples.tolist() == [2.0] np.testing.assert_allclose(recording.get_master(0), [0.25], atol=1e-6) checks.append('restart resets origin, rejects nonfinite timestamps') with (directory / 'paired.csv').open(encoding='utf-8', newline='') as stream: rows = list(csv.reader(stream))[7:] with MDF(directory / 'paired.mf4') as recording: signal = recording.get('Signal') assert len(rows) == len(signal.samples) == 2 assert [int(row[1]) for row in rows] == [0, 1] np.testing.assert_array_equal([float(row[2]) for row in rows], signal.samples) assert [row[0][-4:] for row in rows] == ['.125', '.500'] np.testing.assert_allclose(signal.timestamps, [0.125, 0.5], atol=1e-6) checks.append('CSV/MF4 rows, values and ingress timestamps match') shared_origins = [] for source, first_offset in [('mdb', -0.010), ('dbc', -0.009), ('formula', -0.010)]: with (directory / f'queued_{source}.csv').open(encoding='utf-8', newline='') as stream: rows = list(csv.reader(stream))[7:] with MDF(directory / f'queued_{source}.mf4') as recording: signal = recording.get('Signal') origin = recording.start_time.timestamp() shared_origins.append(origin) assert len(rows) == len(signal.samples) == 3 assert [int(row[1]) for row in rows] == [0, 1, 2] np.testing.assert_array_equal(signal.samples, [0., 1., 2.]) np.testing.assert_array_equal([float(row[2]) for row in rows], signal.samples) np.testing.assert_allclose(signal.timestamps, [first_offset, 0., 0.125], rtol=0, atol=1e-6) np.testing.assert_allclose(origin, 1790923490.915, rtol=0, atol=1e-6) for row, offset in zip(rows, signal.timestamps): # UTC/local HH:mm:ss differences do not change the offset within a minute. csv_second = float(row[0].rsplit(':', 1)[1]) mf4_second = (origin + offset) % 60 np.testing.assert_allclose(csv_second, mf4_second, rtol=0, atol=0.001) checks.append(f'{source}: queued ingress preserved, CSV/MF4 absolute time and values match') assert len(set(shared_origins)) == 1 with MDF(directory / 'queued_midnight.mf4') as recording: signal = recording.get('Signal') origin = recording.start_time.timestamp() np.testing.assert_allclose(signal.timestamps, [-0.010], rtol=0, atol=1e-6) np.testing.assert_array_equal(signal.samples, [7.]) absolute = origin + signal.timestamps[0] np.testing.assert_allclose(absolute, 1790985599.995, rtol=0, atol=1e-6) assert datetime.fromtimestamp(absolute, recording.start_time.tzinfo).date() < recording.start_time.date() checks.append('queued ingress crossing UTC midnight preserves the previous date') for check in checks: print('PASS:', check) print('PASS: ten MF4 files opened by independent reader') if __name__ == '__main__': parser = argparse.ArgumentParser(description=__doc__) parser.add_argument('directory', type=Path) verify(parser.parse_args().directory)