Products & Services Solutions Academia Support User Community Company

Learn more about Real-Time Workshop   

Writing Fully Inlined S-Functions with the mdlRTW Routine

About S-Functions and mdlRTW

You can inline more complex S-functions that use the S-function mdlRTW routine. The purpose of the mdlRTW routine is to provide the code generation process with more information about how the S-function is to be inlined, by creating a parameter record of a nontunable parameter for use with a TLC file. The mdlRTW routine does this by placing information in the model.rtw file. The mdlRTW function is described in the text file matlabroot/simulink/src/sfuntmpl_doc.c.

As an example of how to use the mdlRTW function, this section discusses the steps you must take to create a direct-index lookup S-function. Lookup tables are collections of ordered data points of a function. Typically, these tables use some interpolation scheme to approximate values of the associated function between known data points. To incorporate the example lookup table algorithm into a Simulink model, the first step is to write an S-function that executes the algorithm in mdlOutputs. To produce the most efficient code, the next step is to create a corresponding TLC file to eliminate computational overhead and improve the performance of the lookup computations.

For your convenience, the Simulink product provides support for two general-purpose lookup 1-D and 2-D algorithms. You can use these algorithms as they are or create a custom lookup table S-function to fit your requirements. This section demonstrates how to create a 1-D lookup S-function, sfun_directlook.c, and its corresponding inlined sfun_directlook.tlc file. (See the Target Language Compiler documentation for more details on the Target Language Compiler.) This 1-D direct-index lookup table example demonstrates the following concepts that you need to know to create your own custom lookup tables:

S-Function RTWdata

There is a property of blocks called RTWdata, which can be used by the Target Language Compiler when inlining an S-function. RTWdata is a structure of strings that you can attach to a block. It is saved with the model and placed in the model.rtw file when generating code. For example, this set of MATLAB commands,

mydata.field1 = 'information for field1';
mydata.field2 = 'information for field2';
set_param(gcb,'RTWdata',mydata)
get_param(gcb,'RTWdata')

produces this result:

ans = 
 
    field1: 'information for field1'
    field2: 'information for field2'

Inside the model.rtw file for the associated S-Function block is this information.

