//-------------------------------------------------------// // Project Code : V2H6K01-23-ENEMAN // File Name : RoutineTsamp.cpp // Created on : 2023. 8. 8. // Description : // Author : KimJeongWoo // Last modified Date : //-------------------------------------------------------// #include "Cpu1DeviceDefine.h" #include // std::accumulate와 std::inner_product를 위해 필요 #include #include // For sqrt() and std::abs() int Testpcs4 = 0; int Adc_BUTTON = 0; int Chk_Botton = 0; int Chk_Botton_pre = 0; // 순환 버퍼 #define BUFFER_SIZE 10 #include // For sqrt() and std::abs() // 10 이상 커지면 순간적인 변화를 계산하지 못함? int currentIndex = 0; int UV_buffer[BUFFER_SIZE]; int VIS_buffer[BUFFER_SIZE]; int IR_buffer[BUFFER_SIZE]; // 이전 센서 값 저장을 위한 변수 초기화 int CH4_UV_prev = 0, CH4_VIS_prev = 0, CH4_IR_prev = 0; // 변화량을 저장할 배열 int UV_change[BUFFER_SIZE], VIS_change[BUFFER_SIZE], IR_change[BUFFER_SIZE]; int prev_avg = 0; ////////////////////////////////////////////////////////// // 큐 구조체 정의 typedef struct { int items[BUFFER_SIZE]; int front; int rear; int size; double total; // 총합을 저장하기 위한 변수 int lastValue; // 마지막으로 추가된 값 } Queue; // 큐 초기화 함수 void initQueue(Queue *q) { q->front = 0; q->rear = -1; q->size = 0; q->total = 0; q->lastValue = 0; // 초기화 시 마지막 값은 0으로 가정 memset(q->items, 0, sizeof(q->items)); } // 큐에 데이터 추가 및 누적 계산 수행 함수 void enqueueAndUpdateStats(Queue *q, int value) { // 이전 값 저장 int prevValue = (q->size > 0) ? q->items[q->rear] : 0; // 큐에 데이터 추가 if (q->size == BUFFER_SIZE) { // 큐가 꽉 찼을 때, 가장 오래된 데이터를 빼고 새 데이터 추가 q->total -= q->items[q->front]; // 총합에서 가장 오래된 데이터 제거 q->front = (q->front + 1) % BUFFER_SIZE; } else { q->size++; } q->rear = (q->rear + 1) % BUFFER_SIZE; q->items[q->rear] = value; q->total += value; // 새 값 추가로 총합 업데이트 // 마지막 값 업데이트 q->lastValue = value; // 평균 및 변화량 계산은 필요에 따라 진행 } // 큐에서 데이터 제거 함수 (이 예제에서는 사용하지 않음) int dequeue(Queue* q) { if (q->size == 0) { // printf("Queue is empty\n"); return -1; } else { int item = q->items[q->front]; q->front = (q->front + 1) % BUFFER_SIZE; q->size--; return item; } } //////////////////////////////////////////////////////// // 큐 내 데이터의 평균을 계산하는 함수 double calculateAverage(Queue* q) { int sum = 0; int i = 0; if (q->size > 0) { int idx = (q->front + i) % BUFFER_SIZE; sum += q->items[idx]; i++; } return q->size > 0 ? (double)sum / q->size : 0; } // 큐 내 모든 데이터의 평균 변화량을 계산하는 함수 double calculateAverageChange(Queue* q) { if (q->size < 2) { // 큐에 데이터가 2개 미만일 때는 평균 변화량을 계산할 수 없음 return 0.0; } int currentIdx = q->front; int nextIdx = (currentIdx + 1) % BUFFER_SIZE; int changeSum = q->items[nextIdx] - q->items[currentIdx]; int count = 1; while (nextIdx != q->rear) { currentIdx = nextIdx; nextIdx = (nextIdx + 1) % BUFFER_SIZE; int currentChange = q->items[nextIdx] - q->items[currentIdx]; changeSum += currentChange; count++; } // 평균 변화량 계산 return (double)changeSum / count; } //////////////////////////////////////////////////////////////////////////// // 상관 계수를 계산하는 함수 double calculate_correlation(const int* x, const int* y, int n) { long long sum_x = std::accumulate(x, x + n, 0LL); long long sum_y = std::accumulate(y, y + n, 0LL); long long sum_x2 = std::inner_product(x, x + n, x, 0LL); long long sum_y2 = std::inner_product(y, y + n, y, 0LL); long long sum_xy = std::inner_product(x, x + n, y, 0LL); double numerator = (double)n * sum_xy - sum_x * sum_y; double denominator = sqrt(((double)n * sum_x2 - sum_x * sum_x) * ((double)n * sum_y2 - sum_y * sum_y)); if (denominator == 0) return 0; // 분모가 0이면, 상관관계를 계산할 수 없음 return numerator / denominator; } double calculateCorrelationFromQueues(Queue* q1, Queue* q2) { if (q1->size != q2->size || q1->size < 2) { // 큐의 크기가 다르거나 데이터 포인트가 2개 미만이면 계산할 수 없음 return 0; } int n = q1->size; long long sumX = 0, sumY = 0, sumX2 = 0, sumY2 = 0, sumXY = 0; for (int i = 0; i < n; ++i) { int idx1 = (q1->front + i) % BUFFER_SIZE; int idx2 = (q2->front + i) % BUFFER_SIZE; int x = q1->items[idx1]; int y = q2->items[idx2]; sumX += x; sumY += y; sumX2 += x * x; sumY2 += y * y; sumXY += x * y; } double numerator = n * sumXY - sumX * sumY; // 상관 계수를 계산하는 함수 내에서 sqrt 사용 시 타입 명확히 하기 double denominator = sqrt(static_cast((n * sumX2 - sumX * sumX) * (n * sumY2 - sumY * sumY))); if (denominator == 0) { // 분모가 0이면, 상관관계를 계산할 수 없음 return 0; } return numerator / denominator; } // 변화량의 크기를 계산하는 함수 double calculate_change_magnitude(const int* change_array, int n) { double sum = 0.0; for (int i = 0; i < n; i++) { sum += std::abs(change_array[i]); // 변화량의 절대값을 더함 } return sum / n; // 평균을 반환 } double calculateChangeMagnitudeFromQueue(Queue* q) { if (q->size < 2) { // 큐에 데이터가 2개 미만일 때는 변화량을 계산할 수 없음 return 0.0; } double changeSum = 0; int prev = q->items[q->front]; // 시작 요소를 이전 요소로 설정 for (int i = 1; i < q->size; ++i) { int idx = (q->front + i) % BUFFER_SIZE; // 순환 인덱싱 int current = q->items[idx]; // 현재 요소 changeSum += std::abs(current - prev); // 이전 요소와의 차이(변화량)의 절대값 더하기 prev = current; // 현재 요소를 다음 루프의 이전 요소로 설정 } return changeSum / (q->size - 1); // 변화량의 평균 반환 } void calculateAverageStddevAndAdaptiveThresholdFromQueue(Queue* q, double& avg, double& stddev, double& adaptive_threshold) { if (q->size == 0) { // 큐가 비어있으면 계산 불가 avg = 0; stddev = 0; adaptive_threshold = 0; return; } // 평균 계산 double sum = 0; for (int i = 0; i < q->size; ++i) { int idx = (q->front + i) % BUFFER_SIZE; sum += q->items[idx]; } avg = sum / q->size; // 표준 편차 계산 double sum_sq_diff = 0; for (int i = 0; i < q->size; ++i) { int idx = (q->front + i) % BUFFER_SIZE; double diff = q->items[idx] - avg; sum_sq_diff += diff * diff; } stddev = sqrt(sum_sq_diff / q->size); // 적응형 임계값 설정 adaptive_threshold = avg + 2 * stddev; } ///////////////////////////////////////////////////////////////////////////////////// void RoutineTsamp() { static Queue uvQueue; static Queue visQueue; static Queue irQueue; static int initialized = 0; if (!initialized) { initQueue(&uvQueue); initQueue(&visQueue); initQueue(&irQueue); initialized = 1; } Lpf1stRun(); // if (Testpcs4 == 1) // { // EepromWriteAdcScale(); // Testpcs4 = 0; // } // if (Testpcs4 == 2) // { // EepromReadAdcScale(); // Testpcs4 = 0; // } ModbusACheckBuffer(); // 새 센서 값 추가 enqueueAndUpdateStats(&uvQueue, CH4_UV_Flt); enqueueAndUpdateStats(&visQueue, CH4_VIS_Flt); enqueueAndUpdateStats(&irQueue, CH4_IR_Flt); // ///////////////////////////////////////////// // UV_buffer[currentIndex] = CH4_UV_Flt; // 배열에 센싱값 저장 // VIS_buffer[currentIndex] = CH4_VIS_Flt; // 배열에 센싱값 저장 // IR_buffer[currentIndex] = CH4_IR_Flt; // 배열에 센싱값 저장 // // // 센서 값 갱신 및 변화량 계산 // UV_change[currentIndex] = CH4_UV_Flt - CH4_UV_prev; // UV 변화량 계산 // VIS_change[currentIndex] = CH4_VIS_Flt - CH4_VIS_prev; // VIS 변화량 계산 // IR_change[currentIndex] = CH4_IR_Flt - CH4_IR_prev; // IR 변화량 계산 // // // 현재 값을 '이전 값'으로 저장 // CH4_UV_prev = CH4_UV_Flt; // CH4_VIS_prev = CH4_VIS_Flt; // CH4_IR_prev = CH4_IR_Flt; // // currentIndex = (currentIndex + 1) % BUFFER_SIZE; // 인덱스 갱신 if (CH4_UV_Flt > CH4_UV_max) CH4_UV_max = CH4_UV_Flt; // 새로운 최대값 발견 시 갱신 if (CH4_VIS_Flt > CH4_VIS_max) CH4_VIS_max = CH4_VIS_Flt; // 새로운 최대값 발견 시 갱신 if (CH4_IR_Flt > CH4_IR_max) CH4_IR_max = CH4_IR_Flt; // 새로운 최대값 발견 시 갱신 FaultChecker(); Chk_Botton = Din.Data.bit.Button; if ((Chk_Botton_pre == 0) && (Chk_Botton == 1)) { FaultReset = 1; } else { FaultReset = 0; } Chk_Botton_pre = Chk_Botton; // // 임계값을 넘었을 때만 계산 // if (SystemFault == 1) { // uv_vis_correlation = calculate_correlation(UV_buffer, VIS_buffer, BUFFER_SIZE); // uv_ir_correlation = calculate_correlation(UV_buffer, IR_buffer, BUFFER_SIZE); // vis_ir_correlation = calculate_correlation(VIS_buffer, IR_buffer, BUFFER_SIZE); uv_vis_correlation = calculateCorrelationFromQueues(&uvQueue, &visQueue); uv_ir_correlation = calculateCorrelationFromQueues(&uvQueue, &irQueue); vis_ir_correlation = calculateCorrelationFromQueues(&visQueue, &irQueue); // 변화량의 크기 계산 // uv_change_magnitude = calculate_change_magnitude(UV_change, BUFFER_SIZE); // vis_change_magnitude = calculate_change_magnitude(VIS_change, BUFFER_SIZE); // ir_change_magnitude = calculate_change_magnitude(IR_change, BUFFER_SIZE); uv_change_magnitude = calculateChangeMagnitudeFromQueue(&uvQueue); vis_change_magnitude = calculateChangeMagnitudeFromQueue(&visQueue); ir_change_magnitude = calculateChangeMagnitudeFromQueue(&irQueue); // UV 평균과 표준 편차 계산 // double sum = 0, sum_sq_diff = 0; // for (int i = 0; i < BUFFER_SIZE; ++i) { // sum += UV_buffer[i]; // sum_sq_diff += (UV_buffer[i] - (sum / BUFFER_SIZE)) * (UV_buffer[i] - (sum / BUFFER_SIZE)); // } // double avg = sum / BUFFER_SIZE; // stddev = sqrt(sum_sq_diff / BUFFER_SIZE); // adaptive_threshold = avg + 2 * stddev; calculateAverageStddevAndAdaptiveThresholdFromQueue(&uvQueue, avg, stddev, adaptive_threshold); // 최신 값과 바로 이전 값 사이의 변화율 계산을 위한 인덱스 계산 // int latest_index = (currentIndex - 1 + BUFFER_SIZE) % BUFFER_SIZE; // int prev_index = (latest_index - 1 + BUFFER_SIZE) % BUFFER_SIZE; // latest_change = UV_buffer[latest_index] - UV_buffer[prev_index]; // latest_change = avg - prev_avg; // prev_avg = avg; // if (abs(latest_change) > adaptive_threshold) { // // 변화율이 적응형 임계값을 초과하는 경우 처리 // // 예: 알림 발송, 로깅, 경고 등 // SET_FT_UV_Level = adaptive_threshold; // } // } }