/*
 *  File: sfuntmpl_gate_fortran.c
 *
 *  Abstract:: 
 *
 *  C->Fortran gateway TEMPLATE for a level 2 S-function.
 *  Copy, rename and then edit this file to call your Fortran
 *  code in the solver mode you want, then build it.
 *
 *  To build the mex file, first compile the Fortran file(s), 
 *  then include their object file names in the mex command.  
 *  For example, if your Fortran compiler is invoked with the
 *  'g77' command, a mex session looks like this at the command 
 *  prompt:
 *
 *  >> !g77 -c myfortranfile.f
 *  >> mex my_sfuntmpl_gate_fortran.c myfortranfile.o
 *  
 *  ---------------------------------------------------------
 *  | See matlabroot/simulink/src/sfuntmpl_doc.c for a more |
 *  | detailed description of the C-MEX template.           |
 *  ---------------------------------------------------------
 *
 * Copyright 1990-2000 The MathWorks, Inc.
 * $Revision: 1.2 $
 */


/*
 * You must specify the S_FUNCTION_NAME as the name of your S-function
 * (i.e. replace sfungate with the name of your S-function, which has
 * to match the name of the final mex file, e.g., if the S_FUNCTION_NAME
 * is my_sfuntmpl_gate_fortran, the mex filename will have to be 
 * my_sfuntmpl_gate_fortran.dll on Windows and 
 * my_sfuntmpl_gate_fortran.mexXXX on unix where XXX is the 3 letter 
 * mex extension code for your platform).
 */

#define S_FUNCTION_LEVEL 2
#define S_FUNCTION_NAME  sfuntmpl_gate_fortran

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

/* 
 * As a convenience, this template has options for both variable 
 * step and fixed step algorithm support.  If you want fixed step
 * operation, change the #define below to #undef.
 *
 * If you want to, you can delete all references to VARIABLE_STEP 
 * and set up the C-MEX as described in the "Writing S-functions" 
 * manual.
 */

#define VARIABLE_STEP

/* 
 * The interface (function prototype) for your  Fortran subroutine.  
 * Change the name to the name of the subroutine and the arguments 
 * to the actual argument list.
 *
 * Note that datatype REAL is 32 bits in Fortran and are passed
 * by reference, so the prototype arguments must be 'float *'.
 * INTEGER maps to int, so those arguments are 'int *'. Be
 * wary of IMPLICIT rules in Fortran when datatypes are not
 * explicit in your Fortran code.  To use the datatype double
 * the Fortran variables need to be declared DOUBLE PRECISION
 * either explicitly or via an IMPLICIT DOUBLE PRECISION
 * statement.
 *
 * Your Fortran compiler may decorate and/or change the 
 * capitalization of 'SUBROUTINE nameOfSub' differently 
 * than the prototype below.  Check your Fortran compiler's 
 * manual for options to learn about and possibly control 
 * external symbol decoration.  See also the text file named
 * sfuntmpl_fortran.txt in this file's directory.
 *
 * Additionally, you may want to use CFORTRAN, a tool for 
 * automating interface generation between C and Fortran.
 * Search the web for 'cfortran'.
 * 
 */

#ifdef VARIABLE_STEP

extern void nameofsub_(float *sampleArgs, 
                       float *states, 
                       int   *numstates,
                       float *sampleOutput);
#else

extern void nameofsub_(float *sampleArgs, float *sampleOutput);

#endif


/* 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;
 *      }
 *
 * See matlabroot/simulink/src/sfunctmpl_doc.c for further details.
 */

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

/* 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)
{
    /* See sfuntmpl.doc for more details on the macros below */

    ssSetNumSFcnParams(S, 0);  /* Number of expected parameters */
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) {
        /* Return if number of expected != number of actual parameters */
        return;
    }

    ssSetNumContStates(S, 1);  /* how many continuous states? */
    ssSetNumDiscStates(S, 0);

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

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

    ssSetNumSampleTimes(S, 1);

    /* 
     * If your Fortran code uses REAL for the state, input, and/or output 
     * datatypes, use these DWorks as work areas to downcast continuous 
     * states from double to REAL before calling your code.  You could
     * also put the work vectors in hard-coded local (stack) variables.
     *
     * For fixed step code, keep a copy of the variables  to be output 
     * in a DWork vector so the mdlOutputs() function can provide output 
     * data when needed. You can use as many DWork vectors as you like 
     * for both input and output (or hard-code local variables).
     */
    if(!ssSetNumDWork(   S, 3)) return;

    ssSetDWorkWidth(     S, 0, ssGetOutputPortWidth(S,0));
    ssSetDWorkDataType(  S, 0, SS_SINGLE); /* use SS_DOUBLE if needed */

    ssSetDWorkWidth(     S, 1, ssGetInputPortWidth(S,0));
    ssSetDWorkDataType(  S, 1, SS_SINGLE);

    ssSetDWorkWidth(     S, 2, ssGetNumContStates(S));
    ssSetDWorkDataType(  S, 2, SS_SINGLE);

    ssSetNumNonsampledZCs(S, 0);
    ssSetOptions(S, 0);
}



/* 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)
{
#ifdef VARIABLE_STEP

    /* 
     * For Fortran code with either no states at
     * all or with continuous states that you want
     * to support with variable time steps, use
     * a sample time like this:
     */
    ssSetSampleTime(S, 0, CONTINUOUS_SAMPLE_TIME);
    ssSetOffsetTime(S, 0, 0.0);

#else

    /* 
     * If the Fortran code implicitly steps time
     * at a fixed rate and you don't want to change
     * the code, you need to use a discrete (fixed
     * step) sample time, 1 second is chosen below.
     */
    ssSetSampleTime(S, 0, 0.01); /* Choose the sample time here if discrete */
    ssSetOffsetTime(S, 0, 0.0);
   
