Matlab Kernel Tunable Parameters
This document is a walk-through on accessing Matlab generated code from C++, without hard-coding structure header definitions.
Matlab CAPI
CAPI Data Structures
rtwCAPI_Signals rtBlockSignals
Not used in this example.
rtwCAPI_BlockParameters rtBlockParameters
Not used in this example.
rtwCAPI_ModelParameters rtModelParameters
This data structure stores the parameter data from the workspace. Field definitions are:
- addrMapIndex - The index of the instance of the variable in the rtDataAddrMap array.
- varName - The variable name as a string. This is the primary key used in searching this array.
- dataTypeIndex - The index of the data type definition in the rtDataTypeMap array.
- dimIndex - The index of the dimension definition in the rtDimensionMap array.
- fixPtIndex - The index of the fixed point definition in the rtFixPtMap array. (Not used here.)
Data Instance Address Map (rtDataAddrMap)
This is an array of void pointers to the static instances of the data. The
addrMapIndex in other structures are the index to this array.
Dimension Map (rtVarDimsAddrMap)
This table contains matrix dimension information. (Seems to be empty in the phase 5x kernel?)
Data Type Map (rtDataTypeMap)
The is where things get interesting. This table contains the following fields:
- cName - A string of the type for POD, or "struct" for structure types
- mwName - A string of the type (not necessarily matching the cName for POD) or the typedef name (e.g "struct_XXXaaa03A").
- numElements - The number of subfields in the type.
- elemMapIndex - The starting index into the elementMap table. Entries extend for numElements.
- dataSize - The size of the entire type.
- slDataId - An enum indicating the type. e.g: SS_STRUCT, SS_UINT16, SS_DOUBLE, etc.
- isComplex - I assume this means complex in the mathematical sense. Seems to be zero for all entries in the phase 5x kernel.
- isPointer - I assume this would indicate a reference type.
Element Map (rtElementMap)
This table contains entries whick list the offset, type and dimension of data. The fields are:
- elementName - A string of the element name as it appears in its enclosing structure.
- elementOffset - The byte offset into the structure containing the field.
- dataTypeIndex - An index into the rtDataTypeMap
- dimIndex - An index into the rtVarSimsAddrMap
- fxpIndex - An index into the fixed point definition table.
Example
If the kernel tunable parameter interface contained the following:
Az.RL.PID.Ki=0.5
The processing would follow like so:
- First we take the first part ('Az') as the instance name, and look into the rtModelParameters table, matching "Az" against the varName field.
This should give us an entry like this:
{ 1054, "Az", 21, 0, 0 },
Which indicates the variable exists, and that we should look at rtDataAddrMap[1054] for the address of "Az", providing the base address of Az. We record the base address for later use as 'ptr'.
ptr = rtDataAddrMap[1054];
The rtModelParamters entry for "Az" also referred us to rtDataTypeMap[21]. This entry looks like this:
{ "struct", "struct_9sIqOXMcy1KGEplO92tlLG", 4, 66, sizeof(struct_9sIqOXMcy1KGEplO92tlLG), SS_STRUCT, 0, 0 }
or alternatively represented to include the field semantics:
{
cName : "struct",
mwName : "struct_9sIqOXMcy1KGEplO92tlLG",
numElements : 4,
elemMapIndex : 66,
dataSize : sizeof(struct_9sIqOXMcy1KGEplO92tlLG),
slDataId : SS_STRUCT,
isComplex : 0,
isPointer : 0
}
This entry tells us this is a structure type, and that there are four subfields beginning at index 66 of the rtElementMap. Those entries are:
{ "RL", rt_offsetof(struct_9sIqOXMcy1KGEplO92tlLG, RL), 9, 11, 0 },
{ "PL", rt_offsetof(struct_9sIqOXMcy1KGEplO92tlLG, PL), 12, 11, 0 },
{ "Params", rt_offsetof(struct_9sIqOXMcy1KGEplO92tlLG, Params), 13, 11, 0 },
{ "LC", rt_offsetof(struct_9sIqOXMcy1KGEplO92tlLG, LC), 20, 11, 0 }
or using the alternate notation:
{
elementName : "RL",
elementOffset : rt_offsetof(struct_9sIqOXMcy1KGEplO92tlLG, RL),
dataTypeIndex : 9,
dimIndex : 11,
fxpIndex : 0
}
{ (entries for PL, Params, and LC not shown) }
Since we know this is a structure type, we examine the next field from the config file:
Az.
RL.PID.Ki=0.5
We then examine for four element entries for the next field. Here we see the first entry matches 'RL'. We take the value of the elementOffset for the RL field and add it to the address of Az we found earlier.
ptr += rtElementMap[66].elementOffset;
The "RL" entry also indicates that 'RL' is a type described by rtDataTypeMap[9], which is:
{ "struct", "struct_vefo2I5CE6Gd9DFBCsPgkF", 1, 18, sizeof(struct_vefo2I5CE6Gd9DFBCsPgkF), SS_STRUCT, 0, 0 },
We jump back to the rtElementMap at index 18, which is:
{ "PID", rt_offsetof(struct_vefo2I5CE6Gd9DFBCsPgkF, PID), 8, 11, 0 },
There is only one valid entry, so we confirm the next field (i.e Az.RL.
PID.Ki=0.5) is 'PID'. It is so we take the elementOffset and add it to the address of Az again.
ptr += rtElementMap[18].elementOffset;
Again we the "PID" entry also indicates that 'PID' is a type described by rtDataTypeMap[8], which is:
{ "struct", "struct_c7quVdAsbx7nCPK9EaAsrB", 6, 12, sizeof(struct_c7quVdAsbx7nCPK9EaAsrB), SS_STRUCT, 0, 0 },
Looking at the 6 entries starting at rtElementMap[12] we see:
{ "P", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, P), 0, 11, 0 },
{ "P2", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, P2), 0, 11, 0 },
{ "Ki", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, Ki), 0, 11, 0 },
{ "AWF", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, AWF), 0, 11, 0 },
{ "Kd", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, Kd), 0, 11, 0 },
{ "Kdd", rt_offsetof(struct_c7quVdAsbx7nCPK9EaAsrB, Kdd), 0, 11, 0 },
Again we examine the next field (Az.RL.PID.
Ki=0.5) and match for the Ki field. Which gives us another offset which we add to the address of 'Az'.
ptr += rtElementMap[14].elementOffset;
We then look at the rtDataTypeMap[0] which is:
{ "double", "real_T", 0, 0, sizeof(real_T), SS_DOUBLE, 0, 0 }
Finally a type we know! The pointer 'ptr' now points to the actual address of the Az.RL.PID.Ki field.
A Second Example With Array Indices
Adding now the capability to set arrayed POD fields. When discussing array indicies, we always need to cite what 'base' or number we consider to be the first element. FORTRAN and Matlab both use '1' based indexes, C/C++ on the other hand is zero-based. To ease the confusion we will use the Matlab convensions of '1' based indexing and round brackets rather than square brackets.
So to set the first element in a variable we would use
as opposed to
So given input like the following to index into a 1-D 2-element array:
Az.PL.XPFilt.xinit(2) = 10.0
or to access the last column in the 2nd row of a 2-D 4 by 4 matrix:
AZ.my4x4Matrix(2,4) = 1.0
We would proceed as in our first example, but we have some extra steps to handle the indexing. In the example above we ignored the dimIndex of the rtElementMap entry, but here we must examine it. The rtElementMap entry for xinit looks like so:
{
elementName : "xinit",
elementOffset : rt_offsetof(struct_IffUhjLw1aTgkDHpbE0ufD, xinit),
dataTypeIndex : 0,
dimIndex : 12,
fxpIndex : 0
}
Inspecting the twelfth rtDimensionMap entry shows:
{
dataOrientation : rtwCAPI_MATRIX_COL_MAJOR,
dimArrayIndex : 22,
numDims : 2,
vardimsIndex : 0
}
The referenced rtDimensionArray index 22 and 23 are:
2, /* 22 */
1, /* 23 */
So apparently xinit is a 2 row x 1 column matrix.
Here we can see one of the odd-ball things about Matlab-rtw: numDims for scalars or single dimension arrays is '2'. Why this is done is beyond me, but for our purposes it doesn't hurt, because one of the dimensions is '1'. For column major addressing, the conversion algorithm is
offset_base_variable = ((row_index-1) + (column_index-1) * num_rows) * sizeof(type)
--
JoeBrandt - 2012-02-24