42
42
43
43
44
44
# Constants needed for precise handling of timestamps
45
- RECEIVED_TIMESTAMP_STRUCT = struct .Struct ("@ll" )
45
+ RECEIVED_TIMESPEC_STRUCT = struct .Struct ("@ll" )
46
46
RECEIVED_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
48
48
)
49
49
50
50
@@ -636,11 +636,24 @@ def capture_message(
636
636
# Fetching the timestamp
637
637
assert len (ancillary_data ) == 1 , "only requested a single extra field"
638
638
cmsg_level , cmsg_type , cmsg_data = ancillary_data [0 ]
639
- assert (
640
- cmsg_level == socket .SOL_SOCKET and cmsg_type == constants .SO_TIMESTAMPNS
639
+ assert cmsg_level == socket .SOL_SOCKET and cmsg_type in (
640
+ constants .SO_TIMESTAMPNS ,
641
+ constants .SO_TIMESTAMPING ,
641
642
), "received control message type that was not requested"
642
643
# see https://man7.org/linux/man-pages/man3/timespec.3.html -> struct timespec for details
643
- seconds , nanoseconds = RECEIVED_TIMESTAMP_STRUCT .unpack_from (cmsg_data )
644
+ if cmsg_type == constants .SO_TIMESTAMPNS :
645
+ seconds , nanoseconds = RECEIVED_TIMESPEC_STRUCT .unpack_from (cmsg_data )
646
+ else :
647
+ # cmsg_type == constants.SO_TIMESTAMPING
648
+ #
649
+ # stamp[0] is the software timestamp
650
+ # stamp[1] is deprecated
651
+ # stamp[2] is the raw hardware timestamp
652
+ offset = struct .calcsize (RECEIVED_TIMESPEC_STRUCT .format ) * 2
653
+ seconds , nanoseconds = RECEIVED_TIMESPEC_STRUCT .unpack_from (
654
+ cmsg_data , offset = offset
655
+ )
656
+
644
657
if nanoseconds >= 1e9 :
645
658
raise can .CanOperationError (
646
659
f"Timestamp nanoseconds field was out of range: { nanoseconds } not less than 1e9"
@@ -699,6 +712,7 @@ def __init__(
699
712
self ,
700
713
channel : str = "" ,
701
714
receive_own_messages : bool = False ,
715
+ can_hardware_timestamps : bool = False ,
702
716
local_loopback : bool = True ,
703
717
fd : bool = False ,
704
718
can_filters : Optional [CanFilters ] = None ,
@@ -722,6 +736,17 @@ def __init__(
722
736
channel using :attr:`can.Message.channel`.
723
737
:param receive_own_messages:
724
738
If transmitted messages should also be received by this bus.
739
+ :param bool can_hardware_timestamps:
740
+ Use raw hardware timestamp for can messages if available instead
741
+ of the system timestamp. By default we use the SO_TIMESTAMPNS
742
+ interface which provides ns resolution but low accuracy. If your
743
+ can hardware supports it you can use this parameter to
744
+ alternatively use the SO_TIMESTAMPING interface and request raw
745
+ hardware timestamps. These are much higher precision but will
746
+ almost certainly not be referenced to the time of day. There
747
+ may be other pitfalls to such as loopback packets reporting with
748
+ no timestamp at all.
749
+ See https://www.kernel.org/doc/html/latest/networking/timestamping.html
725
750
:param local_loopback:
726
751
If local loopback should be enabled on this bus.
727
752
Please note that local loopback does not mean that messages sent
@@ -739,6 +764,7 @@ def __init__(
739
764
self .socket = create_socket ()
740
765
self .channel = channel
741
766
self .channel_info = f"socketcan channel '{ channel } '"
767
+ self ._can_hardware_timestamps = can_hardware_timestamps
742
768
self ._bcm_sockets : Dict [str , socket .socket ] = {}
743
769
self ._is_filtered = False
744
770
self ._task_id = 0
@@ -783,12 +809,25 @@ def __init__(
783
809
except OSError as error :
784
810
log .error ("Could not enable error frames (%s)" , error )
785
811
786
- # enable nanosecond resolution timestamping
787
- # we can always do this since
788
- # 1) it is guaranteed to be at least as precise as without
789
- # 2) it is available since Linux 2.6.22, and CAN support was only added afterward
790
- # so this is always supported by the kernel
791
- self .socket .setsockopt (socket .SOL_SOCKET , constants .SO_TIMESTAMPNS , 1 )
812
+ if not self ._can_hardware_timestamps :
813
+ # Utilise SO_TIMESTAMPNS interface :
814
+ # we can always do this since
815
+ # 1) it is guaranteed to be at least as precise as without
816
+ # 2) it is available since Linux 2.6.22, and CAN support was only added afterward
817
+ # so this is always supported by the kernel
818
+ self .socket .setsockopt (socket .SOL_SOCKET , constants .SO_TIMESTAMPNS , 1 )
819
+ else :
820
+ # Utilise SO_TIMESTAMPING interface :
821
+ # Allows us to use raw hardware timestamps where available
822
+ timestamping_flags = (
823
+ constants .SOF_TIMESTAMPING_SOFTWARE
824
+ | constants .SOF_TIMESTAMPING_RX_SOFTWARE
825
+ | constants .SOF_TIMESTAMPING_RAW_HARDWARE
826
+ )
827
+
828
+ self .socket .setsockopt (
829
+ socket .SOL_SOCKET , constants .SO_TIMESTAMPING , timestamping_flags
830
+ )
792
831
793
832
try :
794
833
bind_socket (self .socket , channel )
0 commit comments