laundrysorcery.c (4343B)
1 2 #include <stdio.h> 3 #include <wiringPi.h> 4 #include <time.h> 5 #include <rrd.h> 6 #include <math.h> 7 8 typedef struct { 9 double mean; 10 double variance; 11 unsigned int measurements; 12 } Gaussian; 13 14 15 void update_gaussian_lambda(Gaussian *g, double value, double lambda){ 16 g->mean=g->mean*lambda+(1-lambda)*value; 17 g->variance=g->variance*lambda+(1-lambda)*value*value; 18 unsigned int tmp=g->measurements++; 19 if(tmp > g->measurements){ 20 g->measurements=tmp; 21 } 22 } 23 24 #define MIN_MEASURES 100 25 26 double calculate_lambda(Gaussian *g){ 27 double lambda=0.75+0.25/MIN_MEASURES*g->measurements; 28 lambda=fmin(lambda,0.999995); 29 return lambda; 30 } 31 32 void update_gaussian(Gaussian *g, double value){ 33 update_gaussian_lambda(g,value,calculate_lambda(g)); 34 } 35 36 37 double get_sigma(Gaussian *g){ 38 return (g->variance - (g->mean * g->mean)); 39 } 40 41 double get_stdev(Gaussian *g){ 42 return sqrt(get_sigma(g)); 43 } 44 45 Gaussian g_on,g_off; 46 int on=0; 47 Gaussian *current=&g_off; 48 struct timespec timestamp; 49 double running_mean; 50 51 char *on_file; 52 char *log_file; 53 54 void write_log_file(char *term){ 55 if(!log_file) 56 return; 57 FILE *f=fopen(log_file, "a"); 58 if(!f){ 59 perror("fopen of log file"); 60 return; 61 } 62 fprintf(f, "%lu%s", time(NULL), term); 63 fclose(f); 64 } 65 66 void write_on_file(char *prefix){ 67 FILE *f=fopen(on_file, "w"); 68 if(!f){ 69 perror("fopen of on_file"); 70 } 71 fprintf(f, "%s%lu\n",prefix, time(NULL)); 72 fclose(f); 73 } 74 75 void turn_on(void){ 76 write_on_file(""); 77 write_log_file(" "); 78 } 79 80 void turn_off(void){ 81 write_on_file("-"); 82 write_log_file("\n"); 83 } 84 85 void toggle_on_off(void){ 86 if(on){ 87 on=0; 88 current=&g_off; 89 turn_off(); 90 } 91 else{ 92 on=1; 93 current=&g_on; 94 turn_on(); 95 } 96 } 97 98 int guess_is_on(double delta_t){ 99 if(g_on.measurements==0){ 100 return fabs(delta_t-current->mean)>5*get_stdev(current); 101 } 102 else{ 103 //we assume that sigma_on == sigma_off here, to make the calculation simpler 104 if(fabs(delta_t-g_on.mean)>fabs(delta_t-g_off.mean)){ // closer to "off" 105 return 0; 106 } 107 else{ 108 return 1; 109 } 110 } 111 } 112 113 int guess_if_toggle(double delta_t){ 114 return guess_is_on(delta_t)^on; 115 } 116 117 void process_datapoint(double delta_t){ 118 static unsigned int consecutive_outliers=0; 119 static Gaussian tmp={0,0,0}; 120 running_mean=(running_mean+delta_t)/2; 121 if(current->measurements>MIN_MEASURES){ 122 if(guess_if_toggle(delta_t)){ 123 if(consecutive_outliers == 0){ 124 tmp=*current; 125 } 126 if(consecutive_outliers++>100){ 127 toggle_on_off(); 128 consecutive_outliers=0; 129 } 130 update_gaussian(&tmp,delta_t); 131 return; 132 } 133 else{ 134 if(consecutive_outliers>0){ 135 *current=tmp; 136 consecutive_outliers=0; 137 } 138 } 139 } 140 update_gaussian(current,delta_t); 141 } 142 143 void falling_edge(void) { 144 clock_gettime(CLOCK_MONOTONIC, ×tamp); 145 digitalWrite (0, HIGH); 146 } 147 148 void rising_edge(void) { 149 struct timespec timetemp; 150 clock_gettime(CLOCK_MONOTONIC, &timetemp); 151 double delta_t = (double)(timetemp.tv_sec - timestamp.tv_sec) * 1.0e6 + 152 (double)(timetemp.tv_nsec - timestamp.tv_nsec)*1e-3; 153 digitalWrite (0, LOW); 154 static unsigned int measurements=0; 155 unsigned int tmp=measurements++; 156 if(tmp>measurements) measurements=tmp; 157 if(measurements>50){ 158 process_datapoint(delta_t); 159 } 160 } 161 162 163 164 void edge(void) { 165 if(digitalRead(1)==0) 166 falling_edge(); 167 else 168 rising_edge(); 169 } 170 171 unsigned long microseconds_since(struct timespec ts){ 172 struct timespec now; 173 clock_gettime(CLOCK_MONOTONIC, &now); 174 return (unsigned long)(now.tv_sec - ts.tv_sec)*1000000 + (unsigned long)(now.tv_nsec - ts.tv_nsec)/1000; 175 } 176 177 178 int main(int argc, char **argv) 179 { 180 wiringPiSetup () ; 181 pinMode (0, OUTPUT) ; 182 pinMode (1, INPUT) ; 183 if(!(argc == 2 || argc == 3)){ 184 printf("Usage: %s </path/to/onfile> [/path/to/log_file]\n", argv[0]); 185 return 0; 186 } 187 on_file=argv[1]; 188 if(argc == 3){ 189 log_file=argv[2]; 190 } 191 192 digitalWrite (0, LOW); 193 delay(5); 194 digitalWrite (0, HIGH); 195 196 turn_off(); 197 198 char buffer[512]; 199 const char* arr[]={buffer}; 200 struct timespec secondtimer; 201 clock_gettime(CLOCK_MONOTONIC, &secondtimer); 202 while(1) { 203 while(digitalRead(1)==0){ 204 ; 205 } 206 rising_edge(); 207 while(digitalRead(1)==1){ 208 ; 209 } 210 falling_edge(); 211 if(microseconds_since(secondtimer)>1000000){ 212 snprintf(buffer,sizeof(buffer),"%lu:%e:%e:%e:%e:%e:%d",(unsigned long)time(NULL),running_mean,g_off.mean,g_off.variance,g_on.mean,g_on.variance,on); 213 rrd_update_r("database.rrd", NULL, 1, arr); 214 clock_gettime(CLOCK_MONOTONIC, &secondtimer); 215 } 216 } 217 return 0; 218 } 219