Block {
  Type                    "S-Function"
  RTWdata {
    field1                  "information for field1"
    field2                  "information for field2"
  }

The Direct-Index Lookup Table Algorithm

The 1-D lookup table block provided in the Simulink library uses interpolation or extrapolation when computing outputs. This extra accuracy is not needed in all situations. In this example, you create a lookup table that directly indexes the output vector (y-data vector) based on the current input (x-data) point.

This direct 1-D lookup example computes an approximate solution p(x) to a partially known function f(x) at x=x0, given data point pairs (x,y) in the form of an x-data vector and a y-data vector. For a given data pair (for example, the i'th pair), y_i = f(x_i). It is assumed that the x-data values are monotonically increasing. If x0 is outside the range of the x-data vector, the first or last point is returned.

The parameters to the S-function are

XData, YData, XEvenlySpaced

XData and YData are double vectors of equal length representing the values of the unknown function. XDataEvenlySpaced is a scalar, 0.0 for false and 1.0 for true. If the XData vector is evenly spaced, XDataEvenlySpaced is 1.0 and more efficient code is generated.

The following graph shows how the parameters XData=[1:6]and YData=[1,2,7,4,5,9] are handled. For example, if the input (x-value) to the S-Function block is 3, the output (y-value) is 7.

The Direct-Index Lookup Table Example

This section shows how to improve the lookup table by inlining a direct-index S-function with a TLC file. This direct-index lookup table S-function does not require a TLC file. Here the example uses a TLC file for the direct-index lookup table S-function to reduce the code size and increase efficiency of the generated code.

Implementation of the direct-index algorithm with inlined TLC file requires the S-function main module, matlabroot/simulink/src/sfun_directlook.c, and a corresponding matlabroot/simulink/src/lookup_index.c module. The lookup_index.c module contains the GetDirectLookupIndex function that is used to locate the index in the XData for the current x input value when the XData is unevenly spaced. The GetDirectLookupIndex routine is called from both the S-function and the generated code. Here the example uses the wrapper concept for sharing C/C++ code between Simulink MEX-files and the generated code.

If the XData is evenly spaced, then both the S-function main module and the generated code contain the lookup algorithm (not a call to the algorithm) to compute the y-value of a given x-value, because the algorithm is short. This demonstrates the use of a fully inlined S-function for generating optimal code.

The inlined TLC file, which either performs a wrapper call or embeds the optimal C/C++ code, is matlabroot/toolbox/simulink/blocks/tlc_c/sfun_directlook.tlc (see the example in mdlRTW Usage).

Error Handling

In this example, the mdlCheckParameters routine verifies that

The mdlInitializeSizes function explicitly calls mdlCheckParameters after it verifies that the number of parameters passed to the S-function is correct. After the Simulink engine calls mdlInitializeSizes, it then calls mdlCheckParameters whenever you change the parameters or there is a need to reevaluate them.

User Data Caching

The mdlStart routine shows how to cache information that does not change during the simulation (or while the generated code is executing). The example caches the value of the XDataEvenlySpaced parameter in UserData, a field of the SimStruct. The following line in mdlInitializeSizes tells the Simulink engine to disallow changes to XDataEvenlySpaced.

ssSetSFcnParamTunable(S, iParam, SS_PRM_NOT_TUNABLE);

During execution, mdlOutputs accesses the value of XDataEvenlySpaced from UserData rather than calling the mxGetPr MATLAB API function. This increases performance.

mdlRTW Usage

The Real-Time Workshop code generator calls the mdlRTW routine while generating the model.rtw file. To produce optimal code for your Simulink model, you can add information to the model.rtw file about the mode in which your S-Function block is operating.

The following example adds parameter settings to the model.rtw file. The parameter settings do not change during execution. In this case, the XDataEvenlySpaced S-function parameter cannot change during execution (ssSetSFcnParamTunable was specified as false (0) for it in mdlInitializeSizes). The example writes it out as a parameter setting (XSpacing) using the function ssWriteRTWParamSettings.

Because xData and yData are registered as run-time parameters in mdlSetWorkWidths, the code generator handles writing to the model.rtw file automatically.

Before examining the S-function and the inlined TLC file, consider the generated code for the following model.

The model uses evenly spaced XData in the top S-Function block and unevenly spaced XData in the bottom S-Function block. When creating this model, you need to specify the following for each S-Function block.

set_param(`sfun_directlook_ex/S-Function','SFunctionModules','lookup_index')
set_param(`sfun_directlook_ex/S-Function1','SFunctionModules','lookup_index')

This informs the Real-Time Workshop build process that the module lookup_index.c is needed when creating the executable.

When generating code for this model, the Real-Time Workshop software uses the S-function's mdlRTW method to generate a model.rtw file with the value EvenlySpaced for the XSpacing parameter for the top S-Function block, and the value UnEvenlySpaced for the XSpacing parameter for the bottom S-Function block. The TLC-file uses the value of XSpacing to determine what algorithm to include in the generated code. The generated code contains the lookup algorithm when the XData is evenly spaced, but calls the GetDirectLookupIndex routine when the XData is unevenly spaced The generated model.c or model.cpp code for the lookup table example model is

/*
 * sfun_directlook_ex.c
 * 
 * Real-Time Workshop code generation for Simulink model 
 * "sfun_directlook_ex.mdl".
 *
 * Model Version              : 1.2
 * Real-Time Workshop version : 6.0  (R14)  06-Apr-2004
 * C source code generated on : Fri Apr 09 09:15:12 2004
 */

#include "sfun_directlook_ex.h"
#include "sfun_directlook_ex_private.h"

/* External output (root outports fed by signals with auto storage) */
ExternalOutputs_sfun_directlook_ex sfun_directlook_ex_Y;

/* Real-time model */
rtModel_sfun_directlook_ex sfun_directlook_ex_M_;
rtModel_sfun_directlook_ex *sfun_directlook_ex_M = &sfun_directlook_ex_M_;

/* Model output function */
static void sfun_directlook_ex_output(int_T tid)
{

  /* local block i/o variables */

  real_T rtb_SFunction_h;
  real_T rtb_temp1;

  /* Sin: '<Root>/Sine Wave' */
  rtb_temp1 = sfun_directlook_ex_P.SineWave_Amp *
    sin(sfun_directlook_ex_P.SineWave_Freq * sfun_directlook_ex_M->Timing.t[0] +
    sfun_directlook_ex_P.SineWave_Phase) + sfun_directlook_ex_P.SineWave_Bias;

  /* Code that is inlined for the top S-function block in the 
   * sfun_directlook_ex model
   */
  /* S-Function Block: <Root>/S-Function */
  {
    const real_T *xData = &sfun_directlook_ex_P.SFunction_XData[0];
    const real_T *yData = &sfun_directlook_ex_P.SFunction_YData[0];
    real_T spacing = xData[1] - xData[0];

    if ( rtb_temp1 <= xData[0] ) {
      rtb_SFunction_h = yData[0];
    } else if ( rtb_temp1 >= yData[20] ) {
      rtb_SFunction_h = yData[20];
    } else {
      int_T idx = (int_T)( ( rtb_temp1 - xData[0] ) / spacing );
      rtb_SFunction_h = yData[idx];
    }
  }

  /* Outport: '<Root>/Out1' */
  sfun_directlook_ex_Y.Out1 = rtb_SFunction_h;

/* Code that is inlined for the bottom S-function block in the 
   * sfun_directlook_ex model
   */
  /* S-Function Block: <Root>/S-Function1 */
  {
    const real_T *xData = &sfun_directlook_ex_P.SFunction1_XData[0];
    const real_T *yData = &sfun_directlook_ex_P.SFunction1_YData[0];
    int_T idx;

    idx = GetDirectLookupIndex(xData, 5, rtb_temp1);
    rtb_temp1 = yData[idx];
  }

  /* Outport: '<Root>/Out2' */
  sfun_directlook_ex_Y.Out2 = rtb_temp1;
}

/* Model update function */
static void sfun_directlook_ex_update(int_T tid)
{

  /* Update absolute time for base rate */

  if(!(++sfun_directlook_ex_M->Timing.clockTick0))
  ++sfun_directlook_ex_M->Timing.clockTickH0;
  sfun_directlook_ex_M->Timing.t[0] = sfun_directlook_ex_M->Timing.clockTick0 *
    sfun_directlook_ex_M->Timing.stepSize0 +
    sfun_directlook_ex_M->Timing.clockTickH0 *
    sfun_directlook_ex_M->Timing.stepSize0 * 0x10000;

  {
    /* Update absolute timer for sample time: [0.1s, 0.0s] */

    if(!(++sfun_directlook_ex_M->Timing.clockTick1))
    ++sfun_directlook_ex_M->Timing.clockTickH1;
    sfun_directlook_ex_M->Timing.t[1] = sfun_directlook_ex_M->Timing.clockTick1
      * sfun_directlook_ex_M->Timing.stepSize1 +
      sfun_directlook_ex_M->Timing.clockTickH1 *
      sfun_directlook_ex_M->Timing.stepSize1 * 0x10000;
  }
}
...

matlabroot/simulink/src/sfun_directlook.c

/*
* File    :  sfun_directlook.c
 * Abstract:
 *
 *      Direct 1-D lookup. Here we are trying to compute an approximate
 *      solution, p(x) to an unknown function f(x) at x=x0, given data point 
 *      pairs (x,y) in the form of a x data vector and a y data vector. For a
 *      given data pair (say the i'th pair), we have y_i = f(x_i). It is 
 *      assumed that the x data values are monotonically increasing.  If the 
 *      x0 is outside of the range of the x data vector, then the first or 
 *      last point will be returned.
 *
 *      This function returns the "nearest" y0 point for a given x0. No
 *      interpolation is performed.
 *
 *      The S-function parameters are:
 *        XData              - double vector
 *        YData              - double vector
 *        XDataEvenlySpacing - double scalar 0 (false) or 1 (true)
 *        The third parameter cannot be changed during simulation.
 *
 *      To build:
 *        mex sfun_directlook.c lookup_index.c
 *
 * Copyright 1990-2004 The MathWorks, Inc.
 * $Revision: 1.1.4.6 $
 */

#define S_FUNCTION_NAME  sfun_directlook
#define S_FUNCTION_LEVEL 2


#include <math.h>
#include "simstruc.h"
#include <float.h>

/* use utility function IsRealVect() */
#if defined(MATLAB_MEX_FILE)
#include "sfun_slutils.h"
#endif

/*================*
 * Build checking *
 *================*/
#if !defined(MATLAB_MEX_FILE)
/*
 * This file cannot be used directly with Real-Time Workshop. However,
 * this S-function does work with Real-Time Workshop via
 * the Target Language Compiler technology. See 
 * matlabroot/toolbox/simulink/blocks/tlc_c/sfun_directlook.tlc   
 * for the C version
 */
# error This_file_can_be_used_only_during_simulation_inside_Simulink
#endif


/*=========*
 * Defines *
 *=========*/

#define XVECT_PIDX             0
#define YVECT_PIDX             1
#define XDATAEVENLYSPACED_PIDX 2
#define NUM_PARAMS             3

#define XVECT(S)             ssGetSFcnParam(S,XVECT_PIDX)
#define YVECT(S)             ssGetSFcnParam(S,YVECT_PIDX)
#define XDATAEVENLYSPACED(S) ssGetSFcnParam(S,XDATAEVENLYSPACED_PIDX)


/*==============*
 * misc defines *
 *==============*/
#if !defined(TRUE)
#define TRUE  1
#endif
#if !defined(FALSE)
#define FALSE 0
#endif

/*===========*
 * typedef's *
 *===========*/

typedef struct SFcnCache_tag {
    boolean_T evenlySpaced;
} SFcnCache;

/*===================================================================*
 * Prototype define for the function in separate file lookup_index.c *
 *===================================================================*/
extern int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u);


/*====================*
 * S-function methods *
 *====================*/


#define MDL_CHECK_PARAMETERS           /* Change to #undef to remove function */
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters ================================================
 * Abstract:
 *    This routine will be called after mdlInitializeSizes, whenever
 *    parameters change or get re-evaluated. The purpose of this routine is
 *    to verify that the new parameter setting are correct.
 *
 *    You should add a call to this routine from mdlInitalizeSizes
 *    to check the parameters. After setting your sizes elements, you should:
 *       if (ssGetSFcnParamsCount(S) == ssGetNumSFcnParams(S)) {
 *           mdlCheckParameters(S);
 *       }
 */
static void mdlCheckParameters(SimStruct *S)
{

    if (!IsRealVect(XVECT(S))) {
        ssSetErrorStatus(S,"1st, X-vector parameter must be a real finite "
                         " vector");
        return;
    }

    if (!IsRealVect(YVECT(S))) {
        ssSetErrorStatus(S,"2nd, Y-vector parameter must be a real finite "
                         "vector");
        return;
    }

    /*
     * Verify that the dimensions of X and Y are the same.
     */
    if (mxGetNumberOfElements(XVECT(S)) != mxGetNumberOfElements(YVECT(S)) ||
        mxGetNumberOfElements(XVECT(S)) == 1) {
        ssSetErrorStatus(S,"X and Y-vectors must be of the same dimension "
                         "and have at least two elements");
        return;
    }

    /*
     * Verify we have a valid XDataEvenlySpaced parameter.
     */
    if ((!mxIsNumeric(XDATAEVENLYSPACED(S)) && 
          !mxIsLogical(XDATAEVENLYSPACED(S))) ||
        mxIsComplex(XDATAEVENLYSPACED(S)) ||
        mxGetNumberOfElements(XDATAEVENLYSPACED(S)) != 1) {
        ssSetErrorStatus(S,"3rd, X-evenly-spaced parameter must be logical 
scalar");
        return;
    }

    /*
     * Verify x-data is correctly spaced.
     */
    {
        int_T     i;
        boolean_T spacingEqual;
        real_T    *xData = mxGetPr(XVECT(S));
        int_T     numEl  = mxGetNumberOfElements(XVECT(S));

        /*
         * spacingEqual is TRUE if user  XDataEvenlySpaced 
         */
        spacingEqual = (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0);

        if (spacingEqual) {    /* XData is 'evenly-spaced' */
            boolean_T badSpacing = FALSE;
            real_T    spacing    = xData[1] - xData[0];
            real_T    space;

            if (spacing <= 0.0) {
                badSpacing = TRUE;
            } else {
                real_T eps = DBL_EPSILON;

                for (i = 2; i < numEl; i++) {
                    space = xData[i] - xData[i-1];
                    if (space <= 0.0 || 
                        fabs(space-spacing) >= 128.0*eps*spacing ){
                        badSpacing = TRUE;
                        break;
                    }
                }
            }

            if (badSpacing) {
                ssSetErrorStatus(S,"X-vector must be an evenly spaced "
                                 "strictly monotonically increasing vector");
                return;
            }
        } else {     /* XData is 'unevenly-spaced' */
            for (i = 1; i < numEl; i++) {
                if (xData[i] <= xData[i-1]) {
                    ssSetErrorStatus(S,"X-vector must be a strictly "
                                     "monotonically increasing vector");
                    return;
                }
            }
        }
    }
}
#endif /* MDL_CHECK_PARAMETERS */



/* Function: mdlInitializeSizes ================================================
 * Abstract:
 *    The sizes information is used by Simulink to determine the S-function
 *    block's characteristics (number of inputs, outputs, states, and so on).
 */
static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, NUM_PARAMS);  /* Number of expected parameters */

    /*
     * Check parameters passed in, providing the correct number was specified
     * in the S-function dialog box. If an incorrect number of parameters
     * was specified, Simulink will detect the error since ssGetNumSFcnParams
     * and ssGetSFcnParamsCount will differ.
     *   ssGetNumSFcnParams   - This sets the number of parameters your
     *                          S-function expects.
     *   ssGetSFcnParamsCount - This is the number of parameters entered by
     *                          the user in the Simulink S-function dialog box.
     */
#if defined(MATLAB_MEX_FILE)
    if (ssGetNumSFcnParams(S) == ssGetSFcnParamsCount(S)) {
        mdlCheckParameters(S);
        if (ssGetErrorStatus(S) != NULL) {
            return;
        }
    } else {
        return; /* Parameter mismatch will be reported by Simulink */
    }
#endif

    {
        int iParam = 0;
        int nParam = ssGetNumSFcnParams(S);

        for ( iParam = 0; iParam < nParam; iParam++ )
        {
            switch ( iParam )
            {
              case XDATAEVENLYSPACED_PIDX:
                
                ssSetSFcnParamTunable( S, iParam, SS_PRM_NOT_TUNABLE );
                break;

              default:
                ssSetSFcnParamTunable( S, iParam, SS_PRM_TUNABLE );
                break;
            }
        }
    }

    ssSetNumContStates(S, 0);
    ssSetNumDiscStates(S, 0);

    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
    ssSetInputPortDirectFeedThrough(S, 0, 1);

    ssSetInputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);
    ssSetInputPortOverWritable(S, 0, TRUE);

    if (!ssSetNumOutputPorts(S, 1)) return;
    ssSetOutputPortWidth(S, 0, DYNAMICALLY_SIZED);

    ssSetOutputPortOptimOpts(S, 0, SS_REUSABLE_AND_LOCAL);

    ssSetNumSampleTimes(S, 1);

    ssSetOptions(S,
                 SS_OPTION_WORKS_WITH_CODE_REUSE |
                 SS_OPTION_EXCEPTION_FREE_CODE |
                 SS_OPTION_USE_TLC_WITH_ACCELERATOR);

} /* mdlInitializeSizes */


