LaundrySorcery

git clone git://xatko.vsos.ethz.ch/LaundrySorcery.git
Log | Files | Refs

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, &timestamp);
    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