/* * 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.15.4.4 $ */ #define S_FUNCTION_NAME sfun_directlook #define S_FUNCTION_LEVEL 2 #include #include "simstruc.h" #include /* 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 the Real-Time Workshop. However, * this S-function does work with the 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, etc.). */ 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 * i.e., 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 the 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 /toolbox/simulink/blocks/tlc_c/. */ static void mdlRTW(SimStruct *S) { /* * Write out the spacing setting as a param setting, i.e., 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 */