From 2ebad7f68e973524fc561d6dc3e98d96dd44ea43 Mon Sep 17 00:00:00 2001 From: Christian Risi <75698846+CnF-Gris@users.noreply.github.com> Date: Mon, 9 Dec 2024 11:37:08 +0000 Subject: [PATCH] V0.6.9 Arroyo Toad Added library --- Sources/DataAcquisition | 1 - Sources/DataAcquisition/DataAcquisition.cpp | 440 ++++++++++++++++++ .../DataAcquisition/Include/DataAcquisition.h | 39 ++ 3 files changed, 479 insertions(+), 1 deletion(-) delete mode 160000 Sources/DataAcquisition create mode 100644 Sources/DataAcquisition/DataAcquisition.cpp create mode 100644 Sources/DataAcquisition/Include/DataAcquisition.h diff --git a/Sources/DataAcquisition b/Sources/DataAcquisition deleted file mode 160000 index a9ba495..0000000 --- a/Sources/DataAcquisition +++ /dev/null @@ -1 +0,0 @@ -Subproject commit a9ba49505e8d77e5bdc759914d23540b77fa7b84 diff --git a/Sources/DataAcquisition/DataAcquisition.cpp b/Sources/DataAcquisition/DataAcquisition.cpp new file mode 100644 index 0000000..e077a5b --- /dev/null +++ b/Sources/DataAcquisition/DataAcquisition.cpp @@ -0,0 +1,440 @@ +#include +#include +#include +#include + +#include + +int outlierCount; + +// Variable definition +static float **readings; +static int sensorsNumber; +static int slidingWindowSize; + + + + +/** + * @brief Sets the number of sensors. + * + * This function sets the global variable `sensorsNumber` to the specified value. + * + * @param number The number of sensors to set. + */ +static void setSensorsNumber(int number) { + sensorsNumber = number; +} + +/** + * @brief Sets the size of the sliding window. + * + * This function sets the size of the sliding window used for data acquisition. + * + * @param size The desired size of the sliding window. + */ +static void setSlidingWindowSize(int size) { + slidingWindowSize = size; +} + +/** + * @brief Initializes the sensor readings array. + * + * This function allocates memory for a 2D array to store sensor readings. + * Each sensor will have a sliding window of readings. + * + * @param numSensors The number of sensors. + * @param windowSize The size of the sliding window for each sensor. + * + * The function performs the following steps: + * 1. Allocates memory for the array of sensor readings. + * 2. Allocates memory for each sensor's sliding window of readings. + * 3. Sets the number of sensors and the sliding window size using private setter functions. + * + * If memory allocation fails at any point, the function prints an error message and exits the program. + */ +void initializeReadings(int numSensors, float deltaTime) { + + int windowSize = (int)(1000 / deltaTime); // Standard case of 1 second of acquisition + + // Allocate memory for the array of sensor readings + readings = (float **)malloc(numSensors * sizeof(float *)); + + // Check if memory allocation was successful + if (readings == NULL) { + perror("Failed to allocate memory for readings"); + exit(EXIT_FAILURE); + } + + // Allocate memory for each sensor's sliding window of readings + for (int i = 0; i < numSensors; i++) { + readings[i] = (float *)calloc(windowSize, sizeof(float)); + if (readings[i] == NULL) { + perror("Failed to allocate memory for sensor readings"); + exit(EXIT_FAILURE); + } + } + + // Calling the private setter functions + setSensorsNumber(numSensors); + setSlidingWindowSize(windowSize); +} + +/** + * @brief Frees the memory allocated for sensor readings. + * + * This function iterates through the array of sensor readings and frees the memory + * allocated for each individual reading. After freeing all individual readings, it + * checks if the main readings array is not NULL and frees it as well. + * + * @return true if the main readings array was successfully freed, false otherwise. + */ +bool freeReadings() { + for (int i = 0; i < sensorsNumber; i++) { + free(readings[i]); + } + + if(readings != NULL){ + free(readings); + readings = NULL; + return true; + } + else{ + return false; + } +} + +/** + * @brief Get the number of sensors. + * + * This function returns the total number of sensors currently available. + * + * @return int The number of sensors. + */ +// Get the number of sensors +int getSensorsNumber() { + return sensorsNumber; +} + +/** + * @brief Get the sliding window size. + * + * This function returns the current size of the sliding window used in data acquisition. + * + * @return The size of the sliding window. + */ +// Get the sliding window size +int getSlidingWindowSize() { + return slidingWindowSize; +} + +/** + * @brief Checks if the sliding window for a given sensor is full. + * + * This function iterates through the readings of a specified sensor and + * determines if all entries in the sliding window are non-zero, indicating + * that the window is full. + * + * @param sensorIndex The index of the sensor to check. + * @return true if the sliding window is full (all entries are non-zero), + * false otherwise. + */ +// Control on the fullness of the sliding window +bool isFull(int sensorIndex) { + for (int i = 0; i < slidingWindowSize; i++) { + if (readings[sensorIndex][i] == 0) { + return false; + } + } + return true; +} + +/** + * @brief Retrieves the last reading from the specified sensor. + * + * This function returns the most recent reading from the sensor identified by + * the given index. If the sensor's data buffer is full, it returns the last + * value in the buffer. Otherwise, it returns the first value. + * + * @param sensorIndex The index of the sensor to retrieve the reading from. + * @return The last reading from the specified sensor. + */ +float getLastReading(int sensorIndex) { + if (isFull(sensorIndex)) { + return readings[sensorIndex][slidingWindowSize - 1]; + } else { + return readings[sensorIndex][0]; + } +} + +static int lastSensorIndex = -1; + +/** + * @brief Adds a new sensor reading to the data acquisition system. + * + * This function updates the readings for the sensors in a circular buffer manner. + * If the buffer for a sensor is full, it shifts the readings to make space for the new value. + * If the buffer is not full, it adds the new value to the first available position. + * + * @param value The new sensor reading to be added. + */ +void addReading(float value) { + lastSensorIndex = (lastSensorIndex + 1) % sensorsNumber; + int sensorIndex = lastSensorIndex; + + if (isFull(sensorIndex)) { + for (int i = 0; i < slidingWindowSize - 1; i++) { + readings[sensorIndex][i] = readings[sensorIndex][i + 1]; + } + readings[sensorIndex][slidingWindowSize - 1] = value; + } else { + for (int i = 0; i < slidingWindowSize; i++) { + if (readings[sensorIndex][i] == 0) { + readings[sensorIndex][i] = value; + break; + } + } + } +} + +/** + * @brief Calculates the average reading for a specified sensor. + * + * This function computes the average of the readings stored in a sliding window + * for the sensor specified by the sensorIndex parameter. If the sliding window + * is not full, the function prints a message and returns 0. + * + * @param sensorIndex The index of the sensor for which the average reading is to be calculated. + * @return The average reading of the specified sensor if the sliding window is full; otherwise, returns 0. + */ +float getAverageOnSensor(int sensorIndex) { + if(isFull(sensorIndex) == false){ + printf("The sliding window is not full\n"); + return 0; + } + else{ + float sum = 0; + for (int i = 0; i < slidingWindowSize; i++) { + sum += readings[sensorIndex][i]; + } + return sum / slidingWindowSize; + } +} + +/** + * @brief Calculates the average reading from all sensors. + * + * This function iterates through all available sensors, retrieves the last reading + * from each sensor, and calculates the average of these readings. + * + * @return The average reading from all sensors as a float. + */ +float getAverageOnAllSensors() { + float sum = 0; + for (int i = 0; i < sensorsNumber; i++) { + sum += getLastReading(i); + } + return sum / sensorsNumber; +} + +/** + * @brief Calculates the overall average of sensor readings. + * + * This function iterates through all sensors and their respective sliding windows + * to calculate the overall average of the readings. It first checks if the sliding + * window for each sensor is full. If any sliding window is not full, it prints a + * message indicating which sensor's sliding window is not full and returns 0. + * Otherwise, it sums up all the readings from all sensors and calculates the average. + * + * @return The overall average of the sensor readings if all sliding windows are full, + * otherwise returns 0. + */ +float getOverallAverage() { + float sum = 0; + int totalReadings = 0; + for (int i = 0; i < sensorsNumber; i++) { + if (!isFull(i)) { + printf("The sliding window for sensor %d is not full\n", i); + return 0; + } + for (int j = 0; j < slidingWindowSize; j++) { + sum += readings[i][j]; + totalReadings++; + } + } + return sum / totalReadings; +} + +/** + * @brief Calculates the standard deviation of sensor readings. + * + * This function computes the standard deviation of the readings from a specified sensor. + * It first checks if the sliding window for the sensor is full. If not, it prints a message + * and returns 0. If the sliding window is full, it calculates the average of the readings, + * then computes the sum of the squared differences between each reading and the average. + * Finally, it returns the square root of the average of these squared differences. + * + * @param sensorIndex The index of the sensor for which the standard deviation is to be calculated. + * @return The standard deviation of the sensor readings, or 0 if the sliding window is not full. + */ +float getStandardDeviationOnSensor(int sensorIndex) { + if(isFull(sensorIndex) == false){ + printf("The sliding window is not full\n"); + return 0; + } + else{ + float sum = 0; + float average = getAverageOnSensor(sensorIndex); + for (int i = 0; i < slidingWindowSize; i++) { + sum += pow(readings[sensorIndex][i] - average, 2); + } + return sqrt(sum / slidingWindowSize); + } +} + +/** + * @brief Calculates the standard deviation of the readings from all sensors. + * + * This function computes the standard deviation of the sensor readings by first + * calculating the average of all sensor readings, then summing the squared + * differences between each reading and the average, and finally taking the + * square root of the average of these squared differences. + * + * @return The standard deviation of the sensor readings. + */ +float getStandardDeviationOnAllSensors() { + float sum = 0; + float average = getAverageOnAllSensors(); + for (int i = 0; i < sensorsNumber; i++) { + float lastReading = getLastReading(i); + sum += pow(lastReading - average, 2); + } + + return sqrt(sum / sensorsNumber); +} + +/** + * @brief Detects anomalies in sensor readings based on a given average and standard deviation. + * + * This function calculates the upper and lower thresholds for anomaly detection using a 97% confidence interval. + * It then iterates through the sensor readings within a sliding window and counts the number of outliers that fall + * outside the calculated thresholds. + * + * @param average The average value of the sensor readings. + * @param standardDeviation The standard deviation of the sensor readings. + * + * @note The function uses a confidence interval multiplier of 2.17 to determine the thresholds. + * @note The variable `outlierCount` is used to store the number of detected outliers. + * @note The variables `slidingWindowSize` and `sensorsNumber` are assumed to be defined globally. + * @note The 2D array `readings` is assumed to contain the sensor data. + */ +void anomalyDetect(float average, float standardDeviation) { + float upperThreshold = average + 2.17 * standardDeviation; // 97% confidence interval + float lowerThreshold = average - 2.17 * standardDeviation; // Lower bound for anomaly detection + outlierCount = 0; + for (int j = 0; j < slidingWindowSize; j++) { + for (int i = 0; i < sensorsNumber; i++) { + if (readings[i][j] > upperThreshold || readings[i][j] < lowerThreshold) { + outlierCount++; + } + } + } + +} + +/** + * @brief Calculates the overall standard deviation of sensor readings. + * + * This function computes the overall standard deviation of the readings from multiple sensors. + * It first checks if the sliding window for each sensor is full. If any sensor's sliding window + * is not full, it prints a message and returns 0. Otherwise, it calculates the sum of the squared + * differences between each reading and the overall average, and then computes the standard deviation. + * The function also calls `anomalyDetect` with the calculated average and standard deviation. + * + * @return The overall standard deviation of the sensor readings. Returns 0 if any sensor's sliding window is not full. + */ +float getOverallStandardDeviation() { + float sum = 0; + int totalReadings = 0; + float totalAverage = getOverallAverage(); + for (int i = 0; i < sensorsNumber; i++) { + if (!isFull(i)) { + printf("The sliding window for sensor %d is not full\n", i); + return 0; + } + for (int j = 0; j < slidingWindowSize; j++) { + sum += pow(readings[i][j] - getOverallAverage(), 2); + totalReadings++; + } + } + + float totalStandardDeviation = sqrt(sum / totalReadings); + + anomalyDetect(totalAverage, totalStandardDeviation); + + return totalStandardDeviation; +} + +/** + * @brief Retrieves the current count of outliers. + * + * This function returns the number of outliers detected and stored in the + * variable `outlierCount`. + * + * @return The number of outliers. + */ +int getOutlierCount() { + return outlierCount; +} + +Metrics getMetrics(float *readings[], int sensorNumber, int slidingWindow){ + Metrics metrics; + + int average = 0; + int standardDeviation = 0; + int outlierCount = 0; + int* faultySensors = (int*)malloc(sensorNumber * sizeof(int)); + + for(int i = 0; i < sensorNumber; i++){ + faultySensors[i] = 0; + } + + float sum = 0; + + for(int i = 0; i < sensorNumber; i++){ + for(int j = 0; j < slidingWindow; j++){ + sum += readings[j][i]; + } + } + + average = sum / (sensorNumber * slidingWindow); + + for(int i = 0; i < sensorNumber; i++){ + for(int j = 0; j < slidingWindow; j++){ + standardDeviation += pow(readings[j][i] - average, 2); + } + } + + standardDeviation = sqrt(standardDeviation / (sensorNumber * slidingWindow)); + for(int i = 0; i < sensorNumber; i++){ + for(int j = 0; j < slidingWindow; j++){ + if(readings[j][i] > average + 2.17 * standardDeviation || readings[j][i] < average - 2.17 * standardDeviation){ + outlierCount++; + faultySensors[i]++; + } + } + } + + int max = 0; + for(int i = 0; i < sensorNumber; i++){ + if(faultySensors[i] > max){ + max = faultySensors[i]; + } + } + + metrics.mean = average; + metrics.standardDeviation = standardDeviation; + metrics.possibleFaultySensor = max; + + return metrics; +} \ No newline at end of file diff --git a/Sources/DataAcquisition/Include/DataAcquisition.h b/Sources/DataAcquisition/Include/DataAcquisition.h new file mode 100644 index 0000000..9cf6c34 --- /dev/null +++ b/Sources/DataAcquisition/Include/DataAcquisition.h @@ -0,0 +1,39 @@ + +#include + +typedef struct { + float mean; + float standardDeviation; + int possibleFaultySensor; +} Metrics; + +typedef struct { + int sensorsNumber; + int slidingWindowSize; +} Matrix; + +void initializeReadings(int numSensors, float deltaTime); +bool freeReadings(); + +int getSensorsNumber(); +int getSlidingWindowSize(); +bool isFull(int sensorIndex); + +void addReading(float value); + +float getAverageOnSensor(int sensorIndex); +float getAverageOnAllSensors(); +float getOverallAverage(); + +float getStandardDeviationOnSensor(int sensorIndex); +float getStandardDeviationOnAllSensors(); +float getOverallStandardDeviation(); + +float getLastReading(int sensorIndex); + +void anomalyDetect(float average, float standardDeviation); + +int getOutlierCount(); + +Metrics getMetrics(float *readings[], int sensorNumber, int slidingWindow); +