/*
 * SUNBUFF   A SIMULINK unbuffer block with pipelined execution
 * DSP Blockset S-Function for unbuffering vectors.
 *	This SIMULINK S-function unbuffers a vector input, converting it to
 *	a scalar output; it is called by the Unbuffer blocks in the DSP 
 *	Blockset.
 *
 *   Syntax:  [sys, x0] = sunbuff(t,x,u,flag,...
 *               OutputIndex,LastOutput,sampleTime,InitialOffset,pipelined)
 *
 *  Copyright 1995-2000 The MathWorks, Inc.
 *  $Revision: 1.11 $  $Date: 2000/05/05 20:16:17 $
 */

#define S_FUNCTION_NAME sunbuff

#include <math.h>
#include "dsp_sim.h"

/*
 * Defines for easy access of the input parameters
 */
#define NUM_ARGS        5
#define FIRST_OUTPUT    ssGetArg(S,0)
#define LAST_OUTPUT     ssGetArg(S,1)
#define SAMPLE_TIME     ssGetArg(S,2)
#define INITIAL_OFFSET  ssGetArg(S,3)
#define PIPELINED_MODE  ssGetArg(S,4)
#define sampleTol       1E-8           /* sqrt(eps), more or less */

/*
 * IWork indices
 */
#define BUFFER_INDEX 0
#define FIRST_INDEX  1
#define LAST_INDEX   2
#define INPUT_INDEX  3
#define FILL_COUNT   4


static void mdlInitializeSizes(SimStruct *S)
{
#ifdef MATLAB_MEX_FILE
    mexWarnMsgTxt("Obsolete blocks calling the CMEX S-Function 'sunbuff' have\n"
                  "been detected.  If you wish to upgrade this model, replace\n"
                  "all previous Unbuffer blocks with the new version found in\n"
                  "the block library:\n"
                  "        'dsplib/General DSP/Buffers'\n."
                  "Note that if any Unbuffer blocks are upgraded, all Buffer\n"
                  "blocks must also be upgraded at the same time for compat-\n"
                  "ibility.  Also, note that an additional pipeline delay\n"
                  "equal to one buffer length will occur in the upgraded\n"
                  "model for each Buffer and Unbuffer block.\n"
                  "If you wish to disable this warning message, type\n"
                  "'warning off' at the MATLAB command line.\n\n");
#endif
    /*
     * Set-up size information.
     */ 
    if (ssGetNumArgs(S) == NUM_ARGS) {

	int_T num_sample_times;
	int_T bufSize = (int_T) (mxGetPr(LAST_OUTPUT)[0] - mxGetPr(FIRST_OUTPUT)[0] + 1);
	
	if ((mxGetM(SAMPLE_TIME) != 2) || (mxGetN(SAMPLE_TIME) != 2)) {
	  num_sample_times = 0;
	} else {
	  real_T *pr = mxGetPr(SAMPLE_TIME);
	  if ((pr[0]-pr[1] < sampleTol) && (pr[2]-pr[3] < sampleTol)) {
	    num_sample_times = (int_T) 1;
	  } else {
	    num_sample_times = (int_T) 2;
	  }
	}

#ifdef MATLAB_MEX_FILE
	if (num_sample_times < 1) {
            mexErrMsgTxt("The sample time must be a 2 x 2 matrix");
        }
	if ((mxGetPr(FIRST_OUTPUT)[0]) > (mxGetPr(LAST_OUTPUT)[0]) ) {
            mexErrMsgTxt("The First Output Index must be less than or equal "
			 "to the Last Output Index");
        } 
#endif
       
        ssSetNumContStates(    S, 0);
        ssSetNumDiscStates(    S, 0);
        ssSetNumInputs(        S, DYNAMICALLY_SIZED);
        ssSetNumOutputs(       S, 1);
        ssSetDirectFeedThrough(S, 1);
        ssSetNumInputArgs(     S, NUM_ARGS);
        ssSetNumSampleTimes(   S, num_sample_times);
        ssSetNumRWork(         S, 2 * bufSize);
        ssSetNumIWork(         S, 5);
        ssSetNumPWork(         S, 0);

    }
#ifdef MATLAB_MEX_FILE
    else {
	mexErrMsgTxt("Wrong number of input arguments");
    }
#endif 
}    


static void mdlInitializeSampleTimes(SimStruct *S)
{
    real_T *pr = mxGetPr(SAMPLE_TIME);
    int_T   n  = ssGetNumSampleTimes(S);

    ssSetSampleTimeEvent(S, 0, pr[0]);
    ssSetOffsetTimeEvent(S, 0, pr[2]);
    if (n > 1) {
      ssSetSampleTimeEvent(S, 1, pr[1]);
      ssSetOffsetTimeEvent(S, 1, pr[3]);
    }
}


