lat@stcvax.UUCP (06/01/84)
Included you will find the sources for two programs that provide information about bicycle gearing. The first part is the only documentation I have. The sources come last. The `gears' program asks you for the number of cogs on your chain wheels (usually 2 wheels, sometimes 3, but it will accept up to 4) the number of cogs on each free-wheel (usually you'll have 5 gears, sometimes 6; 8 is the max) and your wheel size. It then prints out a chart like the one below. The program prompts with `: ' when it's waiting for a value. The cross chain combinations are not used. Enter chain wheel sizes (# teeth), one per line. Type RETURN when done. : 53 : 41 : Enter free wheel sizes (# teeth), one per line. Type RETURN when done. : 28 : 24 : 20 : 17 : 14 : Wheel size (inches): 27 Chain Wheel: 53 41 Free Wheel: 28 24 20 17 14 Chain Wheel Free Wheel Ratio Diff 41 28 39.5 ---- 41 24 46.1 6.6 41 20 55.3 9.2 53 24 59.6 4.3 41 17 65.1 5.5 53 20 71.6 6.4 53 17 84.2 12.6 53 14 102.2 18.0 The `setup' program gives you help in choosing a set of gears. It prompts you for a bunch of values. Here are the prompts you will see. Values in parentheses are default values selected if you type just return. Highest gear ratio in gear inches: Lowest gear ratio in gear inches: Max allowed gear spacing (25): # of chain wheels (2): Max # teeth on chain wheels (54): Min # teeth on chain wheels (35): # of free wheel gears (5): Max # teeth on free wheel gears (34): Min # teeth on free wheel gears (13): Wheel size (27): The first two values are so you can better tailor your gearing to your environment. If you live in Kansas you can probably get away with high gearing. To get a set of gears similar to the ones in the chart above, you could type 102 for the 1st value and 39 for the 2nd. The 3rd prompt is so that the program doesn't give any bizarre combinations with huge spacings between gears. I added it because the 1st pass at the program produced a gearing where the spacing between the two highest gears was almost 35 gear-inches! The next 6 prompts describe your gearing. The program asks for the max & min # of teeth because derailleurs (good word for a spelling bee) are limited in what they can handle here. If you don't know about your bike, ask your local bike shop. Ok, here come the source files, in shar format. Cut at the dashed line. Don't forget the dashed line before my .signature. ------------------------------ cat > makefile << End_Of_makefile all: gears setup gears: gears.o gearset.o prompt.o cc -o gears gears.o gearset.o prompt.o setup: setup.o gearset.o prompt.o cc -o setup setup.o gearset.o prompt.o End_Of_makefile cat > gears.c << End_Of_gears.c #include <stdio.h> #define NFS 4 #define NFW 8 int chainw[NFS]; int freew[NFW]; float wheelsz; /* ARGSUSED */ main(argc, argv) char **argv; { register int ng, nf; register int r; fprintf(stderr, "Enter chain wheel sizes (# teeth), one per line.\n"); fprintf(stderr, "Type RETURN when done.\n"); for (ng = 0; ng < NFS; ng++) { if ((r = getint(":", &chainw[ng])) == 0) break; if (r < 0) /* Error */ ng--; } if (ng == 0) { fprintf(stderr, "Let's be reasonable\n"); exit(0); } if (ng >= NFS) fprintf(stderr, "No more chain wheels allowed\n"); fprintf(stderr, "Enter free wheel sizes (# teeth), one per line.\n"); fprintf(stderr, "Type RETURN when done.\n"); for (nf = 0; nf < NFW; nf++) { if ((r = getint(":", &freew[nf])) == 0) break; if (r < 0) /* Error */ nf--; } if (nf == 0) { fprintf(stderr, "Let's be reasonable\n"); exit(0); } if (nf >= NFW) fprintf(stderr, "No more free wheel gears allowed\n"); wheelsz = 27; /* Assume default wheel size */ getflt("Wheel size (inches):", &wheelsz); gearset(&chainw[0], ng, &freew[0], nf, wheelsz, 1); exit(0); } End_Of_gears.c cat > gearset.c << End_Of_gearset.c #include <stdio.h> #define NRATIOS 100 struct gearset { int chgear, freegear; int badmatch; float gearinch; } ratios[NRATIOS]; gcompar(s1, s2) struct gearset *s1, *s2; { if (s1->gearinch < s2->gearinch) return -1; if (s1->gearinch > s2->gearinch) return 1; return 0; } intcmp(i, j) int *i, *j; { return (*j - *i); } gearset(fs, nfs, fw, nfw, wsz, ignore) int *fs, nfs, *fw, nfw; float wsz; { register struct gearset *gp; register char *dp; int i, j, k; struct gearset *gpend; float prev; char diffbuf[20]; /* Sort and print the chain wheels. */ qsort(fs, nfs, sizeof(int), intcmp); printf("\n\n"); printf("Chain Wheel: "); for (i = 0; i < nfs; i++) printf(" %2d", fs[i]); printf("\n"); /* Sort and print the free wheel ratios. */ qsort(fw, nfw, sizeof(int), intcmp); printf("Free Wheel: "); for (i = 0; i < nfw; i++) printf(" %2d", fw[i]); printf("\n"); k = 0; for (i = 0; i < nfs; i++) { for (j = 0; j < nfw; j++) { if (k >= NRATIOS) { fprintf(stderr, "More than %d gear combinations!\n", NRATIOS); exit(1); } ratios[k].chgear = fs[i]; ratios[k].freegear = fw[j]; ratios[k].gearinch = (float)wsz * fs[i] / fw[j]; /* Flag biggest chain wheel with biggest free wheel * and smallest chain wheel with smallest free wheel. */ ratios[k].badmatch = 0; if (i == 0 && j == 0 || i == nfs-1 && j == nfw-1) { if (ignore) continue; ratios[k].badmatch = 1; } k++; } } qsort((char*)ratios, k, sizeof(ratios[0]), gcompar); printf("\n\n"); printf("Chain Wheel Free Wheel Ratio Diff\n\n"); gpend = &ratios[k]; prev = 0.0; for (gp = &ratios[0]; gp < gpend; gp++) { if (prev == 0.0) dp = "----"; else { dp = diffbuf; sprintf(dp, "%4.1f", gp->gearinch - prev); } printf("%8d %7d %5.1f %s %s\n", gp->chgear, gp->freegear, gp->gearinch, gp->badmatch ? "*****" : " ", dp); prev = gp->gearinch; } } End_Of_gearset.c cat > prompt.c << End_Of_prompt.c #include <stdio.h> getint(ps, num) char *ps; int *num; { char line[82]; fprintf(stderr, "%s ", ps); if (fgets(line, 80, stdin) == NULL) { exit(0); } line[80] = '\0'; if (strlen(line) <= 1) { return 0; } if (sscanf(line, "%d", num) != 1) { fprintf(stderr, "Bad number, try again.\n"); return -1; } return 1; } getflt(ps, num) char *ps; float *num; { char line[82]; fprintf(stderr, "%s ", ps); if (fgets(line, 80, stdin) == NULL) { exit(0); } line[80] = '\0'; if (strlen(line) <= 1) { return 0; } if (sscanf(line, "%f", num) != 1) { fprintf(stderr, "Bad number, try again.\n"); return -1; } return 1; } getyn(ps, ans) char *ps; int *ans; { char line[82]; fprintf(stderr, "%s (y/n) ", ps); if (fgets(line, 80, stdin) == NULL) { exit(0); } line[80] = '\0'; if (strlen(line) <= 1) { return 0; } if (line[0] == 'Y' || line[0] == 'y') { *ans = 1; return 1; } if (line[0] == 'N' || line[0] == 'n') { *ans = 0; return 1; } return -1; } End_Of_prompt.c cat > setup.c << End_Of_setup.c /* This program determines bicycle gear sets which * maximize the spacing between gears. * * Note: you might want to set your tab stops to 5. */ #include <stdio.h> #define NO 0 #define YES 1 /* Max # of chain wheels */ #define NFS 4 static int chwheels[NFS]; /* Max # of free wheels */ #define NFW 8 static int frwheels[NFW]; /* Max and min # of teeth on front sprocket */ #define MAXFST 54 #define MINFST 35 /* Max and min # of teeth on free wheel */ #define MAXFWT 34 #define MINFWT 13 static float upper, lower; /* Upper and lower gear ranges */ static int nfs; /* # of chain wheels */ static int nfw; /* # of free wheels */ static float whlsize; /* Wheel size */ static int mgspace; /* Max allowed spacing between gears */ static int maxfst, minfst; /* Max & min # teeth on chain wheels */ static int maxfwt, minfwt; /* Max & min # teeth on free wheels */ static int lfs, sfs; /* Large & small # chain wheels */ static int lfw, sfw; /* Large & small # free wheels */ static int nfound; /* This structure contains a set of chain wheels, free wheel, * and the closest spacing between any two gears. We keep the * NBEST best choices and discard the rest. */ struct gearing { int g_fs[NFS]; /* Choices for chain wheels */ int g_fw[NFW]; /* Choices for free wheel */ float g_minspace; /* Minimum spacing between two gears */ struct gearing *g_next; /* Points to next entry */ }; #define NBEST 10 static struct gearing gears[NBEST]; static int ng_left = NBEST; /* # unused entries remaining in choices */ static struct gearing *gp_head, *gp_tail; static int ccignore; /* Ignore cross chain ratios */ main(argc, argv) { register struct gearing *gp; float whinch; double fabs(); char fmt[80]; for ( ;; ) { while (getflt("Highest gear ratio in gear inches:", &upper) <= 0) ; while (getflt("Lowest gear ratio in gear inches:", &lower) <= 0) ; if (lower < upper) break; fprintf(stderr, "Lowest gear > than highest gear, bozo!\n"); } mgspace = 25; while (getint("Max allowed gear spacing (25):", &mgspace) < 0) ; for ( ;; ) { nfs = 2; while (getint("# of chain wheels (2):", &nfs) < 0) ; if (nfs < 2) { fprintf(stderr, "Must be at least 2"); } else if (nfs > NFS) { fprintf(stderr, "Can't be more than %d", NFS); } else { break; } fputs(", try again\n", stderr); } maxfst = MAXFST; sprintf(fmt, "Max # teeth on chain wheels (%d):", MAXFST); while (getint(fmt, &maxfst) < 0) ; minfst = MINFST; sprintf(fmt, "Min # teeth on chain wheels (%d):", MINFST); while (getint(fmt, &minfst) < 0) ; for ( ;; ) { nfw = 5; while (getint("# of free wheel gears (5):", &nfw) < 0) ; if (nfw < 2) { fprintf(stderr, "Must be at least 2"); } else if (nfw > NFW) { fprintf(stderr, "Can't be more than %d", NFW); } else { break; } fputs(", try again\n", stderr); } maxfwt = MAXFWT; sprintf(fmt, "Max # teeth on free wheel gears (%d):", MAXFWT); while (getint(fmt, &maxfwt) < 0) ; minfwt = MINFWT; sprintf(fmt, "Min # teeth on free wheel gears (%d):", MINFWT); while (getint(fmt, &minfwt) < 0) ; for ( ;; ) { whlsize = 27.0; while (getflt("Wheel size (27):", &whlsize) < 0) ; if (whlsize > 0) break; fprintf(stderr, "Let's be reasonable"); fputs(", try again\n", stderr); } ccignore = YES; /* * while (getyn("Ignore cross-chain gear combinations?", &ccignore) < 0) * ; */ /* Work through the large-front-sprocket/small-free-wheel combos */ for (lfs = maxfst; lfs > minfst; lfs--) { for (sfw = minfwt; sfw < maxfwt; sfw++) { whinch = whlsize * lfs / sfw; if (fabs(whinch - upper) > 0.5) /* Out of bounds */ continue; chwheels[0] = lfs; frwheels[nfw-1] = sfw; /* Small-front-sprocket/large-free-wheel combos */ for (sfs = minfst; sfs < lfs; sfs++) { for (lfw = maxfwt; lfw > sfw; lfw--) { whinch = whlsize * sfs / lfw; if (fabs(whinch - lower) > 0.5) continue; chwheels[nfs-1] = sfs; frwheels[0] = lfw; /* Recurse looking for front sprockets */ fsrecurs(lfs-1, sfs+1, 1); } } } } if ((gp = gp_head) == NULL) { fprintf(stderr, "Couldn't find any with those parameters\n"); } else { printf("Parameters:\n"); printf("\tGear range: %1.0f - %1.0f\n", lower, upper); printf("\tWheel size: %3.1f\n", whlsize); printf("\tMax allowed gear spacing: %d\n", mgspace); printf("\tCross-chain gear combinations %s\n\n", ccignore ? "ignored" : "included"); do { gearset(gp->g_fs, nfs, gp->g_fw, nfw, whlsize, ccignore); } while ((gp = gp->g_next) != NULL); } exit(0); } fsrecurs(ub, lb, ix) int ub, lb; /* Upper & lower bounds for undetermined gears */ register int ix; /* Index into chwheels where new one goes */ { register int i; /* The large & small gears are already chosen; thus the `2' */ if (ix+2 <= nfs) { register int nteeth; i = ix++; for (nteeth = ub; nteeth >= lb; ) { chwheels[i] = nteeth--; fsrecurs(nteeth, lb, ix); } return; } /* We get to here when we have a full chain wheel set. * We can recurse on the choices of free wheel gears. */ fwrecurs(lfw-1, sfw+1, 1); } fwrecurs(ub, lb, ix) int ub, lb; /* Upper & lower bounds for undetermined gears */ register int ix; /* Index into frwheels where new one goes */ { register int i, j; register int gix; /* Index into ginches */ float ginches[NFS*NFW]; float minspace, maxspace, diff; register struct gearing *gp, **gpp; int fcompar(); /* The large & small gears are already chosen. Thus the `2' below */ if (ix+2 <= nfw) { register int nteeth; i = ix++; for (nteeth = ub; nteeth >= lb; ) { frwheels[i] = nteeth--; fwrecurs(nteeth, lb, ix); } return; } /* Get to here when we have a full free wheel gear set. * Compute all the gear-inch values for all combinations. * Sort them into order, find the smallest spacing between * adjacent gears, and put this entry into the list of * good choices. */ gix = 0; for (i = 0; i < nfs; i++) { for (j = 0; j < nfw; j++) { /* Don't use this value if it's one of those * cross-chain combos, unless they want us to. */ if (ccignore && i == 0 && j == 0 || i == nfs-1 && j == nfw-1) continue; ginches[gix++] = whlsize * chwheels[i] / frwheels[j]; } } qsort(ginches, gix, sizeof(float), fcompar); minspace = 100000.0; maxspace = 0.0; for (i = 1; i < gix; i++) { diff = ginches[i] - ginches[i-1]; if (diff < minspace) minspace = diff; if (diff > maxspace) maxspace = diff; } #ifdef DEBUG printf("trying\n "); for (i = 0; i < nfs; i++) { printf(" %2d", chwheels[i]); } printf("\t\t"); for (i = 0; i < nfw; i++) { printf(" %2d", frwheels[i]); } printf("\t%5.2f\t%5.2f\n", minspace, maxspace); #endif if (maxspace > mgspace) { #ifdef DEBUG printf("Too big\n"); #endif return; } /* Now see if this combination is a candidate. */ gpp = &gp_head; while ((gp = *gpp) != NULL) { if (gp->g_minspace < minspace) { break; } gpp = &gp->g_next; } if (gp == NULL) { /* This gearing set has the smallest difference. If * we haven't chosen our ten best yet, we can add * this one, otherwise we just discard it. Note * that gp == NULL if it's discarded. */ if (ng_left > 0) { gp = &gears[--ng_left]; gp_tail = gp; *gpp = gp; } } else { /* Insert new entry. We can either add a new one * from the free list or discard the smallest entry. */ if (ng_left > 0) { *gpp = &gears[--ng_left]; gears[ng_left].g_next = gp; gp = *gpp; } else if (gp != gp_tail) { /* We have to discard the smallest entry and * use it's space for this new entry. */ *gpp = gp_tail; (*gpp)->g_next = gp; while (gp->g_next != gp_tail) { gp = gp->g_next; } gp_tail = gp; /* New smallest entry. */ gp->g_next = NULL; gp = *gpp; } } /* Fill in the gear choices if we didn't discard */ if (gp != NULL) { gp->g_minspace = minspace; for (i = 0; i < nfs; i++) { gp->g_fs[i] = chwheels[i]; } for (i = 0; i < nfw; i++) { gp->g_fw[i] = frwheels[i]; } #ifdef DEBUG for (gp = gp_head; gp != NULL; gp = gp->g_next) { printf("[%2d]", gp - &gears[0]); for (i = 0; i < nfs; i++) { printf(" %2d", gp->g_fs[i]); } printf("\t\t"); for (i = 0; i < nfw; i++) { printf(" %2d", gp->g_fw[i]); } printf("\t%5.2f\n", gp->g_minspace); } printf("\n\n"); #endif DEBUG } } /* Compare two floating point numbers for qsort(). */ fcompar(f1, f2) float *f1, *f2; { if (*f1 < *f2) return -1; if (*f1 > *f2) return 1; return 0; } End_Of_setup.c ------------------------------ -- {decvax, hao}!stcvax!lat Larry Tepper {allegra, amd70, ucbvax}!nbires!stcvax!lat 303-673-5435