From 8ff6f89958073e952e060a7267fe83fec55871f6 Mon Sep 17 00:00:00 2001 From: Kenneth Lausdahl Date: Fri, 14 Jul 2017 13:20:17 +0200 Subject: [PATCH] malloc handling of global variable cache. I'm simply not able to make the crosscompiler compile the dylib for mac such that it actually gets its own global memory. Windows, Linux and Mac if compiled on a mac works but not with the cross compiler. --- StandaloneFMU/src/xxTable2D.c | 1295 +++++++++++++++++---------------- 1 file changed, 655 insertions(+), 640 deletions(-) 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%