/* Function: mdlInitializeSampleTimes ==========================================
 * Abstract:
 *    The lookup inherits its sample time from the driving block.
 */
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, INHERITED_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);
    ssSetModelReferenceSampleTimeDefaultInheritance(S);
} /* end mdlInitializeSampleTimes */

/* Function: mdlSetWorkWidths ===============================================
 * Abstract:
 *    Set up the [X,Y] data as run-time parameters
 *    that is, these values can be changed during execution.
 */
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{
    const char_T    *rtParamNames[] = {"XData","YData"};
    ssRegAllTunableParamsAsRunTimeParams(S, rtParamNames);
}


#define MDL_START                      /* Change to #undef to remove function */
#if defined(MDL_START)
/* Function: mdlStart ==========================================================
 * Abstract:
 *      Here we cache the state (true/false) of the XDATAEVENLYSPACED parameter.
 *      We do this primarily to illustrate how to "cache" parameter values (or
 *      information which is computed from parameter values) which do not change
 *      for the duration of the simulation (or in the generated code). In this
 *      case, rather than repeated calls to mxGetPr, we save the state once.
 *      This results in a slight increase in performance.
 */
static void mdlStart(SimStruct *S)
{
    SFcnCache *cache = malloc(sizeof(SFcnCache));

    if (cache == NULL) {
        ssSetErrorStatus(S,"memory allocation error");
        return;
    }

    ssSetUserData(S, cache);

    if (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0){
        cache->evenlySpaced = TRUE;
    }else{
        cache->evenlySpaced = FALSE;
    }

}
#endif /*  MDL_START */