static void mdlInitializeConditions(real_T *x0, SimStruct *S)
{
    real_T *buffer       = ssGetRWork(S);
    int_T *bufIndex      = ssGetIWork(S) + BUFFER_INDEX; 
    int_T *FirstOut      = ssGetIWork(S) + FIRST_INDEX;
    int_T *LastOut       = ssGetIWork(S) + LAST_INDEX;
    int_T *bufInputIndex = ssGetIWork(S) + INPUT_INDEX;
    int_T *bufFillCount  = ssGetIWork(S) + FILL_COUNT;
    int_T N              = ssGetNumInputs(S);
    int_T offset         = (int_T) (mxGetPr(INITIAL_OFFSET)[0]);
    int_T bufSize;

    *FirstOut      = (int_T) (mxGetPr(FIRST_OUTPUT)[0] - 1);
    *LastOut       = (int_T) (mxGetPr(LAST_OUTPUT)[0]  - 1);
    bufSize        = *LastOut - *FirstOut + 1;
    *bufFillCount  = bufSize;

    if (mxGetPr(PIPELINED_MODE)[0] != (int_T)0) {
	*bufInputIndex = 0;
    } else {
	*bufInputIndex = bufSize;
    }

    /* Preset buffer to zeros: */
    {
        int_T i;
        for(i=2*bufSize; i-- != 0; ) {
            *buffer++ = (real_T)0.0;
        }
    }

    /* 
     * Index into input vector ranges from 1 to N (not 0 to N-1)
     * for purposes of FistOut and LastOut.
     *
     * If offset < 0, then use the default "Buffer Block" compatible
     * initial offset: set it such that at Nth sample (time (N-1)*Ts), 
     * the unbuffer is at its beginning.
     * If offset >= 0, then just use the input offset for the initial offset.
     */
    if (offset == -1) {
	*bufIndex = -(*FirstOut - 1);
    } else {
	*bufIndex = offset;
    }
    *bufIndex %= bufSize;
    if (*bufIndex <= 0) {
	*bufIndex += bufSize;
    }

#ifdef MATLAB_MEX_FILE
    /* 
     * Some defensive programing
     */ 
    if (N != -1) {
        if (*FirstOut < 0) {
	    mexErrMsgTxt("Invalid First Output Index specified.\n"
                         "Must be a positive integer.\n");
        } else if (*LastOut < 0) {
	    mexErrMsgTxt("Invalid Last Output Index specified.\n"
                         "Must be a positive integer.\n");
	}
    }
#endif
}


static void mdlOutputs(real_T *y, const real_T *x, const real_T *u, 
                       SimStruct *S, int_T tid)
{
    int_T   bufFillCount = *(ssGetIWork(S) + FILL_COUNT);
    int_T   bufSize      = bufFillCount * 2;
    real_T *buffer;

    /*
     * Acquire the buffer data
     */  
    if ((ssGetNumSampleTimes(S) == 1) || ssIsSampleHitEvent(S, 1, tid)) {
	int_T *bufInputIndex = ssGetIWork(S) + INPUT_INDEX;
	int_T FirstOut       = *(ssGetIWork(S) + FIRST_INDEX);
	int_T LastOut        = *(ssGetIWork(S) + LAST_INDEX);
	int_T N              = ssGetNumInputs(S);
	int_T i;

	buffer = ssGetRWork(S) + *bufInputIndex;
	for (i = FirstOut; i <= LastOut; i++) {
	    if (i < N) {
		*buffer++ = u[i];
	    } else {
		/*
		 * If the output index passed in specifies a size
		 * larger than the number of inputs, pad with zeros.
		 */
		*buffer++ = (real_T)0.0;
	    }
	}
	*bufInputIndex += bufFillCount;
	if (*bufInputIndex == bufSize) {
	    *bufInputIndex = 0;
	}
    }

    /*
     * Output from the buffer
     */
    if (ssIsSampleHitEvent(S, 0, tid)) {
	int_T *bufIndex = ssGetIWork(S) + BUFFER_INDEX;
	buffer = ssGetRWork(S);
	*y = buffer[(*bufIndex)++];
	if (*bufIndex == bufSize) {
	    *bufIndex = 0;
	}
    }
}


static void mdlUpdate(real_T *x, const real_T *u, SimStruct *S, int_T tid)
{
}

static void mdlDerivatives(real_T *dx, const real_T *x, const real_T *u, 
                           SimStruct *S, int_T tid)
{
}

static void mdlTerminate(SimStruct *S)
{
}

#include "dsp_trailer.c"
 
/* [EOF]: sunbuff.c */
