Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added CAN Equation #328

Merged
merged 11 commits into from
Dec 26, 2024
28 changes: 16 additions & 12 deletions docs/docs/firmware/can-traffic/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Each CAN frame consists of several fields with fixed bit lengths, plus a variabl
|Identifier (ID)| 11 |
|Remote Transmission Request (RTR)| 1|
| Control (DLC)| 6|
|Data| 8 bits per byte with 20% bit stuffing = 9.6x|
|Data| 8 bits per byte = 8x|
|Cyclic Redundancy Check (CRC) | 16 |
|Acknowledgement (ACK)| 2 |
|End of Frame (EOF)| 7|
Expand All @@ -38,22 +38,24 @@ Each CAN frame consists of several fields with fixed bit lengths, plus a variabl

The maximum total frame length is:

\[ 1 + 11 + 1 + 6 + 16 + 2 + 7 + 9.6x = 44 + 9.6x \text{ bits}\]
\[ 1 + 11 + 1 + 6 + 16 + 2 + 7 + 9.6x = 44 + 8x \text{ bits}\]
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved

where \(x\) represents the number of bytes in the variable data field.

--------------------------------------

## Total CAN Traffic

We must multiply total bits per second by 1.2 to account for bit stuffing
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved

\begin{align}
\text{Total Bits Per Second} &= \sum_{i=1}^n \left(\text{Frequency}_i \times \text{Message Length}_i \right)\\
\text{Total Bits Per Second} &= 1.2 \times \sum_{i=1}^n \left(\text{Frequency}_i \times \text{Message Length}_i \right)
\end{align}

## Total Bus Load

\begin{align}
\text{Bus Load}\%= \frac{\text{Total Bits per Second}}{\text{Baud Rate}} \times 100\%
\text{Bus Load}\% = \frac{\text{Total Bits per Second}}{\text{Baud Rate}} \times 100\%
\end{align}

--------------------------------------
Expand All @@ -66,22 +68,24 @@ Baud Rate: 500 kbaud (500,000 bits transferred per second)

|Message Type | Data Length| Frequency (Hz)| Message Length (Bits)|
|--------------|------------|---------------|---------------------|
|Battery Status| 8 | 100 | 44 + 9.6 x 8 = 121|
|Motor Control| 5 | 50 | 44 + 9.6 x 5 = 92|
|Battery Status| 8 | 100 | 44 + 8 x 8 = 108|
|Motor Control| 5 | 50 | 44 + 8 x 5 = 84|

\begin{align}
\text{Total Bits Per Second} &= \sum_{i=1}^n \left(\text{Frequency}_i \times \text{Message Length}_i \right)\\
\text{Total Bits Per Second} &= (100\text{ Hz} \times 121\text{ bits}) + (50\text{ Hz}\times 92\text{ bits})\\
&=(12100 + 4600)\text{ bits per second}\\
&= 16700\text{ bits per second}
\text{Total Bits Per Second} &= 1.2 \times \sum_{i=1}^n \left(\text{Frequency}_i \times \text{Message Length}_i \right)\\
\text{Total Bits Per Second} &= 1.2 \times \left((100\text{ Hz} \times 108\text{ bits}) + (50\text{ Hz} \times 84\text{ bits})\right)\\
&= 1.2 \times \left(10800 + 4200\right)\text{ bits per second}\\
&= 1.2 \times 15000\text{ bits per second}\\
&= 18000\text{ bits per second}
\end{align}


The bus load is the previous example can be calculated as:

$$
\text{Bus Load}\%= \frac{\text{Total Bits per Second}}{\text{Baud Rate}} \times 100\%
\text{Bus Load}\% = \frac{\text{Total Bits per Second}}{\text{Baud Rate}} \times 100\%
$$

$$
\text{Bus Load} = \left(\frac{16700}{500,000}\right) \times 100\% = 3.34\%
\text{Bus Load} = \left(\frac{18000}{500000}\right) \times 100\% = 3.60\%
$$
62 changes: 34 additions & 28 deletions scripts/cangen/cangen/traffic.py
Original file line number Diff line number Diff line change
Expand Up @@ -12,31 +12,37 @@ class CANMessage:
data_length: int
frequency: int


