Matlab S-Function example: Read double input and write to file

This is an example for a Matlab Level-2 S-Function which reads a single continous input of type double and writes it to the file out.txt as CSV time,value. The file is opened in the mdlStart function and closed in the mdlTerminate function. The input value is read in the mdlUpdate function.

Also se Matlab Level 2 S-Function example: Sine wave (continous output).

#define S_FUNCTION_NAME  single_input_write_to_file
#define S_FUNCTION_LEVEL 2

#include "simstruc.h"
#include <cstdio>  // For file handling

// Define constants
#define SAMPLING_RATE 1000 // 1kHz

static void mdlInitializeSizes(SimStruct *S)
{
    ssSetNumSFcnParams(S, 0);  // No parameters
    ssSetNumContStates(S, 0);  // No continuous states
    ssSetNumDiscStates(S, 0);  // No discrete states
    // 1 input port with 1 element
    if (!ssSetNumInputPorts(S, 1)) return;
    ssSetInputPortWidth(S, 0, 1);  // Input port width = 1
    ssSetInputPortDataType(S, 0, SS_DOUBLE);
    ssSetInputPortComplexSignal(S, 0, COMPLEX_NO);

    ssSetInputPortRequiredContiguous(S, 0, 1);  // Require contiguous input port memory
    // ssSetInputPortDirectFeedThrough(S, 0, 1);  // Direct feedthrough

    // No output ports
    if (!ssSetNumOutputPorts(S, 0)) return;
    // Sample times
    ssSetNumSampleTimes(S, 1);  // Single sample time

    // Work vectors
    ssSetNumRWork(S, 0);  // Real work vector
    ssSetNumIWork(S, 0);  // Integer work vector
    ssSetNumPWork(S, 1);  // Pointer work vector for file pointer
    ssSetNumModes(S, 0);  // Mode vector
    ssSetNumNonsampledZCs(S, 0);  // Zero crossings

    // Make the S-function block capable of being used in a Real-Time Workshop-generated model
    ssSetOptions(S, 0);
}

#define MDL_START
static void mdlStart(SimStruct *S) {
    // Open the file "out.txt" for writing
    FILE *file = fopen("out.txt", "w");
    if (file == NULL) {
        ssSetErrorStatus(S, "Failed to open file 'out.txt'");
        return;
    }

    const real_T *u = (const real_T*) ssGetInputPortSignal(S, 0);  // Input pointer
    real_T input_value = u == nullptr ? -1 : u[0];  // Read the input value

    ssSetPWorkValue(S, 0, file);
}

// Function: mdlInitializeSampleTimes =========================================
// Abstract:
//    Initialize the sample times to be 1ms (1kHz)
static void mdlInitializeSampleTimes(SimStruct *S)
{
    ssSetSampleTime(S, 0, 1.0 / SAMPLING_RATE);  // 1kHz sampling rate
    ssSetOffsetTime(S, 0, 0.0);
}

// Not needed for this example
static void mdlOutputs(SimStruct *S, int_T tid) {
    // Do nothing
}

#define MDL_UPDATE
static void mdlUpdate(SimStruct *S, int_T tid)
{
    real_T time = ssGetT(S);  // Get the current simulation time

    // Get the input value
    const real_T *u = (const real_T*) ssGetInputPortRealSignal(S, 0);  // Input pointer
    real_T input_value = u == nullptr ? -1 : u[0];  // Read the input value

    // Get the file pointer from the work vector
    FILE *file = (FILE*) ssGetPWorkValue(S, 0);
    if (file != NULL) {
        fprintf(file, "%f,%f\n", time, input_value);  // Write the input value to the file
    }
}

static void mdlTerminate(SimStruct *S)
{
    // Close the file
    FILE *file = (FILE*) ssGetPWorkValue(S, 0);
    if (file != NULL) {
        fclose(file);
    }
}

// Required S-function trailer
#ifdef MATLAB_MEX_FILE
#include "simulink.c"   // MEX-file interface mechanism
#else
#include "cg_sfun.h"    // Code generation interface
#endif