/*
 * SCPLXWKS  Complex To-Workspace block for signal processing with SIMULINK.
 *
 * DSP Blockset S-Function to store complex SIMULINK data in workspace.
 * This SIMULINK S-function stores its input as a complex workspace
 * variable; it is called by the Complex To Workspace block in the DSP 
 * Blockset.
 *
 *  Copyright 1995-2000 The MathWorks, Inc. 
 *  $Revision: 1.10 $  $Date: 2000/06/11 23:24:12 $
 */

#define S_FUNCTION_NAME  scplxwks
#define S_FUNCTION_LEVEL 2

#include "dsp_sim.h"

#define VARNAME_ARG    (ssGetSFcnParam(S,0))
#define BUFSIZ_ARG     (ssGetSFcnParam(S,1))
#define DECI_ARG       (ssGetSFcnParam(S,2))
#define SAMPLETIME_ARG (ssGetSFcnParam(S,3))
#define NUM_PARAMS     (4)

/* PWork indices: */
#define CURBUF_PTR      0

/* IWork indices: */
#define DECI_TARGET_IDX 0
#define DECI_CNT_IDX    1
#define FILL_IDX        2
#define BUFSIZ_IDX      3


#ifdef MATLAB_MEX_FILE
#define MDL_CHECK_PARAMETERS
static void mdlCheckParameters(SimStruct *S) {
    const char *msg = NULL;

    if (!mxIsChar(VARNAME_ARG)) {
        msg = "Matrix name must be a string";
		goto FCN_EXIT;
    }
    /* Should check that name is a valid MATLAB variable name */

    if ((mxGetN(BUFSIZ_ARG) != 1) || (mxGetM(BUFSIZ_ARG) != 1)) {
        msg = "Maximum row count must be a scalar";
		goto FCN_EXIT;
    }
    if ((mxGetN(DECI_ARG) != 1) || (mxGetM(DECI_ARG) != 1)) {
        msg = "Decimation value must be a scalar integer";
		goto FCN_EXIT;
    }

	{
		real_T dval = mxGetPr(DECI_ARG)[0];
		int_T  ival = (int_T)dval;

		if ((dval < 1) || (dval != ival)) {
			msg = "Decimation must be a positive integer.";
			goto FCN_EXIT;
		}

		dval = mxGetPr(BUFSIZ_ARG)[0];
		ival = (int_T)dval;
		if ((dval < 1) || (dval != ival)) {
			msg = "Maximum row count must be a positive integer";
			goto FCN_EXIT;
		}
	}

    if ( (mxGetM(SAMPLETIME_ARG) != 1) ||
         (mxGetN(SAMPLETIME_ARG) != 1) && (mxGetN(SAMPLETIME_ARG) != 2)
       ) {
        msg = "The sample time must be a scalar or a 2-element row vector";
		goto FCN_EXIT;
	}

FCN_EXIT:
    ssSetErrorStatus(S, msg);
}
#endif


static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, NUM_PARAMS);

#if defined(MATLAB_MEX_FILE)
    if (ssGetNumSFcnParams(S) != ssGetSFcnParamsCount(S)) return;
    mdlCheckParameters(S);
    if (ssGetErrorStatus(S) != NULL) return;
#else
    mexWarnMsgTxt("Real-time data logging is not supported for "
                  "the 'Complex To Workspace' block");
#endif

	/*
	 * Only the variable name (param 0) can be
	 *   modified while simulation runs:
	 */
    ssSetSFcnParamNotTunable(S,1);
    ssSetSFcnParamNotTunable(S,2);
    ssSetSFcnParamNotTunable(S,3);

	/* Inputs: */
    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, DYNAMICALLY_SIZED);
    ssSetInputPortDirectFeedThrough(S, 0, 1);

	/* Outputs: */
    if (!ssSetNumOutputPorts(S,0)) return;

    ssSetNumRWork(      S, DYNAMICALLY_SIZED);
    ssSetNumIWork(      S, 4);
    ssSetNumPWork(      S, 1);
    ssSetNumSampleTimes(S, 1);

    /* NOTE: Because this function interacts with the MATLAB
	 * workspace, we cannot set SS_OPTION_EXCEPTION_FREE_CODE.
	 */
}

