Back to articles

How to Retrieve CAN Bus Frame Time Duration

Blog

Learn how to calculate a CAN bus frame duration time using Python, by Ori Roza

There are a lot of CAN Bus frame parsers out there: CAN live plugin for Wireshark, Python libraries and third-party software. The generic parsed fields from these parsers are “ID”, “Bus”, “Data”, “Length”, “Time since streaming started” and “Period”.  We can use all these values and calculate the message time duration, which can help us sync our frames and send them correctly.

In this article we will learn how to calculate a frame duration time using Python.

Frame Time Duration Calculation Explanation

The bit time duration rate on a custom bus is: 1/ bus baudrate. So, in theory, if we calculate the size of a given message and divide it by the bus baudrate we will get the message time duration.

Before the coding part let’s see what we need to retrieve.

The function for calculating message size in bits is:

 

(frame_data_len * 8 + frame_extended_value + frame_stuffing_bits)

 

frame_data_len – how many bytes of data the frame has (between 0 and 8)

frame_extended_value – If ID is extended (29 bits long ID) then the value is 64 else (11 bits long ID) the value is 44. (8* frame_data_len) + frame_extended_value is the frame size before stuffing.

frame_stuffing– To ensure enough transitions to maintain synchronization, a bit of opposite polarity is inserted after five consecutive bits of the same polarity. This practice is called bit stuffing, and is necessary due to the non-return to zero (NRZ) coding used with CAN. The stuffed data frames are destuffed by the receiver.

 

Practically this means that we increase the bit count by 1 every time we detect a sequence of 5 bits of the same bit:

1000001 – stuffing raised by 1

The calculate starts at the first bit and ends at the CRC delimiter bit.

 

CC BY-SA 3.0, https://commons.wikimedia.org/w/index.php?curid=31571812, CAN bus Frame, time duration, cyber security, Arilou, NNG, CAN bus, ECU, IVN

CAN-Frame before and after the addition of stuff bits (in purple), from Wikipedia. For more info follow the link.

 

We will not consider the CRC segment this time.

Now we can start coding!

Checking Whether ID is Extended or Not

We need to check if the ID is bigger than 11 bits limit, bigger than 2047 (0x7ff)

 

def is_extended(msg_id):
        """
        Return if msg ID is extended
        """
        return msg_id > 0x7ff

 

Represent Frame Fields to Binary

As we have seen before, we need to retrieve the bit stuffing of the frame, hence we need to parse the message to binary. We will pack it to its binary representation.

First our frame struct:

We use the construct library in python.

The struct is split to two main structs:

Header – contains:

  • DLC – frame data length
  • Interface – bus number (will be 1, because we use the primary bus)
  • IDE – Whether the ID is extended or not
  • Message ID

Packet Struct – contains:

  • The packet header
  • The frame data

 

Each attribute is aligned to its actual size regarding the CAN bus protocol.

 

          from construct import *

           # ===============================================================================
           # Construct object defining the frame structure
           # ===============================================================================
           packet_header = BitStruct(
               "dlc" / Nibble,
               "interface" / Bit,
               "ide" / Bit,
               "mid" / Embedded(IfThenElse(this.ide,
                                           Struct(Padding(5), "mid" / 
           BitsInteger(29)),
            # Extended
                                           Struct(Padding(7), "mid" / 
           BitsInteger(11)))
                               )
           )

           packet_struct = Struct(
               "packet_header" / packet_header,
               "data" / Array(this.packet_header.dlc, Int8ul),
           )

 

Now we can build our packet:

The function “build” returns the data in bytes. For further convenient use, we will parse the bytes to a string binary representation.

 

def pack_msg(msg):
        """
       Returns the message packed in binary as STRING
       '100100010010'
       :param msg:
       :return:
       """
       packet_header_dict = {"dlc": len(msg["data"]), 
"interface": msg["bus"], "ide": is_extended(msg["ID"]),
                                              "mid": msg["ID"]}
       frame_data = {"packet_header": packet_header_dict, "data": 
list(msg["data"]), "time": msg["time"]}
       return ''.join(format(x, 'b') for x in 
bytearray(packet_struct.build(frame_data)))

 

Get Frame Bit Stuffing

As we have seen above and in the Wikipedia example, every time we detect a bit sequence that is bigger than, or equal to 5, we need to increase our counter by 1.

 

def get_msg_stuffing(msg):
         """
         Returns how many stuffing bits the msg has
         :param msg: msg packed
         :return:
         """
         packed = pack_msg(msg) # 100000101…
         sequences = [bit[0] for bit in re.findall(r"((.)\2*)", 
packed)] # [1, 00000, 1, 0, 1]
        num_of_sequence = 0
        for seq in sequences:
              if len(seq) >= 5:
              num_of_sequence += len(seq) / 5
        return num_of_sequence # =1

 

Putting it all together

We retrieved whether the message ID is extended or not, and we retrieved the stuffing bits number. We are ready to calculate the message time duration:

 

def calculate_time_delta_addition(msg):
        """
        Returns (len(msg)*8 + (44,64) + stuffing)/ bus_baudrate
        :param msg:
        :return:
        """
        msg_extended_value = 64 if is_extended(msg["ID"]) else 44
        return float((len(msg["data"]) * 8 + msg_extended_value + 
get_msg_stuffing(msg)) / msg["bus_baudrate"]])

 

Let’s check the result:

 

Code results, Arilou, cyber security, time, frame, duration, CAN bus, IVN,

 

Conclusion

Getting a message duration time can help us to understand synchronization on the bus and send our frames correctly. We can see that calculating this value is quite easy and can be executed as part of the code flow.

I hope this article taught you something new. If you have any feedback, or would like to know more, please reach out to me here.

Ori is a lead software developer at Arilou Cyber Security (part of NNG Group). He is responsible for integration systems in the R&D team and is also a technical writer and Python evangelist.

 

Published bypublished