#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; float average = 0; float 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 j = 0; j < slidingWindow; j++) { for (int i = 0; i < sensorNumber; i++) { sum += readings[i][j]; } } average = sum / (sensorNumber * slidingWindow); for (int j = 0; j < slidingWindow; j++) { for (int i = 0; i < sensorNumber; i++) { standardDeviation += pow(readings[i][j] - average, 2); } } standardDeviation = sqrt(standardDeviation / (sensorNumber * slidingWindow)); for (int j = 0; j < slidingWindow; j++) { for (int i = 0; i < sensorNumber; i++) { if (readings[i][j] > average + 2.17 * standardDeviation || readings[i][j] < average - 2.17 * standardDeviation) { outlierCount++; faultySensors[i]++; } } } int faultySensorsCount = 0; for(int i = 0; i < sensorNumber; i++) { if(faultySensors[i] >= 0.001 * slidingWindow) { faultySensorsCount++; } } int *possiblyFaultySensors = (int *)malloc(faultySensorsCount * sizeof(int)); int j = 0; for (int i = 0; i < faultySensorsCount; i++) { if (faultySensors[i] >= 0.001 * slidingWindow) { possiblyFaultySensors[j] = i; } } metrics.mean = average; metrics.standardDeviation = standardDeviation; metrics.possibleFaultySensor = possiblyFaultySensors; free(faultySensors); return metrics; }