- C-ATOM >
- Atomic functions
Atomic functions¶
Atomic function is C-ATOM’s atomic building block for functionality. You may use atomic function from existing library (Atomic functions catalog) or create your own.
Understanding atomic function¶
Atomic function is a software building block for extending existing CatPilot and C-ATOM functionality for specific purposes. Atomic function starts from its specification declaration (functional specification, specification). This specification represents single source of truth for function description and implementation. Integration of the atomic function happens inside SWSYS (SWSYS Introduction), FLOW (FLOW Introduction) and IBR (IBR Introduction) configuration layers.

Depending on the nature of the atomic function several levels exist:
generic - C-ATOM level, basic mathematical operations, navigational calculations, etc (stored inside
atomics/core
inside C-ATOM);board specific - CatPilot level, functions that connect hardware resources like sensors to the C-ATOM’s scope;
project specific - barely reusable function related to the target system, e.g., specific algorithm for the specific vehicle.
Function specification might be found in declaration.py
source inside files tree of atomics
catalog on the specified
level.
Atomic functions related artifacts are produced by the python tool fspecgen.py
which is located at
catpilot/c-atom/tools/fspecgen.py
.
Currently these outputs are available:
C code: atomic function <> communicating middleware source code;
C code: function’s implementation initial stubs;
C code: function’s parameters parsing source code;
C code: prototypes and specification structures;
CMakeLists: atomic function’s software building configuration;
Atomic function specification¶
As an example here is a function specification declaration for PID regulator:
``c-atom/atomics/core/cont/pid/declaration.py``
from fspeclib import *
Function(
name='core.cont.pid',
title=LocalizedString(
en='PID controller'
),
parameters=[
Parameter(
name='Kp',
title='Proportional coefficient',
value_type='core.type.f64',
tunable=True,
default=1,
constraints=[
ThisValue() >= 0
]
),
Parameter(
name='Ki',
title='Integral coefficient',
value_type='core.type.f64',
tunable=True,
default=0,
constraints=[
ThisValue() >= 0
]
),
Parameter(
name='Kd',
title='Derivative coefficient',
value_type='core.type.f64',
tunable=True,
default=0,
constraints=[
ThisValue() >= 0
]
),
Parameter(
name='integral_min',
title='Lower bound of integral part of error',
value_type='core.type.f64',
tunable=True,
default=0,
constraints=[
ThisValue() <= 0
]
),
Parameter(
name='integral_max',
title='Upper bound of integral part of error',
value_type='core.type.f64',
tunable=True,
default=1,
constraints=[
ThisValue() >= 0
]
),
Parameter(
name='output_min',
title='Lower bound of controller output',
value_type='core.type.f64',
tunable=True,
default=0,
constraints=[
ThisValue() <= 0
]
),
Parameter(
name='output_max',
title='Upper bound of controller output',
value_type='core.type.f64',
tunable=True,
default=1,
constraints=[
ThisValue() >= 0,
]
),
],
inputs=[
Input(
name='input',
title='Input',
value_type='core.type.f64'
),
Input(
name='feedback',
title='Feedback',
value_type='core.type.f64'
),
Input(
name='enable',
title='Enable',
value_type='core.type.bool',
mandatory=False
),
Input(
name='preset',
title='Preset source',
description='Used to preset integral part at the moment of the PID enabling',
value_type='core.type.f64',
mandatory=False
),
],
outputs=[
Output(
name='output',
title='Output',
value_type='core.type.f64'
),
Output(
name='enabled',
title='Enabled',
value_type='core.type.bool',
)
],
state=[
Variable(
name='integral',
title='Accumulated Integral term',
value_type='core.type.f64'
),
Variable(
name='previous_error',
title='Previous error',
value_type='core.type.f64'
),
Variable(
name='time_from_last_iteration',
title='Time from last iteration',
value_type='core.type.f64'
),
Variable(
name='activated',
title='PID was activated before by enable signal',
value_type='core.type.bool'
)
],
parameter_constraints=[
ParameterValue('output_max') > ParameterValue('output_min'),
ParameterValue('integral_max') > ParameterValue('integral_min')
],
injection=Injection(
timedelta=True
)
)
Creating atomic function¶
1. Define a level of your atomic function: generic or board specific - if you want to contribute it to the C-ATOM or CatPilot accordingly.
Go to directory
[level_root]/atomics/[class_name]/
Create directory with the name of your function, e.g.
func1
Create a file
[level_root]/atomics/[namespace_name]/[group_name]/func1/declaration.py
Copy and pase basic specification structure:
Replace [namespace_name]
and [group_name]
accordingly.
from fspeclib import *
Function(
name='[namespace_name].[group_name].func1',
title=LocalizedString(
en='Functional function'
),
parameters=[
...
],
inputs=[
...
],
outputs=[
...
],
state=[
...
],
parameter_constraints=[
...
],
injection=Injection(
...
)
)
Describe parameters:
parameters=[
Parameter(
name='p1',
title='Parameter 1',
value_type='core.type.f64',
tunable=True,
default=1,
constraints=[
ThisValue() >= 0
]
),
Parameter(
name='p2',
title='Parameter 2',
value_type='core.type.f64',
tunable=True,
default=1,
constraints=[
ThisValue() >= 0
]
),
...
],
Describe inputs:
inputs=[
Input(
name='input',
title='Input',
value_type='core.type.f64'
),
...
]
Describe outputs:
outputs=[
Output(
name='output',
title='Output',
value_type='core.type.f64'
),
...
],
Describe States variables:
state=[
Variable(
name='integral',
title='Accumulated Integral term',
value_type='core.type.f64'
),
...
]
Describe parameters constrains:
parameter_constraints=[
ParameterValue('p1') > ParameterValue('p2'),
],
...
Specify injection options:
injection=Injection(
timedelta=True
)
Generate code by the command:
./catpilot/c-atom/tools/fspecgen.py --code --cmake --f_specs_dirs project:./atomics/ catpilot:catpilot/atomics/ catom:catpilot/c-atom/atomics/
Inside generated stub
[namespace]_[func1]_exec.c
file implement function as usual C language function: process inputs, states and produce output.
void someNamespace_func1_exec(
const someNamespace_func1_inputs_t *i,
someNamespace_func1_outputs_t *o,
const someNamespace_func1_params_t *p,
someNamespace_func1_state_t *state,
const someNamespace_func1_injection_t *injection
)
{
...
}
Integrating atomic function¶
For integrating new atomic function inside your project you should do the following:
1. Extend build script to run of fspecgen.py
for code generation for the directories with all your atomic functions
(atomics
target in the example below).
Make sure you are generating atomic functions registry code by the option
--registry_c ./atomics_reg.c
(atomics
target).
3. Make sure you are generating XML file for describing building block if you want to maintain graphical representation inside C-ATLAS.
(bblocks
target)
.. note:: C-ATLAS web app uses this makefile target for fetching building blocks, so keep the naming.
Makefile section below is stored at the root of UAS-CatPilot repository.
atomics:
@echo Generating atomic functions
@./catpilot/c-atom/tools/fspecgen.py --catom_path catpilot/c-atom --code --cmake --registry_c ./atomics_reg.c --atomics_dirs project:./atomics catpilot:catpilot/atomics catom:catpilot/c-atom/atomics
xmlinline:
@echo Inlining XML configs
@./catpilot/c-atom/tools/xml2c_inliner.py --cfg_path config/quad/ --out xml_inline_cfgs.c
bblocks:
@./catpilot/c-atom/tools/fspecgen.py --code --cmake --bbxml bblocks.xml --atomics_dirs project:./atomics catpilot:catpilot/atomics catom:catpilot/c-atom/atomics
Target xmlinline
is used for inlining XML into Read-Only microcontroller.
This tool speeds up putting of the configuration XMLs into microcontrollers without IP network stack.