/*
 * mdlInitializeSampleTimes - initializes the array of sample times stored in
 *                            the SimStruct associated with this S-Function.
 */

static void mdlInitializeSampleTimes(SimStruct *S)
{
    real_T sampleTime = mxGetPr(SAMPLETIME_ARG)[0];
    real_T offsetTime = (mxGetN(SAMPLETIME_ARG) == 2) 
					  ? offsetTime = mxGetPr(SAMPLETIME_ARG)[1]
					  : 0.0;

    ssSetSampleTime(S, 0, sampleTime);
    ssSetOffsetTime(S, 0, offsetTime);
}

/*
 * mdlInitializeConditions - initializes the states for the S-Function
 */
#define MDL_INITIALIZE_CONDITIONS
static void mdlInitializeConditions(SimStruct *S)
{
        real_T *buffer = ssGetRWork(S);
		int_T   deci   = (int_T) mxGetPr(DECI_ARG)[0];
		int_T   bufsiz = (int_T) mxGetPr(BUFSIZ_ARG)[0];

		if (ssGetInputPortWidth(S,0) % 2 != 0) {
			ssSetErrorStatus(S, "There must be an even number of data samples "
								"in a complex data vector");
			return;
		}

		ssSetIWorkValue(S, BUFSIZ_IDX, bufsiz);
        ssSetPWorkValue(S, CURBUF_PTR, buffer);

        /* Initialize the buffer fill counter: */
        ssSetIWorkValue(S, FILL_IDX, 0);

        /*
         * Set DECI_CNT to 1 so that the first sample gets stored, regardless of
         * the decimation value.  Otherwise, we will suffer unnecessary latency.
         */
        ssSetIWorkValue(S, DECI_CNT_IDX, 1);
        ssSetIWorkValue(S, DECI_TARGET_IDX, deci);
}


/*
 * mdlOutputs - computes the outputs of the S-Function
 */
static void mdlOutputs(SimStruct *S, int_T tid)
{
        int_T bufsiz;
        int_T *deci_cnt = ssGetIWork(S) + DECI_CNT_IDX;

        /*
         * If the decimation counter isn't zero on entry, return.
         * Otherwise, acquire the input vector into the buffer.
         */
        if (--(*deci_cnt) != 0) {
			return;
		}

        /* Reset decimation counter: */
        *deci_cnt = ssGetIWorkValue(S, DECI_TARGET_IDX);

        /* Get count of the number of vectors stored: */
        bufsiz = ssGetIWorkValue(S, BUFSIZ_IDX);

        /* Copy the input vector to the buffer: */
        {
            int_T             vec_width = ssGetInputPortWidth(S,0);
            real_T           *currbuff  = ssGetPWorkValue(S, CURBUF_PTR);
            real_T           *bufstart  = ssGetRWork(S);
            real_T           *bufend    = bufstart + bufsiz * vec_width;
		    InputRealPtrsType uptr      = ssGetInputPortRealSignalPtrs(S,0);

            /*
             * Copy complex data vector.
             * Storage organization:
             *
             *    Data Time Width
             *    ---- ---- -----
             *    Real 0    vec_width/2 samples
             *    Imag 0    "
             *    Real 1    "
             *    Imag 1    "
             *     ...      "
             *
             * NOTE: Since we have allocated exactly bufsiz number
             * of vec_width vectors in the buffer, the buffer will
             * not wrap in the middle of the vector copy.  It may
             * wrap after the copy, however:
             */
			while (vec_width-- > 0) {
				*currbuff++ = **uptr++;
			}

            if (currbuff == bufend) {
                currbuff = bufstart;
            }
            ssSetPWorkValue(S, CURBUF_PTR, currbuff);
        }

        /*
         * Update the buffer fill counter.  If buffer has been
         * filled, leave fill counter equal to the buffer size:
         */
        {
            int_T *fill_idx = ssGetIWork(S) + FILL_IDX;
            if (*fill_idx < bufsiz) {
                (*fill_idx)++;  /* Increment fill count */
            }
        }
 }



