0
votes

I know that it is possible to write Level-2 MATLAB S-Functionswith variable-sized signals. Is it also somehow possible to do that in C MEX S-Functions?

My data has a different size at each time step. This requirement originates from a compressing block which gets a fixed size signal (2D) as its input. However the output signal (1D / Vector) changes its size at every mdlOutput().

1
In general, what can do done with a C-mex S-Function is a super set of what can be done with S-Functions written in other languages (including m-code). So yes, it is possible. @Daniel's link is a good place to start.Phil Goddard
Thanks a lot. The starting point was great!Robert Kalinke

1 Answers

2
votes

The comments of the question already answered it: Yes it is possible!

Here my example:

// Required S-Function header
#define S_FUNCTION_LEVEL 2
#define S_FUNCTION_NAME  sfunc_varsignal

#include "simstruc.h"

enum {INPUT_PORT = 0, NUM_INPUT_PORTS};
enum {OUTPUT_PORT = 0, NUM_OUPUT_PORTS};      

/**
 * "Specify the number of inputs, outputs, states, parameters, and other
 * characteristics of the C MEX S-function"
 */
static void mdlInitializeSizes(SimStruct* S)
{
    boolean_T boolret;
    int_T intret;

    // Parameter
    ssSetNumSFcnParams(S, 0);
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S))
    {
        return; // Parameter mismatch will be reported by Simulink
    }

    // Input port
    boolret = ssSetNumInputPorts(S, NUM_INPUT_PORTS);
    if (boolret == 0)
    {
        return;
    }    
    ssSetInputPortDirectFeedThrough(S, INPUT_PORT, 1);    
    intret = ssSetInputPortDimensionInfo(S, INPUT_PORT, DYNAMIC_DIMENSION);
    if (intret == 0)
    {
        ssWarning(S, "Input dimensions could not be set.");
    }
    _ssSetInputPortNumDimensions(S, INPUT_PORT, 1);    

    // Output port
    boolret = ssSetNumOutputPorts(S, NUM_OUPUT_PORTS);
    if (boolret == 0)
    {
        return;
    }

    intret = ssSetOutputPortDimensionInfo(S, OUTPUT_PORT, DYNAMIC_DIMENSION);

    if (intret == 0)
    {
        ssWarning(S, "Output dimensions could not be set.");
    }
    _ssSetOutputPortNumDimensions(S, OUTPUT_PORT, 1);


    // Sample Times
    ssSetNumSampleTimes(S, 1);

    // Dimension Modes of the Ports
    ssSetInputPortDimensionsMode(S, INPUT_PORT, INHERIT_DIMS_MODE);
    ssSetOutputPortDimensionsMode(S, OUTPUT_PORT, INHERIT_DIMS_MODE);
    // This is required for any kind of variable size signal:
    ssSetInputPortRequiredContiguous(S, INPUT_PORT, true);

    ssSetOptions(S, 0);

    // Note: In the doc of ssSetOutputPortWidth it is wriiten:
    // "If the width is dynamically sized, the S-function must provide
    // mdlSetOutputPortDimensionInfo and mdlSetDefaultPortDimensionInfo
    // methods to enable the signal dimensions to be set correctly
    // during signal propagation."
    // However in the example sfun_varsize_concat1D this methods are
    // not present. The function _ssSetOutputPortNumDimensions() may be sufficient
    // This usgae of this function is copied from the example sfcndemo_varsize    
}

#if defined(MATLAB_MEX_FILE)    
/**
 * "Set the width of an input port that accepts 1-D (vector) signals"
 */
#define MDL_SET_INPUT_PORT_WIDTH
static void mdlSetInputPortWidth(SimStruct* S, int_T port, int_T width)
{
    // Set to the sugessted width (e.g. the output width
    // from the connected block)
    ssSetInputPortWidth(S, port, width);

    // Check if the setting was sucessful
    if (ssGetInputPortWidth(S, INPUT_PORT) != DYNAMICALLY_SIZED)
    {
        ssSetOutputPortWidth(S, OUTPUT_PORT, width);
    }
    return;
}    

/**
 * "Set the width of an output port that outputs 1-D (vector) signals"
 */
#define MDL_SET_OUTPUT_PORT_WIDTH
static void mdlSetOutputPortWidth(SimStruct* S, int_T port, int_T width)
{
    // Nothing here, but its required since the output port is set as
    // dynamically sized. But its size is set in mdlSetInputPortWidth()
    UNUSED_ARG(S);
    UNUSED_ARG(port);
    UNUSED_ARG(width);
    return;
}    
#endif //defined(MATLAB_MEX_FILE)

/**
 * "Specify the sample rates at which this C MEX S-function operates"
 */
static void mdlInitializeSampleTimes(SimStruct* S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
    ssSetModelReferenceSampleTimeDefaultInheritance(S);
}

/**
 * "Compute the signals that this block emits."
 */
static void mdlOutputs(SimStruct* S, int_T tid)
{
    UNUSED_ARG(tid);

    const real_T* insignal = ssGetInputPortRealSignal(S, INPUT_PORT);
    auto width = static_cast<const int>(insignal[0]);
    // This function does the trick:
    ssSetCurrentOutputPortDimensions(S, OUTPUT_PORT, 0, width);
    // newWidth should be width
    int_T newWidth = ssGetCurrentOutputPortDimensions(S, OUTPUT_PORT, 0 /* dimension ID */);

    real_T* outsignal = ssGetOutputPortRealSignal(S, OUTPUT_PORT);

    for (int i = 0; i < newWidth; i++)
    {
         *outsignal++ = i+1;
    }
}

/**
 * "Perform any actions required at termination of the simulation"
 */
static void mdlTerminate(SimStruct* S)
{
    UNUSED_ARG(S);
}

// Required S-function trailer    
#ifdef  MATLAB_MEX_FILE    /* Is this file being compiled as a MEX-file? */
#include "simulink.c"      /* MEX-file interface mechanism */
#else
#include "cg_sfun.h"       /* Code generation registration function */
#endif