/* Function: mdlOutputs ========================================================
 * Abstract:
 *    In this function, you compute the outputs of your S-function
 *    block. Generally outputs are placed in the output vector, ssGetY(S).
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{
    SFcnCache         *cache = ssGetUserData(S);
    real_T            *xData = mxGetPr(XVECT(S));
    real_T            *yData = mxGetPr(YVECT(S));
    InputRealPtrsType uPtrs  = ssGetInputPortRealSignalPtrs(S,0);
    real_T            *y     = ssGetOutputPortRealSignal(S,0);
    int_T             ny     = ssGetOutputPortWidth(S,0);
    int_T             xLen   = mxGetNumberOfElements(XVECT(S));
    int_T             i;

    /*
     * When the XData is evenly spaced, we use the direct lookup algorithm
     * to calculate the lookup
     */
    if (cache->evenlySpaced) {
        real_T spacing = xData[1] - xData[0];
        for (i = 0; i < ny; i++) {
            real_T u = *uPtrs[i];

            if (u <= xData[0]) {
                y[i] = yData[0];
            } else if (u >= xData[xLen-1]) {
                y[i] = yData[xLen-1];
            } else {
                int_T idx = (int_T)((u - xData[0])/spacing);
                y[i] = yData[idx];
            }
        }
    } else {
        /*
         * When the XData is unevenly spaced, we use a bisection search to
         * locate the lookup index.
         */
        for (i = 0; i < ny; i++) {
            int_T idx = GetDirectLookupIndex(xData,xLen,*uPtrs[i]);
            y[i] = yData[idx];
        }
    }

} /* end mdlOutputs */



