/*
 * DLIMINTC   Example MEX-file for a discrete-time limited integrator.
 *            (see manual under Advanced Topics).
 *
 *            Syntax:  [sys, x0] = dlimintc(t,x,u,flag,lb,ub,x0,ts)
 *
 * Andrew Grace  Jan 10, 1991
 * Copyright (c) 1991-94 by The MathWorks, Inc.
 * All Rights Reserved
 */

/*
 * The following #define is used to specify the name of this S-Function.
 */

#define S_FUNCTION_NAME  dlimintc

/*
 * need to include simstruc.h for the definition of the SimStruct and
 * its associated macro definitions.
 */
#include "simstruc.h"

/*
 * Defines for easy access of the LB, UB, X0 matrices which are passed in
 */
#define LB	ssGetArg(S,0)
#define UB	ssGetArg(S,1)
#define X0	ssGetArg(S,2)
#define TS	ssGetArg(S,3)

/*
 * mdlInitializeSizes - initialize the sizes array
 *
 * The sizes array is used by SIMULINK to determine the S-function block's
 * characteristics (number of inputs, outputs, states, etc.).
 */

static void mdlInitializeSizes(S)
    SimStruct *S;
{
    ssSetNumContStates(    S, 0);      /* number of continuous states */
    ssSetNumDiscStates(    S, 1);      /* number of discrete states */
    ssSetNumInputs(        S, 1);      /* number of inputs */
    ssSetNumOutputs(       S, 1);      /* number of outputs */
    ssSetDirectFeedThrough(S, 0);      /* direct feedthrough flag */
    ssSetNumSampleTimes(   S, 1);      /* number of sample times */
    ssSetNumInputArgs(     S, 4);      /* number of input arguments */
    ssSetNumRWork(         S, 0);      /* number of real work vector elements */
    ssSetNumIWork(         S, 0);      /* number of integer work vector elements */
    ssSetNumPWork(         S, 0);      /* number of pointer work vector elements */
}

/*
 * mdlInitializeSampleTimes - initialize the sample times array
 *
 * This function is used to specify the sample time(s) for your S-function.
 * If your S-function is continuous, you must specify a sample time of 0.0.
 * Sample times must be registered in ascending order.
 */

static void mdlInitializeSampleTimes(S)
    SimStruct *S;
{
    /*
     * one sample time, and it's passed in as the fourth
     * S-function parameter
     */
    
    ssSetSampleTimeEvent(S, 0, mxGetPr(TS)[0]);
    ssSetOffsetTimeEvent(S, 0, 0);
}

/*
 * mdlInitializeConditions - initialize the states
 *
 * In this function, you should initialize the continuous and discrete
 * states for your S-function block.  The initial states are placed
 * in the x0 variable.  You can also perform any other initialization
 * activities that your S-function may require.
 */

static void mdlInitializeConditions(x0, S)
    double *x0;
    SimStruct *S;
{
    /* 
     * The initial condition is passed in as the third 
     * S-function parameter
     */
    
    x0[0] = mxGetPr(X0)[0]; 
}

/*
 * mdlOutputs - compute the outputs
 *
 * In this function, you compute the outputs of your S-function
 * block.  The outputs are placed in the y variable.
 */

static void mdlOutputs(y, x, u, S, tid)
    double *y, *x, *u;
    SimStruct *S;
    int tid;
{
    /*
     * Return the current state as the output.
     */
    y[0] = x[0];
}


/*
 * mdlUpdate - perform action at major integration time step
 *
 * 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(x, u, S, tid)
    double *x, *u;
    SimStruct *S;
    int tid;
{
    /*
     * Is it a sample hit?
     */
    
    if (ssIsSampleHitEvent(S, 0, tid)) {
	double lb, ub;
	double ts;
	
	/*
	 * need the sample time
	 */
	
	ts = mxGetPr(TS)[0];
	
	/*
	 * and the upper and lower bounds
	 */
	
	lb = mxGetPr(LB)[0];
	ub = mxGetPr(UB)[0];
	
	/*
	 * If the current state is outside of the limits, and the input
	 * will force it to be further out of bounds, don't update the
	 * discrete state, effectively shutting the integrator off.
	 * Otherwise, integrate normally.
	 */
	
	if ((x[0] <= lb && u[0] < 0.0) || (x[0] >= ub && u[0] > 0.0))
	    ;
	else
	    x[0] += u[0] * ts;
    }
}

/*
 * mdlDerivatives - compute the derivatives
 *
 * In this function, you compute the S-function block's derivatives.
 * The derivatives are placed in the dx variable.
 */

static void mdlDerivatives(dx, x, u, S, tid)
	double *dx, *x, *u;
	SimStruct *S;
	int tid;
{
}

/*
 * mdlTerminate - called when the simulation is terminated.
 *
 * In this function, you should perform any actions that are necessary
 * at the termination of a simulation.  For example, if memory was allocated
 * in mdlInitializeConditions, this is the place to free it.
 */

static void mdlTerminate(S)
    SimStruct *S;
{
}

#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
