/*------------------------------------------------------------------ Pedal - A Linux Exercise Bicycle Display $Id: pedal.c,v 1.15 2008/10/20 12:36:12 rader Exp $ cc -o pedal.bin -lcurses pedal.c cat<pedal xset -dpms xset s off exec xterm -title Pedal -geom 64x32+5+5 -bg black -fg green +sb -cr green \ -fn '-adobe-courier-bold-r-normal--34-240-100-100-m-200-iso8859-1' -e pedal.bin EOT chmod 755 pedal sudo cp pedal.bin /usr/local/bin/pedal.bin sudo cp pedal /usr/local/bin/pedal pedal! September 2008 steve rader ------------------------------------------------------------------*/ #include #include #include #include #define DEBUG 0 #define max_gear 9 #define min_gear 2 #define num_gears 8 #define initial_gear 8 #define min_interval 45 #define max_interval 150 #define interval_size 90 #define dsp_speed 2 #define TOTAL_TIME 2400 #define X_SIZE 9 #define Y_SIZE 2 #define BORDER 2 #define FUDGE_DOWN 4 #if DEBUG #define SLEEP 50000 #else #define SLEEP 1000000 #endif #define GREEN 1 #define BLUE 2 #define CYAN 3 struct datum { int gear; struct datum *next, *prev; }; int total_time = TOTAL_TIME; int future[1000000]; int future_size; /*----------------------------------------*/ int main() { int rows, cols, i, j, x, y, count, total; struct datum *ptr; struct datum *history_head, *history_tail; int history_size; int gear_printed; int elapsed, hr, min, sec; time_t start, rnow; struct tm *tnow; char buf[100]; /*----------------------------------------*/ /* initialize */ initscr(); clear(); start_color(); init_pair(GREEN,COLOR_GREEN,COLOR_BLACK); init_pair(BLUE,COLOR_BLUE,COLOR_BLACK); init_pair(CYAN,COLOR_CYAN,COLOR_BLACK); attron(COLOR_PAIR(GREEN)); getmaxyx(stdscr,rows,cols); start = time(NULL); srand((int)start); history_head = history_tail = NULL; history_size = 0; i = 0; count = 0; total = 0; /*----------------------------------------*/ /* prompts */ attron(COLOR_PAIR(CYAN)); time(&rnow); tnow = localtime(&rnow); if ( tnow->tm_hour == 0 ) { sprintf(buf,"12:%02dam",tnow->tm_min); } else if ( tnow->tm_hour == 12 ) { sprintf(buf,"12:%dpm",tnow->tm_min); } else if ( tnow->tm_hour > 12 ) { tnow->tm_hour -= 12; sprintf(buf,"%d:%02dpm",tnow->tm_hour,tnow->tm_min); } else { sprintf(buf,"%d:%02dam",tnow->tm_hour,tnow->tm_min); } mvprintw(2,4,"%s",buf); mvprintw(2,cols-12,"00:00:00"); attron(COLOR_PAIR(GREEN)); mvprintw(rows/2,cols/2-9,"Duration [40]: "); refresh(); getstr(buf); if ( buf[0] != '\0' ) { total_time = atoi(buf) * 60; } curs_set(0); mvprintw(rows/2,cols/2-11,"Hit any key to start ",total_time); refresh(); getch(); /*----------------------------------------*/ /* make the route */ make_route(); /*----------------------------------------*/ /* loop */ for(;;) { clear(); #if !(DEBUG) if ( elapsed % dsp_speed == 0 ) { #endif /*----------------------------------------*/ /* add data point */ ptr = malloc(sizeof(struct datum)); ptr->next = history_head; if ( history_head == NULL ) { ptr->prev = NULL; } else { history_head->prev = ptr; } ptr->gear = future[i]; history_head = ptr; if ( history_tail == NULL ) { history_tail = history_head; } history_size++; count++; total += history_head->gear; /*----------------------------------------*/ /* remove old data point */ if ( history_size > cols - (BORDER*2) ) { ptr = history_tail; history_tail = history_tail->prev; history_tail->next = NULL; free(ptr); } /*----------------------------------------*/ /* go to next point in future */ i++; if ( i > future_size ) { i = 0; } /* wrap */ #if !(DEBUG) } #endif /*----------------------------------------*/ /* print graph */ gear_printed = 0; ptr = history_head; x = cols - 1 - BORDER; while ( ptr != NULL ) { for (y = rows - 1 - BORDER; y > (ptr->gear * Y_SIZE) + FUDGE_DOWN; y--) { mvprintw(y,x,"#",ptr->gear); } if ( !gear_printed && (ptr->next == NULL || ptr->gear != ptr->next->gear) ) { attron(COLOR_PAIR(BLUE)); mvprintw(y,x,"%d",ptr->gear); attron(COLOR_PAIR(GREEN)); gear_printed = 1; } ptr = ptr->next; x--; } /*----------------------------------------*/ /* print labels */ attron(COLOR_PAIR(CYAN)); for ( j = 1; j <= max_gear; j++ ) { mvprintw( (j * Y_SIZE) + FUDGE_DOWN + 1, BORDER - 1,"%d",j); } /*----------------------------------------*/ /* print time */ time(&rnow); tnow = localtime(&rnow); if ( tnow->tm_hour == 0 ) { sprintf(buf,"12:%02dam",tnow->tm_min); } else if ( tnow->tm_hour == 12 ) { sprintf(buf,"12:%dpm",tnow->tm_min); } else if ( tnow->tm_hour > 12 ) { tnow->tm_hour -= 12; sprintf(buf,"%d:%02dpm",tnow->tm_hour,tnow->tm_min); } else { sprintf(buf,"%d:%02dam",tnow->tm_hour,tnow->tm_min); } mvprintw(2,4,"%s",buf); /*----------------------------------------*/ /* print ave gear */ sprintf(buf,"%.2f ave",(float)total/count); mvprintw(2,(int)(cols/2)-(strlen((char *)buf)/2),"%s",buf); /*----------------------------------------*/ /* print elapsed */ elapsed = rnow - start; hr = (int)(elapsed / 3600); min = (int)((elapsed - (hr * 3600)) / 60); sec = elapsed % 60; sprintf(buf,"%02d:%02d:%02d",hr,min,sec); mvprintw(2,cols-12,"%s",buf); attron(COLOR_PAIR(GREEN)); /*----------------------------------------*/ /* refresh & sleep */ move(rows - 1 - BORDER,cols - 1 - BORDER); refresh(); usleep(SLEEP); } } /*------------------------------------------------------------------*/ make_route() { time_t start; int intervals[1000]; int deltas[1000]; int random_deltas[1000]; int i, j, t, p, v, idx, prev; int num_peaks, num_deltas, cur_delta, cur_elapsed; float slope; float cur_gear, prev_gear, remainder; int int_gear, count, total; start = time(NULL); srand((int)start); //printf("expecting %d peaks\n\n", // total_time/(((max_interval-min_interval)/2)+min_interval)/2); /* make list of intervals */ i = 0; t = 0; while ( t < total_time ) { j = (rand() % interval_size) + 1 + min_interval; intervals[i] = j; //printf("interval %d: %d\n",i,j); t += j; i++; } num_peaks = i; //printf("\n"); /* make a bit bigger list of deltas */ p = 0; for ( i = 0 ; i < (num_peaks + 10); i += 2 ) { p = (int)(num_gears/2 + min_gear - 1); while ( p == (int)(num_gears/2 + min_gear - 1) ) { p = (rand() % num_gears) + min_gear; } v = max_gear - p + min_gear - 1; deltas[i] = p; deltas[i+1] = v; //printf("deltas %d: %d & %d\n",i,p,v); } num_deltas = i; //printf("\n"); /* pick random peaks and valleys */ prev = (int)(num_gears/2 + min_gear - 1); for ( i = 0; i < num_peaks; i++ ) { idx = rand() % num_deltas + 1; while ( deltas[idx] == -1 ) { idx = rand() % num_deltas + 1; } if ( prev < (num_gears/2 + min_gear - 1) ) { while ( deltas[idx] < (num_gears/2 + min_gear - 1) ) { idx = rand() % num_deltas + 1; while ( deltas[idx] == -1 ) { idx = rand() % num_deltas + 1; } } } else { while ( deltas[idx] > (num_gears/2 + min_gear - 1) ) { idx = rand() % num_deltas + 1; while ( deltas[idx] == -1 ) { idx = rand() % num_deltas + 1; } } } random_deltas[i] = deltas[idx]; if ( random_deltas[i] < min_gear ) { random_deltas[i] = min_gear; } //printf("random delta %d: using delta %d: %d\n",i,idx,random_deltas[i]); prev = deltas[idx]; deltas[idx] = -1; } //printf("\n"); /* make time series list */ count = 0; total = 0; cur_delta = 0; cur_elapsed = 0; cur_gear = initial_gear; prev_gear = initial_gear; for ( i = 0; i <= (total_time/dsp_speed); i++ ) { if ( cur_delta == 0 ) { slope = ((float)random_deltas[cur_delta]-(float)initial_gear)/(float)intervals[cur_delta]; //printf("0: go from %d to %d in %d seconds via a slope of %.9lf\n", // initial_gear, random_deltas[cur_delta],intervals[cur_delta], slope); } else { slope = ((float)random_deltas[cur_delta]-(float)random_deltas[cur_delta-1])/(float)intervals[cur_delta]; //printf("%d: go from %d to %d in %d seconds via a slope of %.9lf\n", // i, cur_gear, random_deltas[cur_delta],intervals[cur_delta], slope); } cur_gear = prev_gear + (slope*dsp_speed); if ( cur_elapsed >= intervals[cur_delta] ) { //printf("%d: new delta\n",i); cur_elapsed = 0; cur_delta++; if ( cur_gear != prev_gear ) { cur_gear = prev_gear; } } int_gear = (int)cur_gear; remainder = cur_gear - (int)cur_gear; if ( remainder > .5 ) { int_gear++; } if ( int_gear < min_gear ) { int_gear = min_gear; } if ( int_gear > max_gear ) { int_gear = max_gear; } future[i] = int_gear; //printf("%03d: %d (%f) ", i, int_gear, cur_gear); //for ( j = 0; j <= ((max_gear-int_gear)*4+4); j++ ) { printf("#"); } //printf("\n"); //usleep(20000); prev_gear = cur_gear; cur_elapsed += dsp_speed; total += (int)cur_gear; count++; } future_size = count - 1; }