Module Header File

Basic C Module Setup

This page describes how to create the C module functions. Because C is not an object oriented language it is necessary to recreate some concepts from C++ such as data encapsulation. Thus, while a C++ module has its variables defined within the class, with a C module we need to create a module configuration structure which contains all the module variables. The module config structure name should be the module name ending with ....Config.

When a C module is setup in python, the module data is created first by making an instance of the module configuration data structure, and then wrapping this with module specific code to make a functioning Basilisk module. The module wrapped is a sub-class of sys_model and thus has the usual ModelTag and moduleID variables, as well as the ability to respond to Reset() and UpdateState() requests.

Sample Code

Let us assume the module is to be called SomeModule. The input and output message type is SomeMsg. The following example is analogous to how the sample C++ module is setup.

 1#ifndef SOME_MODULE_H
 2#define SOME_MODULE_H
 3
 4#include <stdint.h>
 5#include "architecture/utilities/bskLogging.h"
 6#include "cMsgCInterface/SomeMsg_C.h"
 7
 8/*! @brief Top level structure for the sub-module routines. */
 9typedef struct {
10    /* declare module variables */
11    double dummy;                                   //!< [units] sample module variable declaration
12
13    /* declare module IO interfaces */
14    SomeMsg_C dataOutMsg;              //!< sample output message
15    SomeMsg_C dataInMsg;               //!< sample input message
16
17    BSKLogger *bskLogger;              //!< BSK Logging
18}someModuleConfig;
19
20#ifdef __cplusplus
21extern "C" {
22#endif
23
24    void SelfInit_someModule(someModuleConfig *configData, int64_t moduleID);
25    void Update_someModule(someModuleConfig *configData, uint64_t callTime, int64_t moduleID);
26    void Reset_someModule(someModuleConfig *configData, uint64_t callTime, int64_t moduleID);
27
28#ifdef __cplusplus
29}
30#endif

Let’s break down the above sample code. The #include statements import all required support files, including bskLogging.h to log module status messages, as well as all input and output message definitions. Note that while C++ uses template classes to take a message payload definition and create a message object, in C code the message object is create by wrapping the corresponding message behavior code about the message payload structure definition. This step is done automatically when using python3 conanfile.py and the result is stored in basilisk/dist3/autoSource/cMsgCInterface/. The path to autoSource is automatically included in the cmake file, thus the shown shorter include can be used.

The C message wrapper functions, for example for SomeMsg type, are stored in the SomeMsg_C.h file. Importing the ..._C.h file will automatically also include the corresponding SomeMsgPayload.h file. Thus, this single include provides access essentially to the C message object methods as well as the message data definition.

Next the module configure structure is defined. In the above sample code this is the someModuleConfig definition. Think of this module configuration file as the equivalent of class variables with a C++ module. However, as this is a simple C structure, all config variables are public variables. There is no concept of private module variables with C modules.

Finally, the required module functions must be defined. You can define additional support functions. Note that this code must be wrapped so the C code will properly compile within this C++ project.

Required Module Methods

Each C module should define SelfInit, Reset and Update functions. To tie these functions to this module, the function name is expanded with the module name as shown above. Note that C++ modules do not require the SelfInit function as the message objects are automatically connected to their data copy in their constructor. In C we have to do this with an extra step.

Note that each of these required module functions receives a pointer to the module configuration data structure. This structure is typically called configData in each C module.

Module Variables

The module configuration structure defines all module variables. This includes variables and message buffers needed to implement the module, the BSKLogger variable for module status logging, as well as all input and output messages.

Regarding C message objects, the sample SomeMsg_C` type can acts as either an input or output message.  Thus, in the above example the input and output messages are both of type ``SomeMsg_C.

In contrasts to C++ modules were some class variables are public and some private, in the C module all module variables are public.

Array of Messages

If the C module can receive a variable number of input messages, this is done by creating an array of of the desired message type. As C modules can also be used in flight code, dynamic memory allocation should be avoided. Thus, create the array with a size that is large enough to handle all cases.

For example, assume the module needs an array of input messages of type SomeMsg. In the module configuration structure define:

SomeMsg_C moreInMsgs[10]

The module needs to implement separate logic to determine how many messages have been set. For example, the reset function could loop over this array and up to what slot the associate message object has been linked.

As the C wrapped message object can act as either input or output messages, the above example can readily be converted to an outpout message example by renaming the array variable moreOutMsgs.