#endif
}



#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)
  {
      /* 
       * #undef MDL_INITIALIZE_CONDITIONS if you don't have any
       * continuous states.
       */
      real_T *x = ssGetContStates(S);

      /* set the values of the states (x) to start with */

  }
#endif /* MDL_INITIALIZE_CONDITIONS */



#define 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.  The default datatype for signals in Simulink is double,
 *    but you can use other intrinsic C datatypes or even custom
 *    datatypes if you wish.  See Simulink document "Writing S-functions"
 *    for details on datatype topics.
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{

#ifdef VARIABLE_STEP

    /*
     *    For Variable Step Code WITH CONTINUOUS STATES
     *    ---------------------------------------------
     * For Fortran code that implements continuous states and uses
     * the mdlDerivatives interface, call your Fortran code's output
     * routines from here.  If it alters the states, you have to
     * reset the solver.  Remember, in Simulink the continuous states
     * must be of type double, so be prepared to copy them to float 
     * if your Fortran code uses REAL as the datatype for the states.
     *
     *                ... or, NO STATES
     *                -----------------
     * If your code has no states and you want it to execute in
     * a continuous model, keep the uPtrs, sampleArgs, y, and
     * sampleOutput variables and delete x, xf, and nx.  Adjust 
     * the function call accordingly.
     */ 
    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
    float  *sampleArgs   = (float *) ssGetDWork(S,1);
    double *y            = ssGetOutputPortRealSignal(S,0);
    float  *sampleOutput = (float *) ssGetDWork(S,0);
    double *x            = ssGetContStates(S);
    float  *xf           = (float *) ssGetDWork(S,2);
    int     nx           = ssGetNumContStates(S);
    int k;
    
    /* 
     * If the datatype in the Fortran code is REAL
     * then you have to downcast the I/O and states from
     * double to float as copies before sending them 
     * to your code (or change the Fortran code).
     */

    for (k=0; k < ssGetDWorkWidth(S,1); k++) {
        sampleArgs[k] = (float) (*uPtrs[k]);
    }

    /*
     * It is recommended to use a DWork vector to 
     * allocate the space for  the float copy of 
     * the states (if needed).
     */
    for (k=0; k < nx; k++) {
        xf[k] = (float) x[k];
    }
    

    /* ==== Call the Fortran routine (args are pass-by-reference) */
    
    /* nameofsub_(sampleArgs, xf, &nx, sampleOutput ); */

   
    /* 
     * If needed, convert the float outputs to the 
     * double (y) output array 
     */
    for (k=0; k < ssGetOutputPortWidth(S,0); k++) {
        y[k] = (double) sampleOutput[k];
    }

#else

    /* 
     *    For Fixed Step Code
     *    -------------------
     * If the Fortran code implements discrete states (implicitly or
     * registered with Simulink, it doesn't matter), call the code
     * from mdlUpdates() and save the output values in a DWork vector.  
     * The variable step solver may call mdlOutputs() several
     * times in between calls to mdlUpdate, and you must extract the 
     * values from the DWork vector and copy them to the block output
     * variables.
     *
     * Be sure that the ssSetDWorkDataType(S,0) declaration in 
     * mdlInitializeSizes() uses SS_DOUBLE for the datatype when 
     * this code is active.
     */
    
    double *copyOfOutputs = (double *) ssGetDWork(S, 0);
    double *y             = ssGetOutputPortRealSignal(S,0);
    int     k;
    
    for (k=0; k < ssGetOutputSignalWidth(S,0); k++ ) {
        y[k] = copyOfOutputs[k];
    }

#endif

}



#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)
{

#ifndef VARIABLE_STEP

    /* 
     *    For Fixed Step Code Only
     *    ------------------------
     * If your Fortran code runs at a fixed time step that advances
     * each time you call it, it is best to call it here instead of
     * in mdlOutputs().  The states in the Fortran code need not be
     * continuous if you call your code from here.
     */
    InputRealPtrsType uPtrs = ssGetInputPortRealSignalPtrs(S,0);
    float  *sampleArgs   = (float *) ssGetDWork(S,1);
    double *y            = ssGetOutputPortRealSignal(S,0);
    float  *sampleOutput = (float *) ssGetDWork(S,0);
    int k;
    
    /* 
     * If the datatype in the Fortran code is REAL
     * then you have to downcast the I/O and states from
     * double to float as copies before sending them 
     * to your code (or change the Fortran code).
     */

    for (k=0; k < ssGetDWorkWidth(S,1); k++) {
        sampleArgs[k] = (float) (*uPtrs[k]);
    }


    /* ==== Call the Fortran routine (args are pass-by-reference) */
    
    /* nameofsub_(sampleArgs, sampleOutput ); */

   
    /* 
     * If needed, convert the float outputs to the 
     * double (y) output array 
     */
    for (k=0; k < ssGetOutputPortWidth(S,0); k++) {
        y[k] = (double) sampleOutput[k];
    }

#endif

}
#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)
{

#ifdef VARIABLE_STEP

      /* 
       *    For Variable Step Code Only
       *    ---------------------------
       * If your Fortran code needs to support continuous states
       * with variable timestep solvers, you need to call into
       * your Fortran routine (or perhaps one that shares a 
       * common block but only calculates derivatives) here to 
       * extract/calculate state derivatives WITHOUT ADVANCING TIME.
       */

#endif

}
#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)
{
}


/*========================================================*
 * See sfuntmpl_doc.c for the optional S-function methods *
 *========================================================*/

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

