ENERGY MONITOR 0.2
Loading...
Searching...
No Matches
signal.cpp
Go to the documentation of this file.
1#include "signal.h"
2#include <iostream>
3#define _USE_MATH_DEFINES
4#include <math.h>
5#include <cmath>
7#include "signal_operation.h"
8
9
10
11// EXPECTED DATA FORMAT FIRST COLUMN TIME SECOND COLUMN VALUES
12//FUTURE WORK SUBDIVIDE SIGNALS WHEN FREQUENCY CHANGE OR PHASE ANGLE CHANGES SUDDENLY
13//FOR SUB_DIVIDED ANALYSES FOR EACH PART
14
15/*
16 THIS PIECE OF CODE WAS PROVIDED BY : OOOO;
17*/
18
19
20
21bool signal::loadData(string name, string fileLocation){
22 //CONSTRUCTOR
23 file_IO importFile;
24 if(importFile.data_import(fileLocation+name, signal_data, csv)){
26 return true;
27 }else{
28 return false;
29 }
30}
31
33{
34 signal_data = _data;
35 return true;
36}
37
38bool signal::loadData(std::vector<double> time, std::vector<double> vals)
39{
40 if(time.size() != vals.size()){
41 std::cerr<< "TIME VECTOR SIZE DIFFERENT THAN VALUE VECTOR" << endl;
42 return false;
43 }else{
44 for( int idx = 0; idx < time.size(); idx++){
45 putValue(vals.at(idx) , idx , _val);
46 putValue(vals.at(idx) , idx , _time);
47 }
48 return true;
49 }
50}
51
53 return this->data_viable;
54}
55
56
57double signal::_dvBdt(double v1, double v3, double t1, double t2){
58 return (v1 - v3)/((t1 - t2)*2); //function for first derivative of discrete data
59}
60
61
62double signal::_dv2Bdt2(double v1, double v2, double v3,double t1, double t2){
63 return (v1 - 2*v2 + v3) / ((t1-t2)*(t1-t2)); //function for second derivative of discrete data
64}
65
66double signal::_vdt(double v1, double v2, double t1, double t2){
67 return (v1 + v2) * (t1 - t2) * 0.5; //RETURNS _AREA OF TRAPEZOID
68}
69
70
71
72
78{
79
80 this->data_viable = true;
81
82
83
84//CHECK IF THE ONCE PASSED TABLE HAS AT LEAST 2 COLUMNS for TIME and Values respectively
85 if(signal_data.get_col_num() < 2)
86 {
87 std::cerr << "THE CHOSEN DATA TABLE HAS AT LEAST A ROW MISSING" << endl;
88 return false;
89 }else if(signal_data.get_row_num() < 10){
90 std::cerr << "TOOO FEW SAMPLES PRE ANALYZER TERMINATE" << endl;
91 return false;
92 }else{
93
94
95
96 //START DOING BASIC ANALYTICS
97 unsigned int row_index = 0;
98 double current_val = getValue(row_index,_val);
99 double next_val = getValue(row_index + 1,_val);
100 double current_time = getValue(row_index,_time);
101 analytics.timeStart = current_time;
102 double max_amp = current_val;
103 double min_amp = current_val;
104
105 double vdt = 0;
106
107 double last_val = current_val;
108 double last_time = current_time;
109 //ENABLE THE DATA VIABLE Variable and if data is not disable it
110
111 //START FILLING COLUMNS FOR integration with respect to time , first derivative , second derivative;
112 for(row_index = 1; row_index < (signal_data.get_row_num() - 1); row_index++){
113 current_val = getValue(row_index,_val);
114 next_val = getValue(row_index + 1,_val);
115 current_time = getValue(row_index,_time);
116
117 if(last_time > current_time){
118 std::cerr << "CORRUPTED DATA " << row_index << endl;
119 data_viable = false;
120 return false;
121 }
122
123 double current_dvBdt = _dvBdt(last_val, next_val, last_time, current_time);
124 double current_dv2Bdt2 = _dv2Bdt2(last_val,current_val,next_val, current_time, last_time);
125
126 vdt += _vdt(last_val, current_val, current_time, last_time);
127
128 putValue(current_dvBdt,row_index,_first_deriv);
129 putValue(current_dv2Bdt2,row_index,_second_deriv);
130 putValue(vdt,row_index,_area);
131
132
133 last_val = current_val;
134 last_time = current_time;
135 }
136
137 current_val = getValue(row_index,_val);
138 current_time = getValue(row_index,_time);
139 analytics.samples_num = row_index + 1;
140
141 vdt += _vdt(last_val, current_val, current_time, last_time);
142 putValue(vdt,row_index,_area);
143
144 analytics.timeEnd = current_time;
145 analytics.avg_sample_time = (analytics.timeEnd - analytics.timeStart)/(analytics.samples_num - 1) ;
146 refreshData();
147 return true;
148 }
149}
150
151
152
154{
155
156 unsigned int index = 1;
157 int current_firstDeriv_sign = sign(getValue(index,_first_deriv));
158 int last_firstDeriv_sign = current_firstDeriv_sign;
159 //CLEAR ARRAY IF THEY HAD ANY DATA BEFORE SINCE WE ARE DOING PUSHBACKS
160 val_maximas.value.clear();
161 val_maximas.time.clear();
162 val_minimas.value.clear();
163 val_minimas.time.clear();
164 //ONLY UPDATE LAST DERIV SIGN WHEN SIGN CHANGES FROM NEGATIVE TO POSITIVE OR VICEVERSA
165 for(index = 2; index < (analytics.samples_num - 2); index++){
166
167 current_firstDeriv_sign = sign(getValue(index,_first_deriv));
168
169 if((last_firstDeriv_sign != current_firstDeriv_sign)){
170 //IF THE SLOPE CHANGE AS FOLLOW POSITIVE--> NEGATIVE/ZERO ** or NEGATIVE --> POSITIVE/ZERO
171 //IGNORE ZERO SLOPES for now
172 //WE ARE GOING TO GET THE BIGGEST OR THE SMALLEST point from the last change to this change
173 last_firstDeriv_sign = current_firstDeriv_sign;
174 if((last_firstDeriv_sign != zero)){
175 //WE IGNORE CHANGES FROM ZERO SLOPE TO A VALUED SLOPE BUT TAKE CHANGES FROM A VALUED SLOPE TO ZERO
176 if(current_firstDeriv_sign == positive){
177 //SLOPE was NEGATIVE SIGN AND CHANGED TO POSITIVE ------ MINIMA
178 int ridx = -1;
179 unsigned int i = index + ridx;
180 double localMinima = getValue(i,_val);
181 for(i; i < (index + 1); i++){
182 if(getValue(i,_val) < localMinima){
183 localMinima = getValue(i,_val);
184 ridx++;
185 }
186 }
187 val_minimas.value.push_back(localMinima);
188 val_minimas.time.push_back(getValue(index + ridx,_time));
189 }
190 else if(current_firstDeriv_sign == negative){
191 //SLOPE was POSITIVE SIGN AND CHANGED TO NEGATIVE -------- MAXIMA
192 //SLOPE was NEGATIVE SIGN AND CHANGED TO POSITIVE MINIMA
193 int ridx = -1;
194 unsigned int i = index + ridx;
195 double localMaxima = getValue(i,_val);
196 for(i; i < (index + 1); i++){
197 if(getValue(i,_val) > localMaxima){
198 localMaxima = getValue(i,_val);
199 ridx++;
200 }
201 }
202 val_maximas.value.push_back(localMaxima);
203 val_maximas.time.push_back(getValue(index + ridx,_time));
204 }
205
206 else if(current_firstDeriv_sign == zero){
207 //SLOPE WAS POSITIVE OR NEGATIVE THEN BECAME ZERO ACT ACCORDINGLY
208 int ridx = -1;
209 unsigned int i = index + ridx;
210 double localMinima = getValue(i,_val);
211 for(i; i < (index + 1); i++){
212 if(getValue(i,_val) < localMinima){
213 localMinima = getValue(i,_val);
214 ridx++;
215 }
216 }
217 val_minimas.value.push_back(localMinima);
218 val_minimas.time.push_back(getValue(index + ridx,_time));
219 }
220 else if(last_firstDeriv_sign == positive){
221 //LAST SLOPE WAS POSITIVE AND NOW ZERO (MAXIMA)
222 int ridx = -1;
223 unsigned int i = index + ridx;
224 double localMaxima = getValue(i,_val);
225 for(i; i < (index + 1); i++){
226 if(getValue(i,_val) > localMaxima){
227 localMaxima = getValue(i,_val);
228 ridx++;
229 }
230 val_maximas.value.push_back(localMaxima);
231 val_maximas.time.push_back(getValue(index + ridx,_time));
232 }
233 }
234 }
235 }
236 }
237 return true;
238 //END OF update_local_maximas_minimas
239}
240
241/*
242 THIS PIECE OF CODE WAS PROVIDED BY : OOOO;
243*/
244
246{
247 //NOW WE SHOULD HAVE two dataSets of local (maximum and minimum) values and their times respectively
248 //+add some analytics data top min/max vals and their times
250 // (analytics.min_val -- analytics.max_val)and their times -- analytics.avg_max -- analytics.avg_min
251 // ++ updating the local maximas and manimas to hold only the biggest maximas and minimas
252
253 analytics.min_val = val_minimas.value.at(0);
254 analytics.min_val_time = val_minimas.time.at(0);
255 for(int i = 1; i < val_minimas.value.size(); i++){
256 if(val_minimas.value.at(i) < analytics.min_val){
257 analytics.min_val = val_minimas.value.at(i);
258 analytics.min_val_time = val_minimas.time.at(i);
259 }
260 }
261
262 analytics.max_val = val_maximas.value.at(0);
263 analytics.max_val_time = val_maximas.time.at(0);
264 for(int i = 1; i < val_maximas.value.size(); i++){
265 if(val_maximas.value[i] > analytics.max_val){
266 analytics.max_val = val_maximas.value[i];
267 analytics.max_val_time = val_maximas.time[i];
268 }
269 }
270
271 //filter the maximas and minimas for top maximas and minimas only and other local one are ignored for now
272 /*WE ROUND THE DIFFERENCE BY A FACTOR TO (minima_rounding) COMPENSATE FOR VERY CLOSE TO ZERO VALUES
273 THEN COMPARE THE RATIO OF THE DIFFERENCE TO THE MIN_MAX TO ANOTHER FACTOR(min_max_accuracy) to compensate for offset sampling or low sampling rate
274 */
276 for(int i = 0; i < val_minimas.value.size();){
277 double diff = (val_minimas.value.at(i) - analytics.min_val);
278 if( abs( ( roundTo(diff, minima_diff_rounding)/analytics.min_val) ) > min_max_accuracy ) {
279 //erase minimas or maximas that are far than the smallest local minima by a certain factor
280 val_minimas.value.erase(val_minimas.value.begin() + i);
281 val_minimas.time.erase(val_minimas.time.begin() + i);
282 }else{
283 i++;
284 }
285 }
286
287 for(int i = 0; i < val_maximas.value.size();){
288 double diff = (val_maximas.value.at(i) - analytics.max_val);
289 if( abs( ( roundTo(diff, maxima_diff_rounding)/analytics.max_val) ) > min_max_accuracy ){
290 //erase maval_maximas or maximas that are far than the biggest local maxima by a certain factor
291 val_maximas.value.erase(val_maximas.value.begin() + i);
292 val_maximas.time.erase(val_maximas.time.begin() + i);
293 }else{
294 i++;
295 }
296 }
297 }
298 //CALL SHRINK TO FIT FOR THE VECTORS
299 val_maximas.value.shrink_to_fit();
300 val_maximas.time.shrink_to_fit();
301 val_minimas.value.shrink_to_fit();
302 val_minimas.time.shrink_to_fit();
303
304
305 double sum_maxes = 0;
306 for(int i = 0; i < val_maximas.value.size(); i++){
307 sum_maxes+=val_maximas.value[i];
308 }
309 analytics.avg_max_val = sum_maxes/val_maximas.value.size();
310
311 double sum_mins = 0;
312 for(int i = 0; i < val_minimas.value.size(); i++){
313 sum_mins+=val_minimas.value[i];
314 }
315 analytics.avg_min_val = sum_mins/val_minimas.value.size();
316
317
318 //get peak to peak data
319 size_t ptp_num = val_minimas.value.size();
320 if(val_maximas.value.size() < val_minimas.value.size()) ptp_num = val_maximas.value.size();
321 double max_ptp = 0;
322 double min_ptp = 0;
323 if(ptp_num > 0){
324 max_ptp = val_maximas.value.at(0) - val_minimas.value.at(0);
325 min_ptp = val_maximas.value.at(0) - val_minimas.value.at(0);
326
327 double sum_ptp = max_ptp;
328
329 for(int i = 1; i < ptp_num; i++){
330 double ptp = val_maximas.value[i] - val_minimas.value[i];
331 sum_ptp+=ptp;
332 if(ptp < min_ptp){
333 min_ptp = ptp;
334 }else if(ptp > max_ptp){
335 max_ptp = ptp;
336 }
337 }
338 analytics.max_ptp = max_ptp;
339 analytics.min_ptp = min_ptp;
340 analytics.avg_ptp = sum_ptp/ptp_num;
341 analytics.dc_offset = (analytics.avg_max_val + analytics.avg_min_val)/2;
342 }else{
343 std::cerr << "NOT ENOUGH PEAKS " << endl;
344 }
345 return true;
346 //END OF post_local_maximas_minimas
347}
348
350 size_t least_extrema_num = val_maximas.value.size() - 1;
351 if((val_minimas.value.size() - 1) < least_extrema_num) least_extrema_num = val_minimas.value.size() - 1;
352 double sumFrequency_maximaBased = 0;
353 double sumFrequency_minimaBased = 0;
354 std::vector<double> _minima_periods;
355 std::vector<double> _maxima_periods;
356 bool maxima_periodic;
357 bool minima_periodic;
358 for(int i = 0; i < least_extrema_num; i++){
359 double maxima_subPeriod = 1/(val_maximas.time.at(i + 1) - val_maximas.time.at(i));
360 sumFrequency_maximaBased += maxima_subPeriod;
361 _maxima_periods.push_back( (val_maximas.time.at(i + 1) - val_maximas.time.at(i)) );
362 }
363 double base_frequency_maximaBased = sumFrequency_maximaBased/(least_extrema_num);
364
365 for(int i = 0; i < least_extrema_num; i++){
366 double minima_subPeriod = 1/(val_minimas.time.at(i + 1) - val_minimas.time.at(i));
367 sumFrequency_minimaBased += minima_subPeriod;
368 _minima_periods.push_back( (val_minimas.time.at(i + 1) - val_minimas.time.at(i)) );
369 }
370
371 double base_frequency_minimaBased = sumFrequency_minimaBased/(least_extrema_num);
372
373 //CHECK that all maxima-to-maxima periods where equal
374 if(_maxima_periods.size() > 1){
375 //START by assuming it is periodic unless its not
376 maxima_periodic = true;
377 for(int i = 0; i < (_maxima_periods.size() - 1); i++){
378 if(!isNear(_maxima_periods.at(i), _maxima_periods.at(i + 1), period_diff_accuracy)){
379 maxima_periodic = false;
380 }
381 }
382 }else{
383 maxima_periodic = true;
384 }
385 if(_minima_periods.size() > 1){
386 minima_periodic = true;
387 for(int i = 0; i < (_minima_periods.size() - 1); i++){
388 if(!isNear(_minima_periods.at(i), _minima_periods.at(i + 1), period_diff_accuracy)){
389 minima_periodic = false;
390 }
391 }
392 }else{
393 minima_periodic = true;
394 }
395
396 //check that all minima-to-minima periods where equal
397
398 //DEDUCE IF THE FUNCTION IS PERIODIC IF time between maximas equals time between minimas and act accordingly
399 if(maxima_periodic && minima_periodic){
400 if( isNear(base_frequency_minimaBased, base_frequency_maximaBased, period_diff_accuracy) ){
401 analytics.base_frequency = (base_frequency_maximaBased + base_frequency_minimaBased)/2;
402 analytics.base_angular_frequency = analytics.base_frequency*M_PI*2;
403 analytics.periods_num = (analytics.timeEnd - analytics.timeStart)*analytics.base_frequency;
404 analytics.periodic_time = 1/analytics.base_frequency;
405 analytics.is_periodic = true;
406
407 unsigned int idx = 0;
408 double t_on = 0;
409 double t_off = 0;
410 double sumDuty = 0;
411 for(idx; idx < least_extrema_num; idx++){
412 t_on = val_minimas.time.at(idx+1) - val_minimas.time.at(idx);
413 t_off = val_maximas.time.at(idx+1) - val_maximas.time.at(idx);
414 sumDuty += t_on/(t_on + t_off);
415 }
416 analytics.duty_cycle = sumDuty/idx;
417 }
418 }else{
419 return false;
420 }
421 return true;
422
423
424}
425
427 double rise_trigger_time = 0;
428 double fall_trigger_time = 0;
429 double last_rise_trigger_time = 0;
430 double last_fall_trigger_time = 0;
431 unsigned int idx = 0;
432 double last_value = getValue(idx, _val);
433 idx++;
434 int count = 0;
435
436 rising_trigger_times.clear();
437 falling_trigger_times.clear();
438 falling_periods.clear();
439 rising_periods.clear();
440
441 for(idx ; idx < this->analytics.samples_num; idx++){
442 double current_value = getValue(idx, _val);
443
444 if((last_value <= _trigger_level) && (current_value > _trigger_level)){
445 rising_trigger_times.push_back(getValue(idx, _time) );
446 }
447 if((last_value >= _trigger_level) && (current_value < _trigger_level)){
448 falling_trigger_times.push_back(getValue(idx, _time) );
449 }
450 //NOW WE HAVE TWO VECTORS OF PERIODS FOR THE SIGNAL ONE RISING EDGE BASED AND THE OTHER FALLING EDGE
451
452 last_value = current_value;
453 }
454
455
456 double sum_rise_periods = 0;
457 double sum_fall_periods = 0;
458
459
460 unsigned int idx_periods = 0;
461
462 //WE START FROM INDEX 1 to compensate for the disrupted or shifted signal at first due to filtering similar stuff
463 for(idx_periods = 1; idx_periods < rising_trigger_times.size(); idx_periods++){
464 rising_periods.push_back( rising_trigger_times.at(idx_periods) - rising_trigger_times.at(idx_periods - 1) );
465 sum_rise_periods += rising_periods.at(idx_periods - 1);
466 }
467 double avg_rise_period = sum_rise_periods/(idx_periods - 1);
468
469 //WE START FROM INDEX 1 to compensate for the disrupted or shifted signal at first due to filtering similar stuff
470 for(idx_periods = 1; idx_periods < falling_trigger_times.size(); idx_periods++){
471 falling_periods.push_back( falling_trigger_times.at(idx_periods) - falling_trigger_times.at(idx_periods - 1) );
472 sum_fall_periods += falling_periods.at(idx_periods - 1);
473 }
474 double avg_fall_period = sum_fall_periods/(idx_periods - 1);
475 //For now we are going to get the frequency for this signal as a whole We may have to subdivide it later;
476 this->analytics.periodic_time = (avg_fall_period + avg_rise_period)/2 ;
477 this->analytics.base_frequency = 1/this->analytics.periodic_time;
478 this->analytics.base_angular_frequency = 2*M_PI*this->analytics.base_frequency;
479 this->analytics.periods_num = (this->analytics.timeEnd - this->analytics.timeStart) * this->analytics.base_frequency;
480
481 return true;
482}
483
485{
486 hysteresis hysteresisBlock = hysteresis(this->_hysteresis_low_threshold, this->_hysteresis_high_threshold, false);
487 unsigned int idx = 0;
488 bool current_hyster_state = hysteresisBlock.update_state(getValue(idx, _val));
489 bool last_hyster_state = current_hyster_state;
490 idx++;
491
492 rising_trigger_times.clear();
493 falling_trigger_times.clear();
494 falling_periods.clear();
495 rising_periods.clear();
496
497
498 for(idx; idx < this->analytics.samples_num; idx++){
499 current_hyster_state = hysteresisBlock.update_state(getValue(idx, _val));
500 if(last_hyster_state != current_hyster_state && current_hyster_state == true){
501 this->rising_trigger_times.push_back(getValue(idx, _time) );
502 }
503 last_hyster_state = current_hyster_state;
504 }
505
506 double periods_sum = 0;
507 unsigned int rise_idx = 1;
508 for(rise_idx = 1; rise_idx < this->rising_trigger_times.size(); rise_idx++){
509 this->rising_periods.push_back( rising_trigger_times.at(rise_idx) - rising_trigger_times.at(rise_idx - 1) );
510 periods_sum += rising_periods.at(rise_idx - 1);
511 }
512
513 this->analytics.periodic_time = periods_sum/(rise_idx - 1);
514 this->analytics.base_frequency = 1/analytics.periodic_time;
515 this->analytics.base_angular_frequency = analytics.base_frequency * M_PI * 2;
516 this->analytics.periods_num = (analytics.timeEnd - analytics.timeStart)*analytics.base_frequency;
517
518 return true;
519}
520
522 switch (frequency_calc_type){
523 case peakNdtrough:
524 return frequency_peakNdtrough();
525 break;
526
527 case triggerLevel:
528 return frequency_triggerLevel();
529 break;
530
533
534 default:
535 return false;
536 break;
537 }
538}
539
541{
542 double startTime_stamp = analytics.timeStart;
543 double endTime_stamp = 0;
544 //RMS and AVG per period or for the whole signal as a whole
546 endTime_stamp = analytics.periodic_time * floor(analytics.periods_num) + startTime_stamp ;
547 }else{
548 endTime_stamp = analytics.timeEnd;
549 }
550
551 unsigned int index = 0;
552 double currentTime = 0;
553 double lastTime = getValue(index,_time);
554 double currentVal = 0;
555 double lastVal = getValue(index,_val);
556 double vdt = 0;
557 double v2dt = 0;
558 index++;
559 //NOW WE GET THE DATA START INDEX AGAIN
560 for(index; (currentTime <= (endTime_stamp)) && (index < analytics.samples_num); index++){
561 currentTime = getValue(index,_time);
562 currentVal = getValue(index,_val);
563 vdt += _vdt(currentVal, lastVal, currentTime, lastTime);
564 v2dt += _vdt((currentVal*currentVal), (lastVal*lastVal), currentTime, lastTime);
565 lastTime = currentTime;
566 lastVal = currentVal;
567 }
568 analytics.avg = vdt/(endTime_stamp - startTime_stamp);
569 analytics.rms = sqrt( v2dt/(endTime_stamp - startTime_stamp) );
570 return true;
571}
572
574
575//CHECK FOR LOCAL AND MINIMUM MAXIMAS
578 //WE ARE GOING TO SET HYSTERESIS PARAMS AUTOMATICALLY BY THE FOLLOWING
579 //UPPER THRESHOLD = (MAX + MIN)/2 **MIDPOINT
580 //LOWER THRESHOLD = (MAX + MIN*2)/3 ENOUGH FOR MOST NOISE CANCELING
581 //double hyster_lower = (analytics.avg_max_val + analytics.avg_min_val * 2) / 3;
582 //double hyster_upper = (analytics.avg_max_val + analytics.avg_min_val) / 2;
583 //set_hysteresis(hyster_upper , hyster_lower);
586 return true;
587}
588
589
590
592{
593 //Remember we have edge(i) - edge(i - 1) time as the period
594 //time in rising_periods vector same for falling_periods vector
595 //now we are going to analyse any pattern and create subsignals when the periods time changes
596 //consecutive equal periods = subsignal ; transition time to a different consequetive equal periods = transient
597 //consecutive equal periods again = subsignal ;
598
599 unsigned int rise_idx = 1;
600 unsigned int pattern_idx = 0;
601
602 periods_pattern.clear();
603 if(this->rising_trigger_times.size() > 0){
604 periods_pattern.push_back(pattern(this->analytics.timeStart, 0));
605 }
606
607 unsigned int transients_count = 0;
608 unsigned int unique_periods_count = 0;
609
610 for(rise_idx; rise_idx < rising_periods.size(); rise_idx++){
611 if(!isNear(rising_periods.at(rise_idx),rising_periods.at(rise_idx - 1), period_diff_accuracy)){
612 //THE LAST PATTERN ENDED WE STORE ITS ENDING TIME
613 periods_pattern.at(pattern_idx).periodsCount++;
614 periods_pattern.at(pattern_idx).pattern_end_time = rising_trigger_times.at(rise_idx);
615 //WE START A NEW PERIODS PATTERN
616 periods_pattern.push_back(pattern(rising_trigger_times.at(rise_idx), 0) );
617 pattern_idx++;
618 }else{
619 periods_pattern.at(pattern_idx).periodsCount++;
620 }
621 }
622 periods_pattern.at(pattern_idx).periodsCount++;
623 periods_pattern.at(pattern_idx).pattern_end_time = this->analytics.timeEnd;
624
625 //CATEGORIZE THE SUBSIGNALS
626
627 for(int idx = 0; idx < periods_pattern.size(); idx++){
628 if(periods_pattern.at(idx).periodsCount >= minimum_periodic_periodNum){
630 unique_periods_count++;
631 }else{
633 transients_count++;
634 }
635 }
636
637 //CONNECT consecutive transients to make a single big transient
638 //USING A SIMPLE ALGORITHM WE CHECK OUR CONDITION AND IF THE LAST ELEMENT AND CURRENT ONE SATISFY IT
639 //COMBINE THE LAST ELEMENT AND CURRENT ONE UNIION THEIR TIMES AND DELETE THE CURRENT ONE
640
641 //NOW WE ARE GOING TO FILTER THE PATTERN AND CONCATENATE ONES WITH PERIODS SMALLER THAN A USER DEFINED NUMBER
642 //LETS CALL THEM TRANSIENTS FOR NOW DUE TO PHASE SHIFTS AND SUDDEN VALUE CHANGES
643 for(int idx = 1; idx < periods_pattern.size(); ){
644 if( (periods_pattern.at(idx).type == pattern_type::transient) && (periods_pattern.at(idx - 1).type == pattern_type::transient)){
645 periods_pattern.at(idx - 1).pattern_start_time = periods_pattern.at(idx - 1).pattern_start_time;
646 periods_pattern.at(idx - 1).pattern_end_time = periods_pattern.at(idx).pattern_end_time;
647 periods_pattern.at(idx - 1).periodsCount += periods_pattern.at(idx).periodsCount;
648 periods_pattern.erase( periods_pattern.begin() + idx);
649 transients_count--;
650 }else{
651 idx++;
652 }
653 }
654
655 subSignals_period_based.transients_count = transients_count;
656 subSignals_period_based.unique_periods_count = unique_periods_count;
657 return true;
658}
659/*-----------------------END OF period_pattern_analysis()---------------------------------*/
660
661
662
664{
667 }
668 return true;
669}
670
671
672
673
675
676 if(pre_analyze()){
677 //FUTURE WORK THAT INCLUDES TRANSFORM NON EQUALY TIME-SPACED discrete signal into equally time-spaced discrete signal using approximation techniques
678 soft_analyze();
679 timeDomain_analysed = true;
682
683 return true;
684 }
685 else{
686 return false;
687 }
688}
689
690
692{
693 return &analytics;
694}
695
697{
698 return &signal_data;
699}
700
701void signal::make_subsignals(std::vector<pattern> &pattern, _subSignals &sig)
702{
703 sig.subSignals.clear();
704 for(int i = 0; i < pattern.size(); i++){
705 _signal_operation operation;
706 sig.subSignals.push_back( signal() );
707 operation.subsignal_time_based(*this, sig.subSignals.at(i), pattern.at(i).pattern_start_time, pattern.at(i).pattern_end_time);
708 }
709}
710
711
716
721
722
723
724// EXPORT AS CSV OR BINARY COMPRESSED FORMATED
725bool signal::exportSignal(string name, bool export_all, sig_exp exportType, string fileLocation){
726 if(exportType == sig_exp::csv){
727 file_IO file;
728 refreshData();
729 if(export_all){
730 if(file.data_export((fileLocation+name), signal_data, csv)){
731 return true;
732 }
733 //if something bad happens when exporting
734 return false;
735 }else{
736 std::vector<double> time;
738 std::vector<double> values;
740 v_container wrap;
741 wrap.insertColumn(_time,time);
742 wrap.insertColumn(_val,values);
743 if(file.data_export((fileLocation+name), wrap, csv)){
744 return true;
745 }
746 //if something bad happens when exporting
747 return false;
748 }
749 }else if(exportType == sig_exp::sig){
750 std::ofstream file(fileLocation+name, std::ios::out | std::ios::binary);
751 if(file.is_open()){
752 if(!this->timeDomain_analysed)this->analyse();
753 double startTime = this->analytics.timeStart;
754 double endTime = this->analytics.timeEnd;
755 double t_sample = this->analytics.avg_sample_time;
756 double time_comp[] = {startTime , t_sample, endTime};
757
758 std::vector<double> values;
760 std::vector<float> down_scaled(values.begin(),values.end());
761 file.write(reinterpret_cast<const char*>(time_comp) ,sizeof(double)*3);
762 file.write(reinterpret_cast<const char*>(down_scaled.data()), values.size()*sizeof(float));
763 file.close();
764 return true;
765 }else{
766 return false;
767 }
768 }else{
769 return false;
770 }
771}
772
773
774
775//IMPORT A SIGNAL ENCODED IN A BINARY COMPRESSED FORMAT
776bool signal::importSignal(string name, string fileLocation)
777{
778 std::ifstream file(fileLocation+name, std::ios::binary);
779 if(file.is_open()){
780 //READ THE FIRST 3 doubles in the file as START_TIME - T_SAMPLING - END_TIME
781
782 //DETERMINE FILE SIZE
783 file.seekg(0, std::ios::end);
784 std::streampos fileSize = file.tellg();
785 file.seekg(0, std::ios::beg);
786 file.clear();
787 std::vector<char> file_data(fileSize);
788
789 file.read(reinterpret_cast<char*>(file_data.data()), fileSize);
790 double time_comp[3];
791 //READ FIRST 3 doubles in the file as startTime, samplingTime, endTime respectively
792
793 size_t file_idx = 0;
794 for(file_idx = 0; file_idx < 3*sizeof(double) ; file_idx+=8 ){
795 memcpy(&time_comp[file_idx/sizeof(double)], (file_data.data() + file_idx), sizeof(double));
796 }
797
798 std::vector<float> values;
799 //THE REST OF THE FILE IS FLOATS REPRESENTING VALUES
800 for(file_idx; file_idx < file_data.size() ; file_idx+= sizeof(float) ){
801 float value;
802 memcpy(&value, &file_data[file_idx], sizeof(float));
803 values.push_back(value);
804 }
805
806 double timeStart = time_comp[0];
807 double t_sampling = time_comp[1];
808 double timeEnd = time_comp[2];
809
810
811 std::vector<double> upscaled(values.begin(),values.end());
812 std::vector<double> time(values.size());
813 time.at(0) = timeStart;
814 for(unsigned int idx = 1; idx < values.size() ; idx++){
815 time.at(idx) = timeStart + idx*t_sampling;
816 }
817
818 this->signal_data.insertColumn(_val, upscaled);
819 this->signal_data.insertColumn(_time,time);
820
821 file.clear();
822 file.close();
823
824
825 return true;
826 }else{
827 return false;
828 }
829}
830
832{
833 _trigger_level = v;
834}
835
836
837void signal::set_hysteresis(double upThreshold, double lowThreshold){
838 this->_hysteresis_high_threshold = upThreshold;
839 this->_hysteresis_low_threshold = lowThreshold;
840}
841
842bool signal::pdf_export(std::string name, std::string file_address)
843{
844 pdf_wrap pdf_file;
845 string container;
846 //A lambda function that keeps making string and values pairs
847 auto data_val_pair = [&pdf_file](std::string a, double b){
848 pdf_file.setFontStyle(15,90,0,0,1);
849 pdf_file.addText(a);
850 pdf_file.setCursor(400, pdf_file.get_posY());
851 pdf_file.setFontStyle(15,0,0,0,1);
852 pdf_file.addText(to_string(b));
853 pdf_file.newLine();
854 };
855
856 // DISPLAY ALL KEY VALUE PAIRS OF A SIGNAl
857 auto signal_pdf_stream = [&pdf_file, &data_val_pair](signal &sig){
858 pdf_file.setCursor(LEFT(pdf_file.left_padding), TOP(pdf_file.top_padding - 80));
859 pdf_file.interline_gap = 25;
860
861 data_val_pair("TIME START :", sig.analytics.timeStart);
862 data_val_pair("TIME END :", sig.analytics.timeEnd);
863 data_val_pair("NUMBER OF PERIODS :", sig.analytics.periods_num);
864 data_val_pair("NUMBER OF SAMPLES :", sig.analytics.samples_num);
865 data_val_pair("AVG SAMPLING TIME :", sig.analytics.avg_sample_time);
866 data_val_pair("ABSOLUTE MAX :", sig.analytics.max_val);
867 data_val_pair("ABSOLUTE MIN :", sig.analytics.min_val);
868 data_val_pair("AVERAGE OF LOCAL MAXIMAS :", sig.analytics.avg_max_val);
869 data_val_pair("AVERAGE OF LOCAL MINIMAS :", sig.analytics.avg_min_val);
870 data_val_pair("AVERAGE :", sig.analytics.avg);
871 data_val_pair("RMS :", sig.analytics.rms);
872 data_val_pair("AVERAGE PERIODIC TIME :", sig.analytics.periodic_time);
873 data_val_pair("AVERAGE FREQUENCY :", sig.analytics.base_frequency);
874 };
875
876
877
878
879
880 pdf_file.init(file_address+name);
881
882 pdf_file.newPage();
883 /*PAGE HEADING*/
884 pdf_file.textPiece_start();
885 pdf_file.setFontStyle(30,60,60,60,1);
886 pdf_file.setCursor(LEFT(pdf_file.left_padding), TOP(pdf_file.top_padding - 30));
887 std::size_t pos = name.find('.');
888 // Check if the character is found
889 if (pos != std::string::npos) {
890 // Erase everything from the '%' to the end of the string
891 name.erase(pos);
892 }
893 pdf_file.center_text(name + " ANALYSIS");
894 pdf_file.textPiece_end();
895
896 /*PAGE DATA */
897 pdf_file.textPiece_start();
898 signal_pdf_stream(*this);
899 pdf_file.setCursor(LEFT(pdf_file.left_padding), TOP(pdf_file.get_posY() - 80));
900 data_val_pair("GENERATED SUBSIGNALS BASED ON PERIOD TIMES :", this->subSignals_period_based.transients_count + this->subSignals_period_based.unique_periods_count);
901 data_val_pair("TRANSIENTS :", this->subSignals_period_based.transients_count);
902 data_val_pair("UNIQUE PERIODS :", this->subSignals_period_based.unique_periods_count);
903 pdf_file.setCursor(LEFT(pdf_file.left_padding),100);
904 pdf_file.setFontStyle(7,50,20,50,1);
905 pdf_file.center_text("THIS PDF WAS GENERATED BY BIG_ANT OMAR MAGDY X_X");
906
907 pdf_file.textPiece_end();
908 pdf_file.endPage();
909
910
911
912
913
914 if(this->subSignals_period_based.subSignals.size() > 1){
915 for(int i = 0; i < this->subSignals_period_based.subSignals.size(); i++){
916 pdf_file.newPage();
917 pdf_file.textPiece_start();
918 pdf_file.setFontStyle(30,60,60,60,1);
919 pdf_file.setCursor(LEFT(pdf_file.left_padding), TOP(pdf_file.top_padding - 30));
920 std::string type;
921 if(this->periods_pattern.at(i).type == pattern_type::periodic){
922 type = "PERIODIC";
923 }else{
924 type = "TRANSIENT";
925 }
926 pdf_file.center_text(name + " SUBSIGNAL (" + to_string(i) + ") " + type);
927 pdf_file.textPiece_end();
928 pdf_file.textPiece_start();
929 signal_pdf_stream( this->subSignals_period_based.subSignals.at(i) );
930
931 pdf_file.setCursor(LEFT(pdf_file.left_padding),100);
932 pdf_file.setFontStyle(7,50,20,50,1);
933 pdf_file.center_text("THIS PDF WAS GENERATED BY BIG_ANT OMAR_MAGDY X_X");
934 pdf_file.textPiece_end();
935 pdf_file.endPage();
936 }
937 }
938 pdf_file.end();
939
940
941 return true;
942}
bool subsignal_time_based(signal &base_sig, signal &sub_sig, double time_start, double time_end)
create subSignal from bigger ones based on time boundaries
bool insertColumn(unsigned int columnNumber, const std::vector< HELD_DATA > &putArray)
inserts a column in the table replacing existing if they exist or adds them
Definition dataTable.h:153
unsigned int get_col_num()
Definition dataTable.h:41
unsigned int get_row_num()
Definition dataTable.h:38
bool extractColumn(unsigned int columnNumber, std::vector< HELD_DATA > &returnArray)
Extracts a row from the dataTable in a vector.
Definition dataTable.h:129
bool update_state(double value)
void init(std::string fileLocation)
Definition pdfWrapper.h:57
int get_posY()
Definition pdfWrapper.h:123
void newPage()
Definition pdfWrapper.h:66
void center_text(std::string word)
Definition pdfWrapper.h:127
void endPage()
Definition pdfWrapper.h:74
void setCursor(int x, int y)
Definition pdfWrapper.h:112
void textPiece_end()
Definition pdfWrapper.h:91
void addText(std::string text)
Definition pdfWrapper.h:85
int top_padding
Definition pdfWrapper.h:52
void end()
Definition pdfWrapper.h:62
int interline_gap
Definition pdfWrapper.h:54
void newLine()
Definition pdfWrapper.h:95
void setFontStyle(float size, int r, int g, int b, int a)
Definition pdfWrapper.h:101
void textPiece_start()
Definition pdfWrapper.h:79
int left_padding
Definition pdfWrapper.h:51
signal class the parent class for every other signal or any form of time-sorted (time,...
Definition signal.h:39
bool dataViable()
Definition signal.cpp:52
struct signal::_subSignals subSignals_period_based
bool exportSignal(std::string name, bool export_all=false, sig_exp expType=sig_exp::csv, std::string fileLocation=settings.get_setting("signal","export_path"))
export the signal data to a specific file
Definition signal.cpp:725
void make_subsignals(std::vector< pattern > &pattern, _subSignals &sig)
a function that keeps generating subsignals based on the pattern provided that holds pattern start ti...
Definition signal.cpp:701
double _vdt(double v1, double v2, double t1, double t2)
Definition signal.cpp:66
bool frequency_triggerHysteresis()
calculate the base_frequency with hysteresis added for noise ignorance
Definition signal.cpp:484
bool smaller_extremas_ignored
Definition signal.h:131
bool deduce_avg_rms()
rms and avg based on integer number of signals analysis only
Definition signal.cpp:540
bool timeDomain_analysed
Definition signal.h:250
maximas_minimas val_maximas
Definition signal.h:152
maximas_minimas val_minimas
Definition signal.h:153
bool data_viable
Definition signal.h:251
int frequency_calc_type
Definition signal.h:134
bool period_pattern_analysis()
analyse regions of the signal basesd on their periodic times (connected similar periodic times = 1 co...
Definition signal.cpp:591
const _analytics * get_analytics() const
get analytics of the time_domain analysed signal
Definition signal.cpp:691
double _hysteresis_high_threshold
Definition signal.h:194
bool deduce_baseFrequency()
deduce base frequency + angular + number of periods for the signal
Definition signal.cpp:521
int minimum_periodic_periodNum
Definition signal.h:196
double period_diff_accuracy
Definition signal.h:132
void refreshData()
refresh the dataTable that we use
Definition signal.h:122
std::vector< double > rising_trigger_times
rising edges detected are stored here mainly used by trigger level frequency calculation and hysteres...
Definition signal.h:157
double maxima_diff_rounding
Definition signal.h:129
bool frequency_triggerLevel()
calculate frequency based on crossing trigger level times
Definition signal.cpp:426
bool update_local_maximas_minimas()
EVALUATE MAXIMAS/MINIMAS using SLOPE DATA.
Definition signal.cpp:153
bool pdf_export(std::string name, std::string file_address=settings.get_setting("signal","export_path"))
export valuable signal report (images and text)//in the pdf format
Definition signal.cpp:842
_subSignals * subSignal_periodBased()
get the subSignals generated from this signal based on their periodic times pattern
Definition signal.cpp:712
double min_max_accuracy
Definition signal.h:128
bool pattern_analyze()
analyse changes in the signal and store any continous patterns detected for future interval Based sig...
Definition signal.cpp:663
const v_container * get_signal_data() const
get any values in the signal data table
Definition signal.cpp:696
_subSignals * subSignal_valueBased()
get the subSignals generated from this signal based on their rms for periods times pattern
Definition signal.cpp:717
bool frequency_peakNdtrough()
calculate frequency based on Local maximas and minimas and their times respectively
Definition signal.cpp:349
std::vector< pattern > periods_pattern
Definition signal.h:183
double minima_diff_rounding
Definition signal.h:130
struct signal::_subSignals subSignals_value_based
void putValue(double val, int row, int col)
easy abstracted values insersion
Definition signal.h:114
void set_hysteresis(double upThreshold, double lowThreshold)
sets the hysteresis parameters for the hysteresis trigger frequency calculations
Definition signal.cpp:837
bool pre_analyze()
Makes variable data out of the time-value data such as slopes and areas wrt to time.
Definition signal.cpp:77
bool soft_analyze()
soft time tomain analysis
Definition signal.cpp:573
@ triggerLevel
Definition signal.h:139
@ peakNdtrough
Definition signal.h:140
@ triggerHysteresis
Definition signal.h:141
bool loadData(std::string name, std::string fileLocation=settings.get_setting("signal","import_path"))
load data from a file directly if it has the following format (time,value)
double _dvBdt(double v1, double v2, double t1, double t2)
Definition signal.cpp:57
std::vector< double > falling_trigger_times
rising edges detected are stored here mainly used by trigger level frequency calculation and hysteres...
Definition signal.h:159
void set_trigger_level(double v)
sets the trigger level for the trigger level based frequency calculation
Definition signal.cpp:831
bool analyse()
generalized huge analysis in the time domain fetches basic data
Definition signal.cpp:674
bool post_local_maximas_minimas()
filter local MAXIMAS and MINIMAS and update ptp data (only top maximas and lowest minimas)
Definition signal.cpp:245
double _dv2Bdt2(double v1, double v2, double v3, double t1, double t2)
Definition signal.cpp:62
bool importSignal(std::string name, std::string fileLocation=settings.get_setting("signal","import_path"))
import a signal in the .sig binary format
Definition signal.cpp:776
std::vector< double > falling_periods
rising edges detected are stored here mainly used by trigger level frequency calculation and hysteres...
Definition signal.h:163
double getValue(int row, int col)
easy abstracted values extraction
Definition signal.h:118
bool periodic_avg_rms
Definition signal.h:133
std::vector< double > rising_periods
rising edges detected are stored here mainly used by trigger level frequency calculation and hysteres...
Definition signal.h:161
double _hysteresis_low_threshold
Definition signal.h:195
double _trigger_level
signal value for detecting edges and calculating frequency based on
Definition signal.h:193
v_container signal_data
Definition signal.h:151
struct signal::_analytics analytics
#define zero
Definition core.h:26
bool isNear(double v1, double v2, double acc)
Definition core.h:136
t roundTo(t num, t n)
Definition core.h:145
#define negative
Definition core.h:27
#define sign(c)
Definition core.h:28
#define positive
Definition core.h:25
#define TOP(n)
Definition pdfWrapper.h:30
#define LEFT(n)
Definition pdfWrapper.h:29
this file includes the base class "signal" for signals modeling and analysing thier time-domain / fre...
sig_exp
Definition signal.h:9
@ _first_deriv
Definition signal.h:31
@ _second_deriv
Definition signal.h:32
@ _area
Definition signal.h:33
@ _val
Definition signal.h:30
@ _time
Definition signal.h:29
this file includes the basic signal_operation class responsible for doing any required manipulation o...
a basic structure that holds basic analysis results
bool data_import(string file_address, dataTable< double > &data, int type)
a function that imports files with supported file formats
Definition data_IO.cpp:84
bool data_export(string file_address, dataTable< double > &data, int type)
Definition data_IO.cpp:96
unsigned int transients_count
Definition signal.h:187
unsigned int unique_periods_count
Definition signal.h:188
std::vector< signal > subSignals
Definition signal.h:186
std::vector< double > value
Definition signal.h:147
std::vector< double > time
Definition signal.h:148
double pattern_end_time
Definition signal.h:174
double pattern_start_time
Definition signal.h:173