4242
4343
4444# Constants needed for precise handling of timestamps
45- RECEIVED_TIMESTAMP_STRUCT = struct.Struct("@ll")
45+ RECEIVED_TIMESPEC_STRUCT = struct.Struct("@ll")
4646RECEIVED_ANCILLARY_BUFFER_SIZE = (
47- CMSG_SPACE(RECEIVED_TIMESTAMP_STRUCT .size) if CMSG_SPACE_available else 0
47+ CMSG_SPACE(RECEIVED_TIMESPEC_STRUCT .size * 3 ) if CMSG_SPACE_available else 0
4848)
4949
5050
@@ -556,11 +556,26 @@ def capture_message(
556556 # Fetching the timestamp
557557 assert len(ancillary_data) == 1, "only requested a single extra field"
558558 cmsg_level, cmsg_type, cmsg_data = ancillary_data[0]
559- assert (
560- cmsg_level == socket.SOL_SOCKET and cmsg_type == constants.SO_TIMESTAMPNS
559+ assert cmsg_level == socket.SOL_SOCKET and (
560+ (cmsg_type == constants.SO_TIMESTAMPNS)
561+ or (cmsg_type == constants.SO_TIMESTAMPING)
561562 ), "received control message type that was not requested"
562563 # see https://man7.org/linux/man-pages/man3/timespec.3.html -> struct timespec for details
563- seconds, nanoseconds = RECEIVED_TIMESTAMP_STRUCT.unpack_from(cmsg_data)
564+
565+ if cmsg_type == constants.SO_TIMESTAMPNS:
566+ seconds, nanoseconds = RECEIVED_TIMESPEC_STRUCT.unpack_from(cmsg_data)
567+
568+ if cmsg_type == constants.SO_TIMESTAMPING:
569+ # stamp[0] is the software timestamp
570+ # stamp[1] is deprecated
571+ # stamp[2] is the raw hardware timestamp
572+ # See chapter 2.1.2 Receive timestamps in
573+ # linux/Documentation/networking/timestamping.txt
574+ offset = struct.calcsize(RECEIVED_TIMESPEC_STRUCT.format) * 2
575+ seconds, nanoseconds = RECEIVED_TIMESPEC_STRUCT.unpack_from(
576+ cmsg_data, offset=offset
577+ )
578+
564579 if nanoseconds >= 1e9:
565580 raise can.CanOperationError(
566581 f"Timestamp nanoseconds field was out of range: {nanoseconds} not less than 1e9"
@@ -619,6 +634,7 @@ def __init__(
619634 self,
620635 channel: str = "",
621636 receive_own_messages: bool = False,
637+ use_system_timestamp: bool = True,
622638 local_loopback: bool = True,
623639 fd: bool = False,
624640 can_filters: Optional[CanFilters] = None,
@@ -642,6 +658,9 @@ def __init__(
642658 channel using :attr:`can.Message.channel`.
643659 :param receive_own_messages:
644660 If transmitted messages should also be received by this bus.
661+ :param bool use_system_timestamp:
662+ Use system timestamp for can messages instead of the hardware time
663+ stamp
645664 :param local_loopback:
646665 If local loopback should be enabled on this bus.
647666 Please note that local loopback does not mean that messages sent
@@ -659,6 +678,7 @@ def __init__(
659678 self.socket = create_socket()
660679 self.channel = channel
661680 self.channel_info = f"socketcan channel '{channel}'"
681+ self.use_system_timestamp = use_system_timestamp
662682 self._bcm_sockets: Dict[str, socket.socket] = {}
663683 self._is_filtered = False
664684 self._task_id = 0
@@ -703,12 +723,25 @@ def __init__(
703723 except OSError as error:
704724 log.error("Could not enable error frames (%s)", error)
705725
706- # enable nanosecond resolution timestamping
707- # we can always do this since
708- # 1) it is guaranteed to be at least as precise as without
709- # 2) it is available since Linux 2.6.22, and CAN support was only added afterward
710- # so this is always supported by the kernel
711- self.socket.setsockopt(socket.SOL_SOCKET, constants.SO_TIMESTAMPNS, 1)
726+ if self.use_system_timestamp:
727+ # Utilise SOF_TIMESTAMPNS interface :
728+ # we can always do this since
729+ # 1) it is guaranteed to be at least as precise as without
730+ # 2) it is available since Linux 2.6.22, and CAN support was only added afterward
731+ # so this is always supported by the kernel
732+ self.socket.setsockopt(socket.SOL_SOCKET, constants.SO_TIMESTAMPNS, 1)
733+ else:
734+ # Utilise SOF_TIMESTAMPNS interface :
735+ # Allows us to use hardware timestamps where available
736+ timestamping_flags = (
737+ constants.SOF_TIMESTAMPING_SOFTWARE
738+ | constants.SOF_TIMESTAMPING_RX_SOFTWARE
739+ | constants.SOF_TIMESTAMPING_RAW_HARDWARE
740+ )
741+
742+ self.socket.setsockopt(
743+ socket.SOL_SOCKET, constants.SO_TIMESTAMPING, timestamping_flags
744+ )
712745
713746 try:
714747 bind_socket(self.socket, channel)
0 commit comments