diff --git a/StandaloneFMU/src/xxTable2D.c b/StandaloneFMU/src/xxTable2D.c index a6f74bd..edd3036 100644 --- a/StandaloneFMU/src/xxTable2D.c +++ b/StandaloneFMU/src/xxTable2D.c @@ -1,640 +1,655 @@ -/********************************************************** - * This file is generated by 20-sim ANSI-C Code Generator - * - * file: %FILE_NAME% - * model: %MODEL_NAME% - * expmt: %EXPERIMENT_NAME% - * date: %GENERATION_DATE% - * time: %GENERATION_TIME% - * user: %USER_NAME% - * from: %COMPANY_NAME% - * build: %GENERATION_BUILD% - **********************************************************/ - -%IF%%NUMBEROF_DLL_Table2D% -/* This file defines the functions necessary for table 2D lookup functions */ -#include -#include -#include -#include - -#include "xxtypes.h" -#include "xxfuncs.h" -#include "xxTable2D.h" - -#define LASTERRMSGBUFSIZE 512 - -static char g_lastError[LASTERRMSGBUFSIZE]; - -/* -* \brief A type definition for a 2D lookup table. -* -* The table \a data has \a nRows and \a nColumns and -* a set of input \a xValues and \a yValues used -* for interpolation. -*/ -typedef struct -{ - XXInteger nRows; - XXInteger nColumns; - XXDouble* xValues; - XXDouble* yValues; - XXDouble* data; -} LookupTable; - -/* Create a global variable to hold lookup tables */ -LookupTable *g_table2dFiles[%NUMBEROF_DLL_Table2D_Table2DInit%]; -static int g_table_count = 0; - -LookupTable* LookupTable_create(XXInteger rows, XXInteger columns); -void LookupTable_destroy(LookupTable* table); -XXDouble LookupTable_element(LookupTable* table, XXInteger row, XXInteger column); -XXBoolean count_data_dimensions(FILE* fp, XXInteger* rows, XXInteger* columns); -XXBoolean LookupTable_populate(LookupTable* table, FILE* fp); -XXInteger findPosition(XXDouble value, XXDouble* array, XXInteger size); -XXDouble calcDistance(XXDouble value, XXInteger pos, XXDouble* array, XXInteger size); - -void Table2D_Initialize() -{ -} - -void Table2D_Terminate() -{ - /* Free allocated memory */ - int i = 0; - for (i = 0; i < %NUMBEROF_DLL_Table2D_Table2DInit%; ++i) - { - LookupTable_destroy(g_table2dFiles[i]); - g_table2dFiles[i] = NULL; - } -} - -XXString Table2D_LastErrorMessage() -{ - return g_lastError; -} - -%IF%%OR(FMI1,FMI2)% -extern const char* g_fmuResourceLocation; -%ENDIF% - -#define MAX_FILENAME_LEN 2048 - -XXInteger Table2D_Table2DInit(XXDouble* inarr, XXInteger inputs, XXDouble* outarr, XXInteger outputs, XXInteger major) -{ - const char* filePath = NULL; - char* pch = NULL; - FILE* fStream = NULL; - int rows, cols; -%IF%%OR(FMI1,FMI2)% - char fileName[MAX_FILENAME_LEN]; - fileName[MAX_FILENAME_LEN - 1] = '\0'; -%ENDIF% - - /* is it possible to allocate more tables */ - if (g_table_count > %NUMBEROF_DLL_Table2D_Table2DInit%) - { - strncpy(g_lastError, "All tables already allocated", LASTERRMSGBUFSIZE); - return 1; - } - - /* Get the input file name */ - filePath = XXDouble2String(inarr[0]); - - /* Try to open data file */ -%IF%%OR(FMI1,FMI2)% - /* Use the FMU resources folder as base path */ - if (strlen(g_fmuResourceLocation) > 0) - { - strncpy(fileName, g_fmuResourceLocation, MAX_FILENAME_LEN - 1); - } - - /* Get rid of the absolute path of the data file - * 20-sim will always store a Windows oriented path, so we need to check - * for the backslash here - */ - pch = strrchr(filePath, '\\'); - if (pch != NULL) - filePath = ++pch; - - /* Add the filename */ - strncat(fileName, filePath, MAX_FILENAME_LEN - 1); - /* and open the file */ - fStream = fopen(fileName, "r"); -%ELSE% - fStream = fopen(filePath, "r"); -%ENDIF% - if (fStream == NULL) - { - strncpy(g_lastError, "Error opening file", LASTERRMSGBUFSIZE); - outarr[0] = -1; - return 1; - } - - /* TODO Add to global variable of lookup tables */ - if (!count_data_dimensions(fStream, &rows, &cols)) - { - strncpy(g_lastError, "Invalid data file", LASTERRMSGBUFSIZE); - /* Clean up */ - fclose(fStream); - outarr[0] = -1; - return 1; - } - - /* Allocate memory for 2D lookup table */ - g_table2dFiles[g_table_count] = LookupTable_create(rows, cols); - /* before updating the table count, first try to populate the table */ - if (!LookupTable_populate(g_table2dFiles[g_table_count], fStream)) - { - strncpy(g_lastError, "Error reading file", LASTERRMSGBUFSIZE); - - /* clean this entry */ - LookupTable_destroy(g_table2dFiles[g_table_count]); - g_table2dFiles[g_table_count] = NULL; - - /* Clean up */ - fclose(fStream); - outarr[0] = -1; - return 1; - } - - /* Clean up */ - fclose(fStream); - - /* and return the id */ - outarr[0] = g_table_count; - - /* and mark the next empty count*/ - g_table_count++; - return 0; -} - -XXInteger Table2D_TableRead(XXDouble* inarr, XXInteger inputs, XXDouble* outarr, XXInteger outputs, XXInteger major) -{ - /* TODO When multiple input files are supported, check here which one to read data from. - * Expect three input values: - * inputs[0] = first input is assumed to be the file ID - * inputs[1] = y value - * inputs[2] = x value - */ - - XXDouble xValue; - XXDouble yValue; - - XXInteger posX; - XXInteger posY; - - XXInteger tableCase; - XXDouble a, b, c; - XXDouble leftTop, leftBottom, rightTop, rightBottom; - - int id; - LookupTable *theTable; - - if (inputs != 3) - { - strncpy(g_lastError, "Wrong input values for reading 2D table", LASTERRMSGBUFSIZE); - return 1; - } - if (outputs != 1) - { - strncpy(g_lastError, "Wrong output values for reading 2D table", LASTERRMSGBUFSIZE); - return 1; - } - id = (int)inarr[0]; - if (id < 0 || id > 1) // check for correct id. - { - strncpy(g_lastError, "Incorrect id passed", LASTERRMSGBUFSIZE); - return 1; - } - theTable = g_table2dFiles[id]; - if (theTable == NULL) - { - strncpy(g_lastError, "No table allocated yet", LASTERRMSGBUFSIZE); - return 1; - } - if ((theTable->xValues == NULL) || (theTable->yValues == NULL)) - { - strncpy(g_lastError, "Values table not allocated yet", LASTERRMSGBUFSIZE); - return 1; - } - - xValue = inarr[2]; - yValue = inarr[1]; - - posX = findPosition(xValue, theTable->xValues, theTable->nColumns); - posY = findPosition(yValue, theTable->yValues, theTable->nRows); - - tableCase = 0; - if (posY < 0) - { - /* at top */ - if (posX < 0) - tableCase = 1; /* left top */ - else - if (posX >= theTable->nColumns) - tableCase = 3; /* mid top */ - else - tableCase = 2; /* mid top*/ - } - else { - if (posY >= theTable->nRows) - { - /* at bottom */ - if (posX < 0) - tableCase = 7; /* left top */ - else - if (posX >= theTable->nColumns) - tableCase = 9; /* mid top */ - else - tableCase = 8; /* mid top*/ - } - else { - /* at middle */ - if (posX < 0) - tableCase = 4; /* left top */ - else - if (posX >= theTable->nColumns) - tableCase = 6; /* mid top */ - else - tableCase = 5; /* mid top*/ - } - } - - switch (tableCase) - { - case 1: /* left top */ - /* return the left top value */ - outarr[0] = LookupTable_element(theTable, 0, 0); - break; - case 2: /* mid top */ - /* interpolate the top two column values*/ - a = LookupTable_element(theTable, 0, posX); - b = LookupTable_element(theTable, 0, posX + 1); - c = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); - outarr[0] = (1.0 - c) * a + c * b; - break; - case 3: /* right top */ - /* return the right top value */ - outarr[0] = LookupTable_element(theTable, 0, theTable->nColumns - 1); - break; - case 4: /* left mid */ - /* interpolate the left two rows values*/ - a = LookupTable_element(theTable, posY, 0); - b = LookupTable_element(theTable, posY + 1, 0); - c = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); - outarr[0] = (1.0 - c) * a + c * b; - break; - case 5: /* mid mid */ - /* this is the normal case inside the table */ - leftTop = LookupTable_element(theTable, posY, posX); - rightTop = LookupTable_element(theTable, posY, posX + 1); - leftBottom = LookupTable_element(theTable, posY + 1, posX); - rightBottom = LookupTable_element(theTable, posY + 1, posX + 1); - a = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); - b = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); - - outarr[0] = (1.0 - a) * (1.0 - b) * leftTop + - a * (1.0 - b) * rightTop + - (1.0 - a) * b * leftBottom + - a * b * rightBottom; - break; - case 6: /* right mid */ - /* interpolate the right two rows values*/ - a = LookupTable_element(theTable, posY, theTable->nColumns - 1); - b = LookupTable_element(theTable, posY + 1, theTable->nColumns - 1); - c = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); - outarr[0] = (1.0 - c) * a + c * b; - break; - case 7: /* left bottom */ - /* return the left bottom value */ - outarr[0] = LookupTable_element(theTable, theTable->nRows - 1, 0); - break; - case 8: /* mid bottom */ - /* interpolate the top bottom column values*/ - a = LookupTable_element(theTable, theTable->nRows - 1, posX); - b = LookupTable_element(theTable, theTable->nRows - 1, posX + 1); - c = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); - outarr[0] = (1.0 - c) * a + c * b; - break; - case 9: /* right bottom */ - /* return the right bottom value */ - outarr[0] = LookupTable_element(theTable, theTable->nRows - 1, theTable->nColumns - 1); - break; - default: - outarr[0] = 0.0; - break; - } - return 0; -} - -LookupTable* LookupTable_create(XXInteger rows, XXInteger columns) -{ - LookupTable* table; - - if ((rows <= 0) || (columns <= 0)) - { - return NULL; - } - - table = (LookupTable*)malloc(sizeof(LookupTable)); - if (table == NULL) - { - return NULL; - } - - table->nRows = rows; - table->nColumns = columns; - table->xValues = (XXDouble*) malloc(columns * sizeof(XXDouble)); - table->yValues = (XXDouble*) malloc(rows * sizeof(XXDouble)); - table->data = (XXDouble*) malloc(columns * rows * sizeof(XXDouble)); - - if ((table->xValues == NULL) || (table->yValues == NULL) || (table->data == NULL)) - { - LookupTable_destroy(table); - table = NULL; - } - - return table; -} - -void LookupTable_destroy(LookupTable* table) -{ - if (table == NULL) - { - return; - } - - if (table->data != NULL ) free(table->data); - if (table->yValues != NULL) free(table->yValues); - if (table->xValues != NULL) free(table->xValues); - free(table); -} - -/* -* \brief Return the element found at \a row and \a column in \a table. -*/ -XXDouble LookupTable_element(LookupTable* table, XXInteger row, XXInteger column) -{ - if (table == NULL) - return 0.0; - if (row < 0 || column < 0) - return 0.0; - if ( row >= table->nRows || column >= table->nColumns ) - return 0.0; - - return table->data[row * table->nColumns + column]; -} - -/* -* \brief Count the dimensions of the data in the file \fp. -* -* The file contains double values separated by spaces or tabs. -* The first element of the first row is ignored. -* -* \param[in] fp Pointer to the data file -* \param[out] rows Number of data rows found -* \param[out] columns Number of data columns found -* \return True on success, otherwise false: -* - if a reading error happens -* - if the number of columns is inconsistent or -* - if less than two columns or rows are counted. -*/ -XXBoolean count_data_dimensions(FILE* fp, XXInteger* rows, XXInteger* columns) -{ - XXInteger tmpCols = 0; - XXBoolean readingVal = XXFALSE; - XXInteger c; - *rows = 0; - *columns = 0; - - do - { - c = fgetc(fp); - if (c == EOF) - { - break; - } - if (ferror(fp)) - { - return XXFALSE; - } - - switch (c) - { - case '\r': - continue; - case ' ': - case '\t': - if (readingVal){ - readingVal = XXFALSE; - } - break; - case '\n': - case EOF: - if (tmpCols < 2) - { - /* Less than two columns found */ - return XXFALSE; - } - - ++(*rows); - readingVal = XXFALSE; - - if (*rows == 1) - { - /* Set data columns found in first row */ - *columns = tmpCols; - } - - if (tmpCols != *columns) - { - /* Number of columns is inconsistent */ - return XXFALSE; - } - - tmpCols = 0; - break; - default: - if (!readingVal) - { - ++tmpCols; - readingVal = XXTRUE; - } - } - } while (c != EOF); - - if (*rows < 2) - { - /* Less than two rows found */ - return XXFALSE; - } - - /* Ignore the first data point in file (i.e. element of the first row/column). */ - --(*rows); - --(*columns); - - return XXTRUE; -} - -/* -* \brief Populate \a table with data from file \fp. -* -* The format of the file is as follows: -* - The first element of the first row of the file is ignored. -* - The rest of the first row has the x values. -* - The rest of the first column has the y values. -* - The remaining values are the data points of the 2D lookup table. -* -* \param[in] table The table to fill in -* \param[in] fp Pointer to the data file -* \return True on success, false if a reading error happens -*/ -XXBoolean LookupTable_populate(LookupTable* table, FILE* fp) -{ - /* Go to beginning of/first position in file */ - double tmp; - XXDouble *dataEntry = table->data; - XXInteger row = 0; - XXInteger col = 0; - - rewind(fp); - for (row = 0; row < table->nRows + 1; ++row) - { - for (col = 0; col < table->nColumns + 1; ++col) - { - while (fscanf(fp, "%lf", &tmp) == 0) - { - /* Matching failure, so advance to next position */ - fseek(fp, 1, SEEK_CUR); - } - - if (ferror(fp)) - return XXFALSE; - - if (row == 0) - { - if (col == 0) - { - /* left upper corner value, skip this one */ - continue; - } - - /* register this as a column value */ - table->xValues[col - 1] = tmp; - continue; - } - - if (col == 0) - { - /* register this as a row value */ - table->yValues[row - 1] = tmp; - continue; - } - - /* just write at the current entry */ - *dataEntry = (XXDouble) tmp; - - /* and commence to the next value in the array */ - dataEntry++; - } - } - return XXTRUE; -} - -/* - * \brief Return the position of the \a value in the \a array of \a size. - */ -XXInteger findPosition(XXDouble value, XXDouble* array, XXInteger size) -{ - XXInteger pos = 1; - - /* first some error handling */ - if (array == NULL) - return -1; - if (size == 0) - return -1; - - /* check if we're outside the data, assuming the data is always increasing */ - if (value < array[0]) - return -1; - if (value > array[size - 1]) - return size; - - /* and loop through the data until we find the position - * start at element 1, 'cause the check on the first value has been done already - */ - while (pos < size) - { - if (array[pos] > value) - return pos - 1; - - /* and continue to the next position. */ - pos++; - } - - return pos - 1; -} - -/* - * Calculate the distance of the \a value from the lower data point located in - * \a pos within the \a array of \a size. - * - * \param[out] The distance - */ -XXDouble calcDistance(XXDouble value, XXInteger pos, XXDouble* array, XXInteger size) -{ - XXDouble lowerDistance = 0, previousVal = 0, nextVal = 0; - - if (array == NULL) - { - return 0.0; - } - /* check for left out of bounds */ - if (pos < 0) - { - /* extrapolate to the left, by returning a negative distance */ - if (size <= 1) - return 0.0; /* cannot extrapolate on one data point*/ - - /* just the fraction of the given value w.r.t. the first two points - * assume here that the value itself is already negative - * and array[1] > array[0]; - */ - if (array[1] == array[0]) - return 0.0; - return (value - array[1]) / (array[1] - array[0]); - } - /* check for right out of bounds */ - if ( pos >= size ) - { - /* the same as on the left side, but now on the right */ - - /* extrapolate to the right, by returning a positive distance */ - if (size <= 1) - return 0.0; /* cannot extrapolate on one data point*/ - - /* just the fraction of the given value w.r.t. the first two points - * assume here that the value itself is already negative - * and array[size - 1] > array[size - 2]; - */ - if (array[size - 1] == array[size - 2]) - return 0.0; - return (value - array[size - 2]) / (array[size - 1] - array[size - 2]); - } - - /* nicely in the range */ - lowerDistance = 0; - - previousVal = array[pos]; - nextVal = array[pos+1]; - /* prevent division by zero */ - if (nextVal == previousVal) - return 0.0; - - /* return the fraction */ - return (value - previousVal) / (nextVal - previousVal); -} -%ELSE% -/* Empty file; no Table2D support needed for this model */ -%ENDIF% +/********************************************************** + * This file is generated by 20-sim ANSI-C Code Generator + * + * file: %FILE_NAME% + * model: %MODEL_NAME% + * expmt: %EXPERIMENT_NAME% + * date: %GENERATION_DATE% + * time: %GENERATION_TIME% + * user: %USER_NAME% + * from: %COMPANY_NAME% + * build: %GENERATION_BUILD% + **********************************************************/ + +%IF%%NUMBEROF_DLL_Table2D% +/* This file defines the functions necessary for table 2D lookup functions */ +#include +#include +#include +#include + +#include "xxtypes.h" +#include "xxfuncs.h" +#include "xxTable2D.h" + +#define LASTERRMSGBUFSIZE 512 + +static char g_lastError[LASTERRMSGBUFSIZE]; + +/* +* \brief A type definition for a 2D lookup table. +* +* The table \a data has \a nRows and \a nColumns and +* a set of input \a xValues and \a yValues used +* for interpolation. +*/ +typedef struct +{ + XXInteger nRows; + XXInteger nColumns; + XXDouble* xValues; + XXDouble* yValues; + XXDouble* data; +} LookupTable; + +/* Create a global variable to hold lookup tables */ +static LookupTable **g_table2dFiles=0; + + +static int g_table_count = 0; +static int g_table_size =0; + +LookupTable* LookupTable_create(XXInteger rows, XXInteger columns); +void LookupTable_destroy(LookupTable* table); +XXDouble LookupTable_element(LookupTable* table, XXInteger row, XXInteger column); +XXBoolean count_data_dimensions(FILE* fp, XXInteger* rows, XXInteger* columns); +XXBoolean LookupTable_populate(LookupTable* table, FILE* fp); +XXInteger findPosition(XXDouble value, XXDouble* array, XXInteger size); +XXDouble calcDistance(XXDouble value, XXInteger pos, XXDouble* array, XXInteger size); + +void Table2D_Initialize() +{ + g_table_size++; + + if(!g_table2dFiles) + g_table2dFiles = malloc( g_table_size * sizeof(LookupTable *)); + else + g_table2dFiles =realloc(g_table2dFiles, g_table_size * sizeof(LookupTable *)); + +} + +void Table2D_Terminate() +{ + if(!g_table2dFiles) + return; + /* Free allocated memory */ + int i = 0; + for (i = 0; i < g_table_size; ++i) + { + LookupTable_destroy(g_table2dFiles[i]); + g_table2dFiles[i] = NULL; + } + + free(g_table2dFiles); + g_table2dFiles = NULL; +} + +XXString Table2D_LastErrorMessage() +{ + return g_lastError; +} + +%IF%%OR(FMI1,FMI2)% +extern const char* g_fmuResourceLocation; +%ENDIF% + +#define MAX_FILENAME_LEN 2048 + +XXInteger Table2D_Table2DInit(XXDouble* inarr, XXInteger inputs, XXDouble* outarr, XXInteger outputs, XXInteger major) +{ + const char* filePath = NULL; + char* pch = NULL; + FILE* fStream = NULL; + int rows, cols; +%IF%%OR(FMI1,FMI2)% + char fileName[MAX_FILENAME_LEN]; + fileName[MAX_FILENAME_LEN - 1] = '\0'; +%ENDIF% + + /* is it possible to allocate more tables */ + if (g_table_count >= g_table_size) + { + //Make room for one additional table + Table2D_Initialize(); + } + + /* Get the input file name */ + filePath = XXDouble2String(inarr[0]); + + /* Try to open data file */ +%IF%%OR(FMI1,FMI2)% + /* Use the FMU resources folder as base path */ + if (strlen(g_fmuResourceLocation) > 0) + { + strncpy(fileName, g_fmuResourceLocation, MAX_FILENAME_LEN - 1); + } + + /* Get rid of the absolute path of the data file + * 20-sim will always store a Windows oriented path, so we need to check + * for the backslash here + */ + pch = strrchr(filePath, '\\'); + if (pch != NULL) + filePath = ++pch; + + /* Add the filename */ + strncat(fileName, filePath, MAX_FILENAME_LEN - 1); + /* and open the file */ + fStream = fopen(fileName, "r"); +%ELSE% + fStream = fopen(filePath, "r"); +%ENDIF% + if (fStream == NULL) + { + strncpy(g_lastError, "Error opening file", LASTERRMSGBUFSIZE); + outarr[0] = -1; + return 1; + } + + /* TODO Add to global variable of lookup tables */ + if (!count_data_dimensions(fStream, &rows, &cols)) + { + strncpy(g_lastError, "Invalid data file", LASTERRMSGBUFSIZE); + /* Clean up */ + fclose(fStream); + outarr[0] = -1; + return 1; + } + + /* Allocate memory for 2D lookup table */ + g_table2dFiles[g_table_count] = LookupTable_create(rows, cols); + /* before updating the table count, first try to populate the table */ + if (!LookupTable_populate(g_table2dFiles[g_table_count], fStream)) + { + strncpy(g_lastError, "Error reading file", LASTERRMSGBUFSIZE); + + /* clean this entry */ + LookupTable_destroy(g_table2dFiles[g_table_count]); + g_table2dFiles[g_table_count] = NULL; + + /* Clean up */ + fclose(fStream); + outarr[0] = -1; + return 1; + } + + /* Clean up */ + fclose(fStream); + + /* and return the id */ + outarr[0] = g_table_count; + + /* and mark the next empty count*/ + g_table_count++; + return 0; +} + +XXInteger Table2D_TableRead(XXDouble* inarr, XXInteger inputs, XXDouble* outarr, XXInteger outputs, XXInteger major) +{ + /* TODO When multiple input files are supported, check here which one to read data from. + * Expect three input values: + * inputs[0] = first input is assumed to be the file ID + * inputs[1] = y value + * inputs[2] = x value + */ + + XXDouble xValue; + XXDouble yValue; + + XXInteger posX; + XXInteger posY; + + XXInteger tableCase; + XXDouble a, b, c; + XXDouble leftTop, leftBottom, rightTop, rightBottom; + + int id; + LookupTable *theTable; + + if (inputs != 3) + { + strncpy(g_lastError, "Wrong input values for reading 2D table", LASTERRMSGBUFSIZE); + return 1; + } + if (outputs != 1) + { + strncpy(g_lastError, "Wrong output values for reading 2D table", LASTERRMSGBUFSIZE); + return 1; + } + id = (int)inarr[0]; + if (id < 0 || id > 1) // check for correct id. + { + strncpy(g_lastError, "Incorrect id passed", LASTERRMSGBUFSIZE); + return 1; + } + theTable = g_table2dFiles[id]; + if (theTable == NULL) + { + strncpy(g_lastError, "No table allocated yet", LASTERRMSGBUFSIZE); + return 1; + } + if ((theTable->xValues == NULL) || (theTable->yValues == NULL)) + { + strncpy(g_lastError, "Values table not allocated yet", LASTERRMSGBUFSIZE); + return 1; + } + + xValue = inarr[2]; + yValue = inarr[1]; + + posX = findPosition(xValue, theTable->xValues, theTable->nColumns); + posY = findPosition(yValue, theTable->yValues, theTable->nRows); + + tableCase = 0; + if (posY < 0) + { + /* at top */ + if (posX < 0) + tableCase = 1; /* left top */ + else + if (posX >= theTable->nColumns) + tableCase = 3; /* mid top */ + else + tableCase = 2; /* mid top*/ + } + else { + if (posY >= theTable->nRows) + { + /* at bottom */ + if (posX < 0) + tableCase = 7; /* left top */ + else + if (posX >= theTable->nColumns) + tableCase = 9; /* mid top */ + else + tableCase = 8; /* mid top*/ + } + else { + /* at middle */ + if (posX < 0) + tableCase = 4; /* left top */ + else + if (posX >= theTable->nColumns) + tableCase = 6; /* mid top */ + else + tableCase = 5; /* mid top*/ + } + } + + switch (tableCase) + { + case 1: /* left top */ + /* return the left top value */ + outarr[0] = LookupTable_element(theTable, 0, 0); + break; + case 2: /* mid top */ + /* interpolate the top two column values*/ + a = LookupTable_element(theTable, 0, posX); + b = LookupTable_element(theTable, 0, posX + 1); + c = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); + outarr[0] = (1.0 - c) * a + c * b; + break; + case 3: /* right top */ + /* return the right top value */ + outarr[0] = LookupTable_element(theTable, 0, theTable->nColumns - 1); + break; + case 4: /* left mid */ + /* interpolate the left two rows values*/ + a = LookupTable_element(theTable, posY, 0); + b = LookupTable_element(theTable, posY + 1, 0); + c = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); + outarr[0] = (1.0 - c) * a + c * b; + break; + case 5: /* mid mid */ + /* this is the normal case inside the table */ + leftTop = LookupTable_element(theTable, posY, posX); + rightTop = LookupTable_element(theTable, posY, posX + 1); + leftBottom = LookupTable_element(theTable, posY + 1, posX); + rightBottom = LookupTable_element(theTable, posY + 1, posX + 1); + a = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); + b = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); + + outarr[0] = (1.0 - a) * (1.0 - b) * leftTop + + a * (1.0 - b) * rightTop + + (1.0 - a) * b * leftBottom + + a * b * rightBottom; + break; + case 6: /* right mid */ + /* interpolate the right two rows values*/ + a = LookupTable_element(theTable, posY, theTable->nColumns - 1); + b = LookupTable_element(theTable, posY + 1, theTable->nColumns - 1); + c = calcDistance(yValue, posY, theTable->yValues, theTable->nRows); + outarr[0] = (1.0 - c) * a + c * b; + break; + case 7: /* left bottom */ + /* return the left bottom value */ + outarr[0] = LookupTable_element(theTable, theTable->nRows - 1, 0); + break; + case 8: /* mid bottom */ + /* interpolate the top bottom column values*/ + a = LookupTable_element(theTable, theTable->nRows - 1, posX); + b = LookupTable_element(theTable, theTable->nRows - 1, posX + 1); + c = calcDistance(xValue, posX, theTable->xValues, theTable->nColumns); + outarr[0] = (1.0 - c) * a + c * b; + break; + case 9: /* right bottom */ + /* return the right bottom value */ + outarr[0] = LookupTable_element(theTable, theTable->nRows - 1, theTable->nColumns - 1); + break; + default: + outarr[0] = 0.0; + break; + } + return 0; +} + +LookupTable* LookupTable_create(XXInteger rows, XXInteger columns) +{ + LookupTable* table; + + if ((rows <= 0) || (columns <= 0)) + { + return NULL; + } + + table = (LookupTable*)malloc(sizeof(LookupTable)); + if (table == NULL) + { + return NULL; + } + + table->nRows = rows; + table->nColumns = columns; + table->xValues = (XXDouble*) malloc(columns * sizeof(XXDouble)); + table->yValues = (XXDouble*) malloc(rows * sizeof(XXDouble)); + table->data = (XXDouble*) malloc(columns * rows * sizeof(XXDouble)); + + if ((table->xValues == NULL) || (table->yValues == NULL) || (table->data == NULL)) + { + LookupTable_destroy(table); + table = NULL; + } + + return table; +} + +void LookupTable_destroy(LookupTable* table) +{ + if (table == NULL) + { + return; + } + + if (table->data != NULL ) free(table->data); + if (table->yValues != NULL) free(table->yValues); + if (table->xValues != NULL) free(table->xValues); + free(table); +} + +/* +* \brief Return the element found at \a row and \a column in \a table. +*/ +XXDouble LookupTable_element(LookupTable* table, XXInteger row, XXInteger column) +{ + if (table == NULL) + return 0.0; + if (row < 0 || column < 0) + return 0.0; + if ( row >= table->nRows || column >= table->nColumns ) + return 0.0; + + return table->data[row * table->nColumns + column]; +} + +/* +* \brief Count the dimensions of the data in the file \fp. +* +* The file contains double values separated by spaces or tabs. +* The first element of the first row is ignored. +* +* \param[in] fp Pointer to the data file +* \param[out] rows Number of data rows found +* \param[out] columns Number of data columns found +* \return True on success, otherwise false: +* - if a reading error happens +* - if the number of columns is inconsistent or +* - if less than two columns or rows are counted. +*/ +XXBoolean count_data_dimensions(FILE* fp, XXInteger* rows, XXInteger* columns) +{ + XXInteger tmpCols = 0; + XXBoolean readingVal = XXFALSE; + XXInteger c; + *rows = 0; + *columns = 0; + + do + { + c = fgetc(fp); + if (c == EOF) + { + break; + } + if (ferror(fp)) + { + return XXFALSE; + } + + switch (c) + { + case '\r': + continue; + case ' ': + case '\t': + if (readingVal){ + readingVal = XXFALSE; + } + break; + case '\n': + case EOF: + if (tmpCols < 2) + { + /* Less than two columns found */ + return XXFALSE; + } + + ++(*rows); + readingVal = XXFALSE; + + if (*rows == 1) + { + /* Set data columns found in first row */ + *columns = tmpCols; + } + + if (tmpCols != *columns) + { + /* Number of columns is inconsistent */ + return XXFALSE; + } + + tmpCols = 0; + break; + default: + if (!readingVal) + { + ++tmpCols; + readingVal = XXTRUE; + } + } + } while (c != EOF); + + if (*rows < 2) + { + /* Less than two rows found */ + return XXFALSE; + } + + /* Ignore the first data point in file (i.e. element of the first row/column). */ + --(*rows); + --(*columns); + + return XXTRUE; +} + +/* +* \brief Populate \a table with data from file \fp. +* +* The format of the file is as follows: +* - The first element of the first row of the file is ignored. +* - The rest of the first row has the x values. +* - The rest of the first column has the y values. +* - The remaining values are the data points of the 2D lookup table. +* +* \param[in] table The table to fill in +* \param[in] fp Pointer to the data file +* \return True on success, false if a reading error happens +*/ +XXBoolean LookupTable_populate(LookupTable* table, FILE* fp) +{ + /* Go to beginning of/first position in file */ + double tmp; + XXDouble *dataEntry = table->data; + XXInteger row = 0; + XXInteger col = 0; + + rewind(fp); + for (row = 0; row < table->nRows + 1; ++row) + { + for (col = 0; col < table->nColumns + 1; ++col) + { + while (fscanf(fp, "%lf", &tmp) == 0) + { + /* Matching failure, so advance to next position */ + fseek(fp, 1, SEEK_CUR); + } + + if (ferror(fp)) + return XXFALSE; + + if (row == 0) + { + if (col == 0) + { + /* left upper corner value, skip this one */ + continue; + } + + /* register this as a column value */ + table->xValues[col - 1] = tmp; + continue; + } + + if (col == 0) + { + /* register this as a row value */ + table->yValues[row - 1] = tmp; + continue; + } + + /* just write at the current entry */ + *dataEntry = (XXDouble) tmp; + + /* and commence to the next value in the array */ + dataEntry++; + } + } + return XXTRUE; +} + +/* + * \brief Return the position of the \a value in the \a array of \a size. + */ +XXInteger findPosition(XXDouble value, XXDouble* array, XXInteger size) +{ + XXInteger pos = 1; + + /* first some error handling */ + if (array == NULL) + return -1; + if (size == 0) + return -1; + + /* check if we're outside the data, assuming the data is always increasing */ + if (value < array[0]) + return -1; + if (value > array[size - 1]) + return size; + + /* and loop through the data until we find the position + * start at element 1, 'cause the check on the first value has been done already + */ + while (pos < size) + { + if (array[pos] > value) + return pos - 1; + + /* and continue to the next position. */ + pos++; + } + + return pos - 1; +} + +/* + * Calculate the distance of the \a value from the lower data point located in + * \a pos within the \a array of \a size. + * + * \param[out] The distance + */ +XXDouble calcDistance(XXDouble value, XXInteger pos, XXDouble* array, XXInteger size) +{ + XXDouble lowerDistance = 0, previousVal = 0, nextVal = 0; + + if (array == NULL) + { + return 0.0; + } + /* check for left out of bounds */ + if (pos < 0) + { + /* extrapolate to the left, by returning a negative distance */ + if (size <= 1) + return 0.0; /* cannot extrapolate on one data point*/ + + /* just the fraction of the given value w.r.t. the first two points + * assume here that the value itself is already negative + * and array[1] > array[0]; + */ + if (array[1] == array[0]) + return 0.0; + return (value - array[1]) / (array[1] - array[0]); + } + /* check for right out of bounds */ + if ( pos >= size ) + { + /* the same as on the left side, but now on the right */ + + /* extrapolate to the right, by returning a positive distance */ + if (size <= 1) + return 0.0; /* cannot extrapolate on one data point*/ + + /* just the fraction of the given value w.r.t. the first two points + * assume here that the value itself is already negative + * and array[size - 1] > array[size - 2]; + */ + if (array[size - 1] == array[size - 2]) + return 0.0; + return (value - array[size - 2]) / (array[size - 1] - array[size - 2]); + } + + /* nicely in the range */ + lowerDistance = 0; + + previousVal = array[pos]; + nextVal = array[pos+1]; + /* prevent division by zero */ + if (nextVal == previousVal) + return 0.0; + + /* return the fraction */ + return (value - previousVal) / (nextVal - previousVal); +} +%ELSE% +/* Empty file; no Table2D support needed for this model */ +%ENDIF%