def data_length(data_bytes):
fixed_length = 44 # fixed length of CAN frame
variable_length = data_bytes * 9.6 # accounted for bit stuffing
total_frame_length = variable_length + fixed_length
return total_frame_length


def calculate_bus_load(messages, can_speed):
total_bits = 0
for message in messages:
message_length = data_length(
message.data_length
) # collect frame length of message
total_bits += message_length * message.frequency
bus_load = total_bits / can_speed * 100 # bus load formula
return bus_load


if __name__ == "__main__":
messages = [
CANMessage(data_length=8, frequency=100),
CANMessage(data_length=5, frequency=50),
CANMessage(data_length=5, frequency=500),
]
can_speed = 500000
bus_load = calculate_bus_load(messages, can_speed)
print(f"Calculated Bus Load is: {bus_load:.2f}%")
@property
def bit_count(self):
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
fixed_length = 44 # fixed length of CAN frame
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
variable_length = self.data_length * 8.0 # accounted for bit stuffing
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
total_frame_length = variable_length + fixed_length
return total_frame_length


def calculate_bus_load(messages: list[CANMessage], can_speed: int) -> float:
"""Calculates the bus load percentage based on CAN messages and bus speed

Parameters
----------
messages : list[CANMessage]
A list of CANMessage objects, each containing `bit_count` and `frequency` attributes
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
can_speed : int
The CAN bus speed in bits per second (bps).

Returns
-------
float
Returns bus load as a percentage
"""
if can_speed < 0:
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
print(f"Invalid CAN speed: {can_speed}")
return 0

total_bits = sum(message.bit_count * message.frequency for message in messages)

if total_bits < 0:
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
return 0

bus_load = ((total_bits) * 1.2) / can_speed * 100
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
return round(bus_load, 2)
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
64 changes: 62 additions & 2 deletions scripts/cangen/tests/test_traffic.py
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
Original file line number Diff line number Diff line change
@@ -1,5 +1,65 @@
from cangen.traffic import calculate_bus_load
from cangen.traffic import calculate_bus_load, CANMessage
import pytest


def test_no_messages():
assert calculate_bus_load([], 500000) == 0
assert calculate_bus_load(
[
CANMessage(data_length=8, frequency=100),
CANMessage(data_length=5, frequency=50),
],
500000,
) == pytest.approx(3.60, rel=1e-2)

# zero frequency or negative frequency
assert calculate_bus_load(
[
ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
CANMessage(data_length=8, frequency=0),
CANMessage(data_length=10, frequency=-100),
],
500000,
) == pytest.approx(0, rel=1e-2)

# bus overload
assert calculate_bus_load(
[
CANMessage(data_length=64, frequency=100),
CANMessage(data_length=32, frequency=200),
],
100000,
) == pytest.approx(138.72, rel=1e-5)

ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
# high traffic - high frequency messages
assert calculate_bus_load(
[
CANMessage(data_length=8, frequency=1000),
CANMessage(data_length=4, frequency=800),
],
1000000,
) == pytest.approx(20.26, rel=1e-5)

# zero can speed
try:
assert calculate_bus_load(
[
CANMessage(data_length=8, frequency=1000),
CANMessage(data_length=4, frequency=800),
],
0,
) == pytest.approx(0, rel=1e-5)
except ZeroDivisionError:
print("Cannot divide by zero baud")

ManushPatell marked this conversation as resolved.
Show resolved Hide resolved
# negative can speed
assert (
calculate_bus_load(
[
CANMessage(data_length=8, frequency=1000),
CANMessage(data_length=4, frequency=800),
],
-500000,
)
) == pytest.approx(0, rel=1e-5)

# no message
assert (calculate_bus_load([], 500000)) == pytest.approx(0, rel=1e-5)
Loading