![]() |
AMS Advanced Air Mobility Sensors UG
|
The FreeRTOS operating system with CMSIS API is used.
Rationale 1: we need multiple tasks that are executed at different frequencies.
Rationale 2: we use FreeRTOS because
Rationale 3: we use CMSIS API v2 because
The software is written in C and C++. The languages can be mixed.
Rationale: C++ features are handy for implementation of sensor fusion algorithms, at the same time HAL libraries and FreeRTOS source files are in C.
All the source code files shall fall into one of the following categories:
A software component is a class that realizes one or more primary or auxiliary system functions mapped to the software. A wrapper that inplements a C-API of a software component belongs to the software component.
All the operating system source files including API wrappers belong to the operating system.
Everything that is neither a software component nor a piece of an operating system. Examples: linear algebra library, runtime environment (RTE) implementation, STM32 HAL libraries.
Dynamic memory allocation is prohibited in production code to avoid non-deterministic timing, heap fragmentation, and memory exhaustion risks critical for safety-critical systems.
Dynamic memory is permitted only in the following scenarios:
std::vector for RS232 transmission) and printf functionality when explicitly enabled via CMake option ENABLE_PRINTF. These features must never appear in release builds.The system uses a multi-layered approach to prevent accidental dynamic memory usage:
Layer 1: Disable newlib heap
The _sbrk() function (foundation of C standard library allocators) is replaced with a stub that triggers an assertion, preventing initialization of newlib's heap.
Layer 2: Block C memory functions
Standard C memory functions (malloc, free, calloc, realloc) are replaced with stubs that assert immediately in production builds.
When ENABLE_PRINTF is defined for local debugging (V3 only, via CMake option), the _malloc_r() and _free_r() are wrapped and routed through C++ operators. The _calloc_r() and _realloc_r() remain unsupported as printf does not require them.
Layer 3: Route C++ new/delete to FreeRTOS
All C++ operator new and operator delete overloads are redirected to FreeRTOS heap functions (pvPortMalloc, vPortFree). Note: These operators remain functional in release builds but are prohibited by coding guidelines and enforced through code reviews. This provides:
Layer 4: Prevent allocation in interrupts
All allocation functions assert if called from interrupt context, as FreeRTOS heap functions are not interrupt-safe.
Layer 5: No aligned allocation support
FreeRTOS heap does not support memory alignment. Calls to aligned new/delete operators trigger assertions.
Allowed in debug builds only:
std::vector, std::string, etc. (wrapped in #ifdef SEND_DEBUG_OUTPUT)ENABLE_PRINTF CMake option is enabled)Forbidden in all builds:
malloc(), calloc(), etc.Rationale: This approach provides multiple safety barriers while maintaining flexibility for development and debugging. Violations are enforced through a combination of hard assertions (C allocation functions, aligned allocations, interrupt context usage) and policy-based code reviews (new/delete operators).
Remark: the aforementioned design patterns shall help to fulfill the software component design principles listed in the previous section.
Example of a SWC declaration: CSoftwareComponentExample.
Runtime environment (RTE) is an object that aggregates all SWC ports.
Ports are the only data exchange mechanism between SWCs. Each port is a mutex-protected value with a unique ID and fixed data type. Ports are created statically, generated by the RTE generator, and initialized once during system startup. All port IDs must be unique across the system, and each port object is a unique C++ type because the ID is a template parameter. Ports provide safe data exchange between SWCs that run in different FreeRTOS tasks and at different rates. The mutex is created in CSoftwareComponentPortBase::CSoftwareComponentPortBase; CSoftwareComponentPortBase::Init is kept for API compatibility and only returns the readiness flag.
Port Read() operation
Port Write() operation
Bulk read of multiple ports
CPortReader::ReadPorts() reads several ports as one operation to provide a consistent snapshot for a runnable. Use this API whenever a runnable depends on multiple input ports; use single-port Read() only for isolated reads where cross-port consistency is not required. The function is a variadic template and returns a tuple of the exact port data types, providing compile-time type safety.
This bulk-read pattern is used by both target firmware and PC re-simulation. The runnable-call event and the per-write port data (when enabled) ensure the replay tool consumes the same port values as the firmware at each runnable call in real time. This keeps offline analysis aligned with in-target behavior without exposing OS or HAL APIs to application SWCs.
Attitude and vertical motion (including altitude and vertical velocity) are estimated using data from a 6D IMU and a barometer. A Kalman filter is employed to fuse the signals from both sensors. This approach represents the current state-of-the-art and offers several key advantages:
The estimator is implement in the form of a closed-loop error state Kalman filter. In this formulation, a linear Kalman filter is used to estimate the error in the state accumulated during the nonlinear state prediction process. This estimated state error is then applied to correct the predicted state, allowing the nonlinear prediction process to continue from the corrected value.
The state vector has dimension 12 and includes
The state error vector has dimension 10 and includes
Magnetic heading is estimated separately from attitude and vertical motion.
Rationale:
Magnetometer signals are highly susceptible to environmental disturbances that vary significantly by location. These disturbances can degrade the performance and stability of a Kalman filter, reducing the accuracy and reliability of the estimated attitude and vertical motion parameters. Additionally, we have no control over where customers install EULER-NAV devices within the target vehicle, and they may choose a location prone to intense magnetic interference. Because our device cannot predict or compensate for these disturbances, incorporating magnetometer data into the core attitude and vertical motion estimates could lead to corrupted results, potentially giving customers a false impression of our product’s performance and quality.
Rationale:
Allowing the Kalman filter to switch IMU input sources would necessitate re-initializing the estimation process for inertial sensor biases each time a switch occurs. Frequent re-initializations – such as those triggered by IMU monitor's false alarms – could degrade output accuracy, reduce filter stability, and endanger output integrity.