/* Function: mdlTerminate ======================================================
 * Abstract:
 *    Free the cache which was allocated in mdlStart.
 */
static void mdlTerminate(SimStruct *S)
{
    SFcnCache *cache = ssGetUserData(S);
    if (cache != NULL) {
        free(cache);
    }
} /* end mdlTerminate */



#define MDL_RTW                        /* Change to #undef to remove function */
#if defined(MDL_RTW) && (defined(MATLAB_MEX_FILE) || defined(NRT))
/* Function: mdlRTW ============================================================
 * Abstract:
 *    This function is called when Real-Time Workshop is generating the
 *    model.rtw file. In this routine, you can call the following functions
 *    which add fields to the model.rtw file.
 *
 *    Important! Since this s-function has this mdlRTW method, it is required
 *    to have a correcponding .tlc file so as to work with RTW. You will find
 *    the sfun_directlook.tlc in <matlaroot>/toolbox/simulink/blocks/tlc_c/.
 */
static void mdlRTW(SimStruct *S)
{
    /*
     * Write out the spacing setting as a param setting, that is, this cannot be
     * changed during execution.
     */
    {
        boolean_T even = (mxGetScalar(XDATAEVENLYSPACED(S)) != 0.0);

        if (!ssWriteRTWParamSettings(S, 1,
                                     SSWRITE_VALUE_QSTR,
                                     "XSpacing",
                                     even ? "EvenlySpaced" : "UnEvenlySpaced")){
            return;/* An error occurred which will be reported by Simulink */
        }
    }
}
#endif /* MDL_RTW */


