import numpy as np def flatten_type_dict(d, sep="/", prefix=None): res = {} if isinstance(d, dict): for key, val in d.items(): if prefix is None: res.update(flatten_type_dict(val, prefix=key)) else: res.update(flatten_type_dict(val, prefix=prefix + sep + key)) return res elif isinstance(d, list): return {prefix: np.array(d)} else: return {prefix: d} def get_message_dict(message, typ): valid = message.valid message = message._get(typ) if not hasattr(message, 'to_dict') or typ in ('qcomGnss', 'ubloxGnss'): # TODO: support these #print("skipping", typ) return msg_dict = message.to_dict(verbose=True) msg_dict = flatten_type_dict(msg_dict) msg_dict['_valid'] = valid return msg_dict def append_dict(path, t, d, values): if path not in values: group = {} group["t"] = [] for k in d: group[k] = [] values[path] = group else: group = values[path] group["t"].append(t) for k, v in d.items(): group[k].append(v) def potentially_ragged_array(arr, dtype=None, **kwargs): # TODO: is there a better way to detect inhomogeneous shapes? try: return np.array(arr, dtype=dtype, **kwargs) except ValueError: return np.array(arr, dtype=object, **kwargs) def msgs_to_time_series(msgs): """ Convert an iterable of canonical capnp messages into a dictionary of time series. Each time series has a value with key "t" which consists of monotonically increasing timestamps in seconds. """ values = {} for msg in msgs: typ = msg.which() tm = msg.logMonoTime / 1.0e9 msg_dict = get_message_dict(msg, typ) if msg_dict is not None: append_dict(typ, tm, msg_dict, values) # Sort values by time. for group in values.values(): order = np.argsort(group["t"]) for name, group_values in group.items(): group[name] = potentially_ragged_array(group_values)[order] return values if __name__ == "__main__": import sys from openpilot.tools.lib.logreader import LogReader m = msgs_to_time_series(LogReader(sys.argv[1])) print(m['driverCameraState']['t']) print(np.diff(m['driverCameraState']['timestampSof']))