/* * Calculate the gear ratios and shifting patterns for bicycles. * * From: speed@etecnw.com (Paul W. Laudon x7241 (sun)) * Date: 17 Aug 1994 23:21:36 GMT * Newsgroups: rec.bicycles.tech * Subject: Gear Shift Tables Made Easy ... * * This programs expects an input file as an argument. * * The file needs to have pairs of lines of numbers. Blank lines or lines * that start with a '#' are allowed before or after the line pairs. * * The first line of a pair specifies the number of teeth on the ring gears. * The second line of a pair specifies the number of teeth on the cog gears. * * The numbers in the set of numbers on either line can appear in any order, * including random, as long as the numbers are delimited by spaces. * * This program supports up to three ring gears and up to eight cog gears. * If you specify more than this the excess will be ignored! * * If you do not want each gear spec to appear on a separate page you can * comment out the printf that prints a 'Ctl-L' on line 127. * * * 08/04/98 m.e.sherman start to add log graphing * 08/05/98 m.e.sherman complete log graphing * 08/06/98 m.e.sherman add crank, wheel size, more help, and * Sheldon Brown's gear charting * up max cogs to 10 - who knows... * 08/10/98 m.e.sherman add display option * 08/11/98 m.e.sherman display crank arm length. * 09/23/99 m.e.sherman change numbers displayed in semilog graph * so they are in the right order (line 363) * 11/16/99 m.e.sherman remove newline in print of comment and add to * newline * 05/04/02 m.e.sherman make room for campy 10sp with tripple * 05/25/02 m.e.sherman 132 col display for log graph * */ #include #include extern double log(double); void lower(char *string); int LookupWheel(char *ptr); void ShowUsage(); #define RATIO_CONSTANT 27.0 /* MAGIC RATIO Multiplier */ #define NUMBER_OF_RATIOS 30 /* campy 10x3 */ char lbuf[128]; /* Input Line Buffer */ int rings[3]; /* Main Ring Gear Teeth Counts */ int cogs[10]; /* Cog Gear Teeth Counts */ /* log graph limits */ int p1 = 78; /* paper is 78 characters wide */ int p2 = 77; double n1; /* lower limit for log graph */ double n2; /* upper limit for log graph */ char line[132]; /* a line of the log graph */ char tmps[80]; /* a temp string */ struct ratio { int gear; /* Initialize from 1 to NUMBER_OF_RATIOS */ float value; /* Gear Ratio times RATIO_CONSTANT */ int ring; /* which chainring */ float ratio; /* Sheldon Brown's radius ratio */ } ratios[NUMBER_OF_RATIOS]; char *spc = " "; /* Printing Spaces */ char *dsh = "------------------------"; /* Printing Dashes */ /* the Sheldon Brown gearing method */ /* wheel radius' taken from his web page */ int wheel_index; /* index into wheels table */ #define DEFAULT_WHEEL_INDEX 1 #define NUMBER_OF_WHEELS 26 struct wheel_table{ char wheel[20]; /* wheel size name */ char alt[20]; /* alternate name */ int radius; /* wheel radius in cm */ float diameter; /* wheel diameter in inches */ } wheels[NUMBER_OF_WHEELS] = { "27 x 1 3/8", "", 345, (float)(345*2)/25.4, "27 x 1 1/4", "", 343, (float)(343*2)/25.4, "27 x 1 1/8", "", 342, (float)(342*2)/25.4, "27 x 1", "", 340, (float)(340*2)/25.4, "700 x 44", "", 354, (float)(354*2)/25.4, "700 x 38", "", 347, (float)(347*2)/25.4, "700 x 35", "", 345, (float)(345*2)/25.4, "700 x 32", "", 342, (float)(342*2)/25.4, "700 x 28", "", 336, (float)(336*2)/25.4, "700 x 25", "", 335, (float)(335*2)/25.4, "700 x 20", "", 332, (float)(332*2)/25.4, "26 x 2.125", "", 330, (float)(330*2)/25.4, "26 x 1.9", "", 324, (float)(324*2)/25.4, "26 x 1.5", "", 312, (float)(312*2)/25.4, "26 x 1.25", "", 311, (float)(311*2)/25.4, "26 x 1.0", "559mm", 305, (float)(305*2)/25.4, "650c", "26 x 1", 311, (float)(311*2)/25.4, "wide tubular", "", 338, (float)(338*2)/25.4, "tubular", "narrow tubular", 335, (float)(335*2)/25.4, "26 x 1 3/8", "590mm", 330, (float)(330*2)/25.4, "24", "", 305, (float)(305*2)/25.4, "24 x 1", "520mm", 279, (float)(279*2)/25.4, "20 x 1.75", "406mm", 254, (float)(254*2)/25.4, "20 x 1 1/4", "451mm", 257, (float)(257*2)/25.4, "17 x 1 1/4", "369mm", 211, (float)(211*2)/25.4, "16 x 1 3/8", "349mm", 204, (float)(204*2)/25.4 }; int int_compare( int *a, int *b ) { return( *a - *b ); } int rat_compare( struct ratio *a, struct ratio *b ) { if ( a->value > b->value ) return( 1 ); else if ( a->value < b->value ) return( -1 ); else return( 0 ); } main( int argc, char *argv[ ] ) { FILE *fs; /* File Stream Pointer */ int num_rings, num_cogs; /* Number of rings & cogs */ int i, j, k, l; /* Loopers */ int t, z; /* Temp Vars */ int first = 0; /* Have we printed first page? */ char *sptr; /* a string pointer */ int crank; /* crank arm length */ float wheel_size; /* in inches */ float radius_ratio; /* Sheldon Brown's radius Ratio */ int gear, logs, sheldon; /* display option flags */ int pat, wide; if ( argc != 2 ) { fprintf( stderr, "Usage: %s gear_file\n", argv[0] ); ShowUsage(); exit( -5 ); } if ( (fs = fopen( argv[1], "r" )) == (FILE *)NULL ) { fprintf( stderr, "Could not open gear spec file '%s'\n", argv[1] ); exit( -1 ); } printf("\nGearing for %s\n", argv[1]); /* defaults */ wheel_index = DEFAULT_WHEEL_INDEX; /* 27 x 1 1/4 */ crank = 170; gear = 1; /* all displays on */ logs = 1; sheldon = 1; pat = 1; while ( fgets( &lbuf[0], 128, fs ) != (char *)NULL ) { if(first == 1){ first = 2; printf( "\014\n" ); /* Ctl-L */ } /* skip blank lines */ if (lbuf[0] == '\n') continue; /* print comments */ if (lbuf[0] == '#'){ printf("%s", lbuf); continue; } /* ---=== crank length ===--- */ if(lbuf[0] == 'c'){ (int)sptr = strstr(lbuf, "="); sptr++; crank = atoi(sptr); continue; } /* ---=== wheel size ===--- */ if(lbuf[0] == 'w'){ (int)sptr = strstr(lbuf, "="); sptr++; wheel_index = LookupWheel(sptr); continue; } /* ---=== display options ===--- */ if(lbuf[0] == 'd'){ (int)sptr = strstr(lbuf, "="); gear = 0; logs = 0; sheldon = 0; pat = 0; wide = 0; sptr++; if(strstr(sptr, "g") > 0){ gear = 1; } if(strstr(sptr, "l") > 0){ logs = 1; } if(strstr(sptr, "s") > 0){ sheldon = 1; } if(strstr(sptr, "p") > 0){ pat = 1; } if(strstr(sptr, "w") > 0){ wide = 1; } continue; } /* ---=== set the wheel size ===--- */ printf(" wheel size = %s", wheels[wheel_index].wheel); if(strlen(wheels[wheel_index].alt)>0){ printf(", %s", wheels[wheel_index].alt); } printf(" (radius = %d)\n", wheels[wheel_index].radius); wheel_size = wheels[wheel_index].diameter; radius_ratio = (float)wheels[wheel_index].radius / (float)crank; printf(" crank arm = %dmm\n", crank); /* ---=== clear the variables ===--- */ rings[0] = rings[1] = rings[2] = 0; cogs[0] = cogs[1] = cogs[2] = cogs[3] = cogs[4] = cogs[5] = cogs[6] = cogs[7] = cogs[8] = cogs[9] = 0; for ( i = 0; i < NUMBER_OF_RATIOS; i++ ) { ratios[i].gear = 0; ratios[i].value = 0.0; ratios[i].ring = 0; ratios[i].ratio = 0.0; } /* ---=== scan in the chainrings ===--- */ /* */ /* if the line didn't contain any of */ /* the above, it must be the chainrings */ /* */ /* ---==============================--- */ num_rings = sscanf( &lbuf[0], "%d%d%d", &rings[0], &rings[1], &rings[2] ); if ( num_rings == 0 ) { fprintf( stderr, "Could not parse the ring gear spec\n" ); exit( -2 ); } /* ---=== read the cogs from the file ===--- */ if ( fgets( &lbuf[0], 80, fs ) == (char *)NULL ) { fprintf( stderr, "Missing cog gear spec\n" ); exit( -3 ); } /* ---=== and scan them in to the program ===--- */ num_cogs = sscanf( &lbuf[0], "%d%d%d%d%d%d%d%d%d%d", &cogs[0], &cogs[1], &cogs[2], &cogs[3], &cogs[4], &cogs[5], &cogs[6], &cogs[7], &cogs[8], &cogs[9]); if ( num_cogs == 0 ) { fprintf( stderr, "Could not parse the cog gear spec\n" ); exit( -4 ); } /* ---=== sort the gears by teeth ===--- */ qsort( &rings[0], num_rings, sizeof(int), int_compare ); qsort( &cogs[0], num_cogs, sizeof(int), int_compare ); /* ---=== calculate gears and ratios ===--- */ for ( j = 0; j < num_rings; j++ ){ for ( i = 0; i < num_cogs; i++ ) { ratios[(j*num_cogs)+i].gear = ((j + 1) * num_cogs) - i; ratios[(j*num_cogs)+i].value = ((float)rings[j] / (float)cogs[i]) * wheel_size; ratios[(j*num_cogs)+i].ring = rings[j]; /* Sheldon Brown's radius ratio */ ratios[(j*num_cogs)+i].ratio = radius_ratio * (float)rings[j] / (float)cogs[i]; } } /* ---=== now draw the charts and graphs ===--- */ /* ========== */ /* Gear Chart */ /* ========== */ if(gear == 1){ printf( "\n\n Gear Ratio Chart\n\n" ); printf( " |" ); for ( i = 0; i < num_cogs; i++ ) printf( " %2d", cogs[i] ); printf( "\n----+" ); for ( i = 0; i < num_cogs; i++ ) printf( "%0.6s", dsh ); printf( "-\n" ); for ( j = (num_rings - 1); j >= 0; j-- ) { printf( " %2d |", rings[j] ); for ( i = 0; i < num_cogs; i++ ) printf( " %5.1f", ratios[(j*num_cogs)+i].value ); printf( "\n" ); } } /* ============== */ /* Semi-Log Graph */ /* ============== */ if(logs == 1){ if(wide == 1){ p1 = 130; p2 = 129; } printf( "\n\n Semi-Log Graph\n\n" ); n1 = log((double)20); /* draw gears from 20" */ n2 = log((double)120); /* to 120" */ /* clear the line */ for(l=0; l= 0; j-- ) { printf( " %2d |", rings[j] ); for ( i = 0; i < num_cogs; i++ ) { printf(" %1.2f", ratios[(j*num_cogs)+i].ratio); } printf( "\n" ); } } /* =================== */ /* Shift Pattern Chart */ /* =================== */ if(pat == 1){ /* ---=== resort by gears ===--- */ qsort( &ratios[0], (num_cogs * num_rings), sizeof(struct ratio), rat_compare ); printf( "\n Shift Pattern Chart (with ratio value changes)\n\n" ); t = (num_rings - 1) * 2; printf( " |%0.*s Gear %0.*s Value Ratio Step", t, spc, t, spc ); if ( num_rings > 1 ) printf( " Main Ring Steps" ); printf( "\n----+-------------%0.*s", ((t * 2) + 16), dsh ); if ( num_rings > 1 ) printf( "----%0.*s", (num_rings * 7), dsh ); printf( "\n" ); /* for each chainring */ for ( j = 0; j < num_rings; j++ ){ /* for each cog */ for ( i = 0; i < num_cogs; i++ ) { z = (j * num_cogs) + i; t = ratios[z].gear; /* gear# | gear value */ printf( " %2d | %0.*s %2d %0.*s %5.1f %4.2f", (z + 1), (((t - 1) / num_cogs) * 4), spc, t, (((num_rings - 1)* 4) - (((t - 1) / num_cogs)* 4)), spc, ratios[z].value, ratios[z].ratio); /* the step */ if ( ((j + 1) * (i + 1)) != (num_rings * num_cogs) ) printf( " %4.1f", (ratios[z + 1].value - ratios[z].value) ); /* ring steps */ if((num_rings > 1) && ((ratios[z].gear % num_cogs) != 0)){ for ( k = z + 1; k < NUMBER_OF_RATIOS; k++ ) if ( (ratios[z].gear + 1) == ratios[k].gear ) break; printf( "%0.*s%4.1f", (((ratios[z].gear / num_cogs) + 1) * 7), spc, (ratios[k].value - ratios[z].value) ); } printf( "\n" ); } } }/* end of if(pat) */ if(first == 0 || first == 2 ){ first = 1; } } exit( 0 ); } int LookupWheel(char *ptr){ int i, l; char string[80], stringn[80]; strncpy(string, ptr, 80); lower(string); for(i=0; i l){ l = strlen(string); } if(strncmp(string, stringn, l) == 0){ return(i); } /* check the alt name */ strncpy(stringn, wheels[i].alt, 80); lower(stringn); l = strlen(stringn); if(strlen(string) > l){ l = strlen(string); } if(strncmp(string, stringn, l) == 0){ printf(" alt index = %d\n", i); return(i); } } printf("no wheel size match found for %s, %d\n",string,i); return(DEFAULT_WHEEL_INDEX); } void lower(char *string){ int i; char *iptr, *optr; iptr = string; optr = iptr; for(i=0; i='A' && *iptr <='Z'){ *optr = *iptr+32; }else if(*iptr == 0x0d || *iptr == 0x0a){ *optr = '\0'; }else{ *optr = *iptr; } optr++; } } *optr = '\0'; } void ShowUsage(){ int i; fprintf(stderr, "Where the gear_file is an ASCII file containing pairs "); fprintf(stderr, "of lines of numbers.\n"); fprintf(stderr, "The first line of a pair specifies the number of "); fprintf(stderr, "teeth on the ring gears.\n"); fprintf(stderr, "The second line of a pair specifies the number of "); fprintf(stderr, "teeth on the cog gears.\n"); fprintf(stderr, "Numbers on each line are separated by spaces.\n"); fprintf(stderr, "Blank lines are ignored.\n"); fprintf(stderr, "Lines that start with a '#' are considered comments.\n"); fprintf(stderr, "Optional inputs are:\n"); fprintf(stderr, " c = nnn Inputs crank arm length in mm "); fprintf(stderr, "(for Sheldon Brown's raitios).\n"); fprintf(stderr, " Default crank size is 170mm.\n"); fprintf(stderr, " w = sss Inputs wheel size\n"); fprintf(stderr, " Default wheel size is 27 x 1 1/4\n"); fprintf(stderr, "Valid wheel sizes are:\n"); for(i=0; i