27
27
log .error ("fcntl not available on this platform" )
28
28
29
29
30
+ try :
31
+ from socket import CMSG_SPACE
32
+
33
+ CMSG_SPACE_available = True
34
+ except ImportError :
35
+ CMSG_SPACE_available = False
36
+ log .error ("socket.CMSG_SPACE not available on this platform" )
37
+
38
+
30
39
import can
31
40
from can import Message , BusABC
32
41
from can .broadcastmanager import (
38
47
from can .interfaces .socketcan .constants import * # CAN_RAW, CAN_*_FLAG
39
48
from can .interfaces .socketcan .utils import pack_filters , find_available_interfaces
40
49
50
+
41
51
# Setup BCM struct
42
52
def bcm_header_factory (
43
53
fields : List [Tuple [str , Union [Type [ctypes .c_uint32 ], Type [ctypes .c_long ]]]],
@@ -494,6 +504,8 @@ def bind_socket(sock: socket.socket, channel: str = "can0") -> None:
494
504
495
505
:param sock:
496
506
The socket to be bound
507
+ :param channel:
508
+ The channel / interface to bind to
497
509
:raises OSError:
498
510
If the specified interface isn't found.
499
511
"""
@@ -517,24 +529,27 @@ def capture_message(
517
529
"""
518
530
# Fetching the Arb ID, DLC and Data
519
531
try :
532
+ cf , ancillary_data , msg_flags , addr = sock .recvmsg (
533
+ CANFD_MTU , RECEIVED_ANCILLARY_BUFFER_SIZE
534
+ )
520
535
if get_channel :
521
- cf , _ , msg_flags , addr = sock .recvmsg (CANFD_MTU )
522
536
channel = addr [0 ] if isinstance (addr , tuple ) else addr
523
537
else :
524
- cf , _ , msg_flags , _ = sock .recvmsg (CANFD_MTU )
525
538
channel = None
526
- except socket .error as exc :
527
- raise can .CanError ("Error receiving: %s" % exc )
539
+ except socket .error as error :
540
+ raise can .CanError (f "Error receiving: { error } " )
528
541
529
542
can_id , can_dlc , flags , data = dissect_can_frame (cf )
530
- # log.debug('Received: can_id=%x, can_dlc=%x, data=%s', can_id, can_dlc, data)
531
543
532
544
# Fetching the timestamp
533
- binary_structure = "@LL"
534
- res = fcntl .ioctl (sock .fileno (), SIOCGSTAMP , struct .pack (binary_structure , 0 , 0 ))
535
-
536
- seconds , microseconds = struct .unpack (binary_structure , res )
537
- timestamp = seconds + microseconds * 1e-6
545
+ assert len (ancillary_data ) == 1 , "only requested a single extra field"
546
+ cmsg_level , cmsg_type , cmsg_data = ancillary_data [0 ]
547
+ assert (
548
+ cmsg_level == socket .SOL_SOCKET and cmsg_type == SO_TIMESTAMPNS
549
+ ), "received control message type that was not requested"
550
+ # see https://man7.org/linux/man-pages/man3/timespec.3.html -> struct timespec for details
551
+ seconds , nanoseconds = RECEIVED_TIMESTAMP_STRUCT .unpack_from (cmsg_data )
552
+ timestamp = seconds + nanoseconds * 1e-9
538
553
539
554
# EXT, RTR, ERR flags -> boolean attributes
540
555
# /* special address description flags for the CAN_ID */
@@ -574,11 +589,15 @@ def capture_message(
574
589
data = data ,
575
590
)
576
591
577
- # log_rx.debug('Received: %s', msg)
578
-
579
592
return msg
580
593
581
594
595
+ # Constants needed for precise handling of timestamps
596
+ if CMSG_SPACE_available :
597
+ RECEIVED_TIMESTAMP_STRUCT = struct .Struct ("@II" )
598
+ RECEIVED_ANCILLARY_BUFFER_SIZE = CMSG_SPACE (RECEIVED_TIMESTAMP_STRUCT .size )
599
+
600
+
582
601
class SocketcanBus (BusABC ):
583
602
"""A SocketCAN interface to CAN.
584
603
@@ -647,7 +666,7 @@ def __init__(
647
666
except socket .error as error :
648
667
log .error ("Could not receive own messages (%s)" , error )
649
668
650
- # enable CAN-FD frames
669
+ # enable CAN-FD frames if desired
651
670
if fd :
652
671
try :
653
672
self .socket .setsockopt (SOL_CAN_RAW , CAN_RAW_FD_FRAMES , 1 )
@@ -660,6 +679,13 @@ def __init__(
660
679
except socket .error as error :
661
680
log .error ("Could not enable error frames (%s)" , error )
662
681
682
+ # enable nanosecond resolution timestamping
683
+ # we can always do this since
684
+ # 1) is is guaranteed to be at least as precise as without
685
+ # 2) it is available since Linux 2.6.22, and CAN support was only added afterward
686
+ # so this is always supported by the kernel
687
+ self .socket .setsockopt (socket .SOL_SOCKET , SO_TIMESTAMPNS , 1 )
688
+
663
689
bind_socket (self .socket , channel )
664
690
kwargs .update (
665
691
{
0 commit comments