/*=============================*
 * 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


/* [EOF] sfun_directlook.c */

matlabroot/simulink/src/lookup_index.c

/* File    : lookup_index.c
* Abstract:
 *
 *     Contains a routine used by the S-function sfun_directlookup.c to
 *     compute the index in a vector for a given data value.
 * 
 *  Copyright 1990-2004 The MathWorks, Inc.
 *  $Revision: 1.1.4.6 $
 */
#include "tmwtypes.h"

/*
 * Function: GetDirectLookupIndex ==============================================
 * Abstract:
 *     Using a bisection search to locate the lookup index when the x-vector
 *     isn't evenly spaced.
 *
 *     Inputs:
 *        *x   : Pointer to table, x[0] ....x[xlen-1]
 *        xlen : Number of values in xtable
 *        u    : input value to look up
 *
 *     Output:
 *        idx  : the index into the table such that:
 *              if u is negative
 *                 x[idx] <= u < x[idx+1]
 *              else
 *                 x[idx] < u <= x[idx+1]
 */
int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u)
{
    int_T idx    = 0;
    int_T bottom = 0;
    int_T top    = xlen-1;
    
    /*
     * Deal with the extreme cases first:
     *
     *  i] u <= x[bottom] then idx = bottom
     * ii] u >= x[top] then idx = top-1
     *
     */
    if (u <= x[bottom]) {
        return(bottom);
    } else if (u >= x[top]) {
        return(top);
    }
    
    /*
     * We have: x[bottom] < u < x[top], onward
     * with search for the appropriate index ...
     */
    for (;;) {
        idx = (bottom + top)/2;
        if (u < x[idx]) {
            top = idx;
        } else if (u > x[idx+1]) {
            bottom = idx + 1;
        } else {
            /*
             * We have:  x[idx] <= u <= x[idx+1], only need
             * to do two more checks and we have the answer
             */
            if (u < 0) {
                /*
                 * We want right continuity, that is,
                 * if u == x[idx+1]
                 *    then x[idx+1] <= u < x[idx+2]
                 * else    x[idx  ] <= u < x[idx+1]
                 */
                return( (u == x[idx+1]) ? (idx+1) : idx);
            } else {
                /*
                 * We want left continuity, that is, 
                 * if u == x[idx]
                 *    then x[idx-1] < u <= x[idx  ]
                 * else    x[idx  ] < u <= x[idx+1]
                 */
                return( (u == x[idx]) ? (idx-1) : idx);
            }
        }
    }
} /* end GetDirectLookupIndex */

