S-function procedure calling order?

4 views (last 30 days)
Brian Tremaine
Brian Tremaine on 1 Dec 2020
Commented: Brian Tremaine on 2 Dec 2020
I have a relativley trivial S-function I can' get to work properly.It will be a larger function but for now is a saw-tooth that ramps from -1 to +1 with slope m then reverses and ramps from +1 to -1 with slope -m.
I am using x[0] as the instantaneous saw-tooth and am using x[1] as the slope.
It is not working as I expect.
In my mdlUpdate procedure I have this code:
#define MDL_UPDATE /* Change to #undef to remove function */
#if defined(MDL_UPDATE)
/* Function: mdlUpdate ==================================================
* Abstract:
* This function is called once for every major integration time step.
* Discrete states are typically updated here, but this function is
* useful for performing any tasks that should only take place once
* per integration step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *dx = ssGetdX(S);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
#define SLOPE (4.0*25.0)
real_T m = SLOPE;
// generate sawtooth ramp x[0], using slope x[1]
if (x[0] > 1.0) {
x[1]= -m;
}
if (x[0] < -1.0) {
x[1]= m;
}
}
#endif /* MDL_UPDATE */
In the mdlDerivatives I have this code:
#define MDL_DERIVATIVES /* Change to #undef to remove function */
#if defined(MDL_DERIVATIVES)
/* Function: mdlDerivatives =============================================
* Abstract:
* In this function, you compute the S-function block's derivatives.
* The derivatives are placed in the derivative vector, ssGetdX(S).
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
// generate sawtooth ramp x[0], using slope x[1]
dx[0] = x[1];
dx[1] = 0; // slope fixed
dx[2] = 0;
dx[3] = 0;
dx[4] = 0;
}
#endif /* MDL_DERIVATIVES */
The derivative of x[1] is zero because I want the slope to remain +m or -m until it gets changed in mdlUpdateIs
The mdlOutputs is this code:
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *y = ssGetOutputPortSignal(S,0);
real_T *x = ssGetContStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
real_T Uw = U(0);
real_T Vw = U(1);
real_T Ww = U(2);
// compare vector components to ramp (x[0]) to generate U, V & W
y[0] = x[0]; /* ramp, slope 4/Tfast */
y[1] = x[1]; /* slope, fixed value +m or -m */
y[2] = x[2]; /* U */
y[3] = x[3]; /* V */
y[4] = x[4]; /* W *
}
I'm currently not using y[2]--y[4]
The output y[0] currently ramps from initial value of 0 to 4 at which time the simulation ends. The displayed output y[1] is the slope m=100 and doesn't change. Is the calling sequence of mdlDerivatives and mdlUpdate correct for what I'm trying to do?
  1 Comment
Brian Tremaine
Brian Tremaine on 2 Dec 2020
I found a partial solution to my problem. I believe I cannot change a continuous state (my saw-tooth slope) so I added a single discrete state to hold the value of the slope. In mdlUpdate I modify the discrete state based on the value of the continuous state. My issue now is the time delay from reaching the saw-tooth peak to the time the saw-tooth switches is too long.
This will work for low frequency applications but I am trying to generate a 30kHz sawtooth to use in the simulation of a SVPWM module.Is there an alternative to making a fixed short step time?
#define S_FUNCTION_NAME svpwm
#define S_FUNCTION_LEVEL 2
#include <math.h>
#include <string.h>
#include <stdio.h>
#include "simstruc.h"
#include "matrix.h"
#define U(element) (*uPtrs[element]) /* Pointer to Input Port0 */
#define Vbus_PARAM(S) ssGetSFcnParam(S,0)
#define NUM_CSTATES 4
#define NUM_DSTATES 1
#define PI M_PI
#define SLOPE (10000.0)
/* Error handling
* --------------
*
* You should use the following technique to report errors encountered within
* an S-function:
*
* ssSetErrorStatus(S,"Error encountered due to ...");
* return;
*
* Note that the 2nd argument to ssSetErrorStatus must be persistent memory.
* It cannot be a local variable. For example the following will cause
* unpredictable errors:
*
* mdlOutputs()
* {
* char msg[256]; {ILLEGAL: to fix use "static char msg[256];"}
* sprintf(msg,"Error due to %s", string);
* ssSetErrorStatus(S,msg);
* return;
* }
*
*/
#define IS_PARAM_DOUBLE(pVal) (mxIsNumeric(pVal) && !mxIsLogical(pVal) &&\
!mxIsEmpty(pVal) && !mxIsSparse(pVal) && !mxIsComplex(pVal) && mxIsDouble(pVal))
#define OK_EMPTY_DOUBLE_PARAM(pVal) (mxIsNumeric(pVal) && !mxIsLogical(pVal) &&\
!mxIsSparse(pVal) && !mxIsComplex(pVal) && mxIsDouble(pVal))
/*====================*
* S-function methods *
*====================*/
#define MDL_CHECK_PARAMETERS
#if defined(MDL_CHECK_PARAMETERS) && defined(MATLAB_MEX_FILE)
/* Function: mdlCheckParameters =============================================
* Abstract:
* Validate our parameters to verify they are okay.
*/
static void mdlCheckParameters(SimStruct *S)
{
/* Check 1st parameter: Vbus */
{
if ( (mxGetN(Vbus_PARAM(S)) != 1) || !IS_PARAM_DOUBLE(Vbus_PARAM(S)) ) {
ssSetErrorStatus(S,"1st parameter to S-function, Vbus, is in error ");
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, 1); /* Number of expected parameters */
if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
/* Return if number of expected != number of actual parameters */
return;
}
ssSetNumContStates(S, NUM_CSTATES); // TBD
ssSetNumDiscStates(S, NUM_DSTATES);
if (!ssSetNumInputPorts(S, 1)) return;
ssSetInputPortWidth(S, 0, 2);
ssSetInputPortDirectFeedThrough(S, 0, 1);
/*
* Set direct feedthrough flag (1=yes, 0=no).
* A port has direct feedthrough if the input is used in either
* the mdlOutputs or mdlGetTimeOfNextVarHit functions.
*/
ssSetInputPortRequiredContiguous(S, 0, 0);
if (!ssSetNumOutputPorts(S, 1)) return;
ssSetOutputPortWidth(S, 0, 5);
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
ssSetNumSampleTimes(S, 2);
ssSetNumRWork(S, 0);
ssSetNumIWork(S, 0);
ssSetNumPWork(S, 0);
ssSetNumModes(S, 0);
ssSetNumNonsampledZCs(S, 0);
/* Specify the operating point save/restore compliance to be same as a
* built-in block */
ssSetOperatingPointCompliance(S, USE_DEFAULT_OPERATING_POINT);
ssSetOptions(S, SS_OPTION_EXCEPTION_FREE_CODE);
}
/* Function: mdlInitializeSampleTimes =========================================
* Abstract:
* This function is used to specify the sample time(s) for your
* S-function. You must register the same number of sample times as
* specified in ssSetNumSampleTimes.
*/
static void mdlInitializeSampleTimes(SimStruct *S)
{
ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
ssSetSampleTime(S, 1, 1E-3); //INHERITED_SAMPLE_TIME);
ssSetOffsetTime(S, 0, 0.0);
ssSetOffsetTime(S, 1, 0.0);
ssSetModelReferenceSampleTimeDefaultInheritance(S);
}
#define MDL_INITIALIZE_CONDITIONS /* Change to #undef to remove function */
#if defined(MDL_INITIALIZE_CONDITIONS)
/* Function: mdlInitializeConditions ========================================
* Abstract:
* In this function, you should initialize the continuous and discrete
* states for your S-function block. The initial states are placed
* in the state vector, ssGetContStates(S) or ssGetRealDiscStates(S).
* You can also perform any other initialization activities that your
* S-function may require. Note, this routine will be called at the
* start of simulation and if it is present in an enabled subsystem
* configured to reset states, it will be call when the enabled subsystem
* restarts execution to reset the states.
*/
static void mdlInitializeConditions(SimStruct *S)
{
real_T *xC0 = ssGetContStates(S);
real_T *xD0 = ssGetDiscStates(S);
xC0[0]= -1;
xC0[1]= 0;
xC0[2]= 0;
xC0[3]= 0;
xD0[0]= SLOPE;
}
#endif /* MDL_INITIALIZE_CONDITIONS */
#undef MDL_START /* Change to #undef to remove function */
#if defined(MDL_START)
/* Function: mdlStart =======================================================
* Abstract:
* This function is called once at start of model execution. If you
* have states that should be initialized once, this is the place
* to do it.
*/
static void mdlStart(SimStruct *S)
{
}
#endif /* MDL_START */
/* Function: mdlOutputs =======================================================
* Abstract:
* In this function, you compute the outputs of your S-function
* block.
*/
static void mdlOutputs(SimStruct *S, int_T tid)
{
real_T *xD = ssGetRealDiscStates(S);
real_T *xC = ssGetContStates(S);
real_T *y = ssGetOutputPortRealSignal(S,0);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
UNUSED_ARG(tid); /* not used in single tasking mode */
real_T Uw = U(0);
real_T Vw = U(1);
real_T Ww = U(2);
// compare vector components to ramp (x[0]) to generate U, V & W
// outputs here
y[0] = xC[0]; /* ramp, slope 4/Tfast */
y[1] = xD[0]; /* slope, fixed value of +m or -m */
y[2] = xC[1]; /* U */
y[3] = xC[2]; /* V */
y[4] = xC[3]; /* W */
}
#define MDL_UPDATE /* Change to #undef to remove function */
#if defined(MDL_UPDATE)
/* Function: mdlUpdate ==================================================
* Abstract:
* This function is called once for every major integration time step.
* Discrete states are typically updated here, but this function is
* useful for performing any tasks that should only take place once
* per integration step.
*/
static void mdlUpdate(SimStruct *S, int_T tid)
{
real_T *dx = ssGetdX(S);
real_T *xC = ssGetContStates(S);
real_T *xD = ssGetDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T m = SLOPE;
UNUSED_ARG(tid); /* not used in single tasking mode */
// generate sawtooth ramp x[0], using slope x[1]
// if (ssIsSampleHit(S, 1, tid))//
{
if ((xC[0] > 1.0) && (xD[0]>0)) { xD[0] = -m;}
if ((xC[0] < -1.0) && (xD[0]<0)) { xD[0] = m;}
}
}
#endif /* MDL_UPDATE */
#define MDL_DERIVATIVES /* Change to #undef to remove function */
#if defined(MDL_DERIVATIVES)
/* Function: mdlDerivatives =============================================
* Abstract:
* In this function, you compute the S-function block's derivatives.
* The derivatives are placed in the derivative vector, ssGetdX(S).
*/
static void mdlDerivatives(SimStruct *S)
{
real_T *dx = ssGetdX(S);
real_T *xD = ssGetDiscStates(S);
InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
real_T m = SLOPE;
// generate sawtooth ramp x[0], using slope xD[0]
dx[0] = xD[0];
dx[1] = 0;
dx[2] = 0;
dx[3] = 0;
}
#endif /* MDL_DERIVATIVES */
/* Function: mdlTerminate =================================================
* Abstract:
* In this function, you should perform any actions that are necessary
* at the termination of a simulation. For example, if memory was
* allocated in mdlStart, this is the place to free it.
*/
static void mdlTerminate(SimStruct *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

Sign in to comment.

Answers (0)

Categories

Find more on Simulink Functions in Help Center and File Exchange

Products


Release

R2020a

Community Treasure Hunt

Find the treasures in MATLAB Central and discover how the community can help you!

Start Hunting!