static void mdlTerminate(SimStruct *S)
{
#ifdef MATLAB_MEX_FILE
	    real_T  *bufstart     = ssGetRWork(S);
        int_T    bufsiz       = ssGetIWorkValue(S, BUFSIZ_IDX);
        int_T    vec_width    = ssGetInputPortWidth(S,0);
        int_T    rows         = ssGetIWorkValue(S, FILL_IDX);
        int_T    cols         = vec_width/2;    /* # complex values to write */
        real_T  *currbuff     = ssGetPWorkValue(S, CURBUF_PTR);
        real_T  *bufend       = bufstart + bufsiz * vec_width;
        char    *matname;     /* name of array to construct in MATLAB WS    */
		mxArray *pm;          /* array to create in MATLAB's base workspace */

        /* Get name of array to create in MATLAB workspace: */
        {
            const mxArray *pmat      = VARNAME_ARG;
    	    int_T          name_len  = mxGetM(pmat)*mxGetN(pmat)+1;

            matname = (char *)mxCalloc(name_len, sizeof(char));
            mxGetString(pmat, matname, name_len);
        }

        /*
         * Create result array in MATLAB's base workspace.
         * # cols = half the # of vector entries (1 complex MATLAB arg
         *          formed from 2 Simulink vector entries)
         * # rows = number of actual samples collected, or buffer size
         *          whichever is greater
         */
        pm = mxCreateDoubleMatrix(rows, cols, mxCOMPLEX);
        mxSetName(pm, matname);  /* Set the array's name     */
        mxFree(matname);         /* Free the name allocation */

        /*
         * Transfer buffer data to array.
         * Must transpose the data so vector runs along the row
         *
         * For a filled buffer:
         *   currbuff points to the oldest sample in circular buffer
         * For a partially filled buffer:
         *   currbuff points to past end of linear data buffer
         */
        {
            real_T *in     = currbuff;
            real_T *out_Re = mxGetPr(pm);
            real_T *out_Im = mxGetPi(pm);
            int_T   j;

            /* For partial buffer, adjust initial data pointer: */
            if (rows < bufsiz) {
                in = bufstart;
            }

            /* Transpose during data copy */
            for (j=0; j<rows; j++) {
                int_T i;
                for (i=0; i<cols; i++) {
                    out_Re[j+i*rows] = *in++;
                }
                for (i=0; i<cols; i++) {
                    out_Im[j+i*rows] = *in++;
                }
                if (in == bufend) {
                    in = bufstart;
                }
            }
        }

        if (mexPutArray(pm,"base") != 0) {
            ssSetErrorStatus(S,"Failed to transfer complex array to the base workspace");
			return;
        }
#endif
}


#if defined(MATLAB_MEX_FILE)
/* Function: mdlSetWorkWidths =================================================
 * Abstract:
 *      Called after all the width and sample times have been propagated.
 *      Determine the size of the buffers (RWork) needed and the number
 *      of buffer indices.
 */
#define MDL_SET_WORK_WIDTHS
static void mdlSetWorkWidths(SimStruct *S)
{
	int_T  num_ele = ssGetInputPortWidth(S,0);
	int_T  bufsiz  = (int_T)mxGetPr(BUFSIZ_ARG)[0];

    ssSetNumRWork(S, bufsiz * num_ele);
}
#endif


/*=============================*
 * Required S-function trailer *
 *=============================*/

#ifdef  MATLAB_MEX_FILE
#include "simulink.c"   /* MEX Interface Mechanism   */
#else
#include "cg_sfun.h"    /* RTW Registration Function */
#endif