/* [EOF] lookup_index.c */

matlabroot/toolbox/simulink/blocks/tlc_c/sfun_directlook.tlc

%% File    : sfun_directlook.tlc
%% Abstract: 
%%      Level-2 S-function sfun_directlook block target file.
%%      It is using direct lookup algorithm without interpolation
%%
%% Copyright 1990-2004 The MathWorks, Inc.
%% $Revision: 1.1.4.6 $ 


%implements "sfun_directlook" "C"

%% Function: BlockTypeSetup ====================================================
%% Abstract:
%%     Place include and function prototype in the model's header file.
%%
%function BlockTypeSetup(block, system) void

  %% To add this external function's prototype in the header of the generated 
  %% file.
  %%
  %openfile buffer
  extern int_T GetDirectLookupIndex(const real_T *x, int_T xlen, real_T u);
  %closefile buffer
  
  %<LibCacheFunctionPrototype(buffer)>
   
%endfunction

%% Function: mdlOutputs ========================================================
%% Abstract:
%%      Direct 1-D lookup table S-function example. 
%%      Here we are trying to compute an approximate solution, p(x) to an 
%%      unknown function f(x) at x=x0, given data point pairs (x,y) in the 
%%      form of a x data vector and a y data vector. For a given data pair
%%      (say the i'th pair), we have y_i = f(x_i). It is assumed that the x 
%%      data values are monotonically increasing.  If the first or last x is 
%%      outside of the range of the x data vector, then the first or last 
%%      point will be returned.
%%
%%      This function returns the "nearest" y0 point for a given x0. 
%%      No interpolation is performed.
%%
%%      The S-function parameters are:
%%        XData
%%        YData
%%        XEvenlySpaced: 0 or 1
%%      The third parameter cannot be changed during execution and is
%%      written to the model.rtw file in XSpacing filed of the SFcnParamSettings
%%      record as "EvenlySpaced" or "UnEvenlySpaced". The first two parameters
%%      can change during execution and show up in the parameter vector.
%%
%function Outputs(block, system) Output
  /* %<Type> Block: %<Name> */  
  {
    %assign rollVars = ["U", "Y"]
    %%
    %% Load XData and YData as local variables
    %%
    const real_T *xData  = %<LibBlockParameterAddr(XData, "", "", 0)>;
    const real_T *yData  = %<LibBlockParameterAddr(YData, "", "", 0)>;
    %assign xDataLen = SIZE(XData.Value, 1)
    %%
    %% When the XData is evenly spaced, we use the direct lookup algorithm
    %% to locate the lookup index.
    %%
    %if SFcnParamSettings.XSpacing == "EvenlySpaced"
      real_T spacing = xData[1] - xData[0];

      %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
        %assign u = LibBlockInputSignal(0, "", lcv, idx)
        %assign y = LibBlockOutputSignal(0, "", lcv, idx)
        if ( %<u> <= xData[0] ) {
          %<y> = yData[0];
        } else if ( %<u> >= yData[%<xDataLen-1>] ) {
          %<y> = yData[%<xDataLen-1>];
        } else {
          int_T idx = (int_T)( ( %<u> - xData[0] ) / spacing );
          %<y> = yData[idx];
        }
        %%
        %% Generate an empty line if we are not rolling,
        %% so that it looks nice in the generated code.
        %%
        %if lcv == ""
          
        %endif
      %endroll
    %else
      %% When the XData is unevenly spaced, we use a bisection search to 
      %% locate the lookup index.
      int_T idx;

      %assign xDataAddr = LibBlockParameterAddr(XData, "", "", 0)
      %roll idx = RollRegions, lcv = RollThreshold, block, "Roller", rollVars
        %assign u = LibBlockInputSignal(0, "", lcv, idx)
        idx =  GetDirectLookupIndex(xData, %<xDataLen>, %<u>);
        %assign y = LibBlockOutputSignal(0, "", lcv, idx)
        %<y> = yData[idx];
        %%
        %% Generate an empty line if we are not rolling,
        %% so that it looks nice in the generated code.
        %%
        %if lcv == ""
          
        %endif
      %endroll
    %endif
  }
%endfunction
%% EOF: sfun_directlook.tlc
  


Related Products & Applications

Learn more about Simulink through this collection of videos, articles, technical literature and the Getting Started with Simulink Guide.

 © 1984-2009- The MathWorks, Inc.    -   Site Help   -   Patents   -   Trademarks   -   Privacy Policy   -   Preventing Piracy   -   RSS