Modular Telemetry Processing & Pass-by-Reference
An ADAS radar node receives relative range and velocity targets from front-facing sensors. Instead of scattering radar calculations across multiple event handlers, clean architecture uses modular functions. When modifying multi-byte CAN payloads or calculating safe following distances, passing arrays or structs by reference ensures high performance and prevents stack duplication.
Overview & Key Concepts
CAPL (CAN Application Programming Language) is used primarily in the automotive and embedded systems industry for the simulation and testing of CAN (Controller Area Network) bus systems. In CAPL, functions are blocks of reusable code that perform specific tasks. Functions help to structure your script more logically, reduce redundancy, and make the code more maintainable. Hereβs an in-depth look at functions in CAPL scripting with detailed examples.
Definition of Functions in CAPL
A function in CAPL is a named block of code that performs a specific task and can be called from anywhere in the script. Functions can take inputs (parameters) and return outputs (return values). Functions can also be divided into two types:
User-defined functions: These are functions created by the user to perform specific tasks.
Built-in functions: CAPL provides several built-in functions for working with CAN signals, timing, system events, etc.
The basic syntax for defining a function is
return_type function_name(parameters) {
// Function body
// Perform operations
}
return_type: The data type of the value the function will return (e.g., int, float, bool, void).
function_name: The name you give to the function, used when calling it.
parameters: Variables passed to the function for processing. Parameters are optional.
return: The function can return a value using the return keyword. If the function is of type void, it does not return anything.
1. Function with no parameters and no return value (void)
In this example, the function doesn't take any parameters and does not return a value.
void displayMessage() {
output("This is a simple message.");
}
void main() {
displayMessage(); // Calling the function
}
The function displayMessage() is called in the main() function.
The function displayMessage() will simply print the message to the output window.
2. Function with parameters and no return value
In this example, the function accepts parameters but doesn't return a value.
void printSignalValue(int signalValue) {
output("Signal Value: %d", signalValue);
}
void main() {
int signal = 10;
printSignalValue(signal); // Calling the function with a parameter
}
The printSignalValue(int signalValue) function takes an integer as an argument and prints it.
The function is called with signal = 10.
3. Function with parameters and a return value
This example demonstrates a function that takes parameters and returns a value.
int calculateSum(int a, int b) {
return a + b;
}
void main() {
int result = calculateSum(5, 10);
output("The sum is: %d", result);
}
The calculateSum() function takes two integer parameters a and b and returns their sum.
The result is stored in the variable result and printed to the output.
4. Function with a boolean return type
This function checks if a signal value is within a specified range and returns a boolean value (true or false).
bool isSignalInRange(int signalValue, int lowerLimit, int upperLimit) {
if (signalValue >= lowerLimit && signalValue <= upperLimit) {
return true;
} else {
return false;
}
}
void main() {
int signalValue = 25;
int lower = 10;
int upper = 30;
bool result = isSignalInRange(signalValue, lower, upper);
if (result) {
output("Signal value is within the range.");
} else {
output("Signal value is outside the range.");
}
}
The function isSignalInRange() checks if signalValue is between lowerLimit and upperLimit and returns true or false.
In the main() function, the result is checked, and a message is printed.
Built-in Functions in CAPL
CAPL comes with a number of built-in functions that make interacting with the CAN bus easier. For instance, there are functions to read and write signals, handle timers, and process CAN messages. Here are some examples:
1. Reading and Writing Signals
CAPL provides functions to read and write signals in the CAN database.
void main() {
// Reading a signal
int signalValue = VehicleSpeed;
// Writing a signal
VehicleSpeed = 50; // Setting the vehicle speed to 50 km/h
}
VehicleSpeed is an example of a signal that can be read or written to.
2. Timer Functions
CAPL provides timer functions that allow you to set up time delays or periodic tasks. Hereβs an example of using a timer function.
void TimerHandler() {
output("Timer expired!");
}
void main() {
SetTimer(TimerHandler, 5000); // Set a timer to call TimerHandler after 5 seconds
}
The SetTimer() function sets a timer to call the TimerHandler function after 5000 milliseconds (5 seconds).
3. Event Handling
CAPL also supports event-driven programming, where certain functions are triggered by specific events, such as receiving a CAN message.
on message MyMessage {
output("Received MyMessage!");
}
The on message MyMessage event handler automatically triggers when MyMessage is received on the CAN bus.
Recursion in CAPL Functions
In CAPL, functions can also be recursive, meaning a function can call itself. This is useful in certain situations like traversing data structures (e.g., linked lists).
int factorial(int n) {
if (n <= 1) {
return 1;
}
return n * factorial(n - 1);
}
void main() {
int result = factorial(5); // This will return 5! = 120
output("Factorial of 5 is: %d", result);
}
The factorial() function calls itself until the base case (n <= 1) is reached.
Passing by Reference in CAPL
CAPL supports passing parameters by reference. This allows the function to modify the argument directly.
void incrementValue(int &value) {
value++;
}
void main() {
int num = 5;
incrementValue(num); // num will be modified to 6
output("Modified value: %d", num);
}
The incrementValue() function takes an integer by reference and modifies it directly.
Best Practices
Modular code: Break down large tasks into smaller, more manageable functions.
Reusability: Write functions to be reused in multiple parts of the script.
Descriptive names: Name functions and parameters descriptively so that their purpose is clear.
Avoid side effects: Functions should perform a single task and not modify global state unnecessarily.
Conclusion
Functions in CAPL help to structure and organize code efficiently. By using functions, you can make your scripts more readable, maintainable, and reusable. CAPL supports both user-defined and built-in functions, allowing for a wide range of capabilities from simple tasks like printing a message to complex interactions with the CAN bus system. With proper function use, CAPL scripting becomes more organized and easier to debug.
- Avoid excessive recursion in CAPL. CAPL uses a fixed stack size, and deep recursion can crash the CANoe simulation engine.
- Function names cannot conflict with built-in CAPL API functions or DBC signal names.
Select your answer for each question below to receive immediate correctness evaluation and in-depth technical rationale.
Write a user-defined function `float CalculateTTC(float relativeDistance_m, float relativeSpeed_mps)` that calculates Time-To-Collision in seconds. If the relative speed is greater than or equal to 0 (lead vehicle moving away or at same speed), return 999.0s (no collision danger).
Verified CAPL implementation:
float CalculateTTC(float relativeDistance_m, float relativeSpeed_mps)
{
float ttc_sec;
// relativeSpeed is negative when distance is closing
if (relativeSpeed_mps >= 0.0)
{
return 999.0; // Distance is increasing or constant
}
ttc_sec = relativeDistance_m / (-relativeSpeed_mps);
return ttc_sec;
}