#!/usr/bin/env python3 """ Bicycle Gear Calculator This script calculates and generates markdown tables for: - Gear inches for combinations of chainring and rear cog - Cadence for combinations of chainring and rear cog at different target speeds - Speeds in MPH for combinations of chainring and rear cog at different target cadences The script also provides an option to save each table as a separate file. """ import os import math def calculate_gear_inches(chainring, cog, wheel_size, tire_width): """Calculate gear inches for a given combination.""" # Calculate wheel diameter in inches (including tire) wheel_diameter = wheel_size / 25.4 + 2 * tire_width / 25.4 return (chainring / cog) * wheel_diameter def calculate_speed(chainring, cog, wheel_size, tire_width, cadence): """Calculate speed in MPH for a given combination and cadence.""" # Calculate wheel circumference in meters wheel_diameter_m = (wheel_size + 2 * tire_width) / 1000 wheel_circumference_m = wheel_diameter_m * math.pi # Calculate distance traveled per pedal revolution in meters distance_per_revolution = wheel_circumference_m * (chainring / cog) # Calculate speed in MPH (cadence is in RPM) # distance_per_minute = distance_per_revolution * cadence (meters/minute) # Convert to meters/hour and then to miles/hour meters_per_hour = distance_per_revolution * cadence * 60 miles_per_hour = meters_per_hour / 1609.34 return miles_per_hour def calculate_cadence(chainring, cog, wheel_size, tire_width, speed_mph): """Calculate cadence in RPM for a given combination and speed.""" # Calculate wheel circumference in meters wheel_diameter_m = (wheel_size + 2 * tire_width) / 1000 wheel_circumference_m = wheel_diameter_m * math.pi # Calculate distance traveled per pedal revolution in meters distance_per_revolution = wheel_circumference_m * (chainring / cog) # Calculate cadence in RPM # speed_mph converted to meters/minute meters_per_minute = speed_mph * 1609.34 / 60 cadence = meters_per_minute / distance_per_revolution return cadence def create_gear_inches_table(chainrings, cogs, wheel_size, tire_width): """Create a markdown table showing gear inches for all combinations.""" header = "| Chainring / Cog | " + " | ".join([str(cog) for cog in cogs]) + " |\n" separator = "| --- | " + " | ".join(["---" for _ in cogs]) + " |\n" rows = "" for chainring in chainrings: row = f"| {chainring} | " row += " | ".join([f"{calculate_gear_inches(chainring, cog, wheel_size, tire_width):.1f}" for cog in cogs]) row += " |\n" rows += row return header + separator + rows def create_cadence_table(chainrings, cogs, wheel_size, tire_width, speed_mph): """Create a markdown table showing cadence for all combinations at a specific speed.""" header = f"| Cadence at {speed_mph} MPH | " + " | ".join([str(cog) for cog in cogs]) + " |\n" separator = "| --- | " + " | ".join(["---" for _ in cogs]) + " |\n" rows = "" for chainring in chainrings: row = f"| {chainring} | " row += " | ".join([f"{calculate_cadence(chainring, cog, wheel_size, tire_width, speed_mph):.1f}" for cog in cogs]) row += " |\n" rows += row return header + separator + rows def create_speed_table(chainrings, cogs, wheel_size, tire_width, cadence): """Create a markdown table showing speed for all combinations at a specific cadence.""" header = f"| Speed at {cadence} RPM | " + " | ".join([str(cog) for cog in cogs]) + " |\n" separator = "| --- | " + " | ".join(["---" for _ in cogs]) + " |\n" rows = "" for chainring in chainrings: row = f"| {chainring} | " row += " | ".join([f"{calculate_speed(chainring, cog, wheel_size, tire_width, cadence):.1f}" for cog in cogs]) row += " |\n" rows += row return header + separator + rows def save_to_file(content, filename): """Save content to a file.""" with open(filename, 'w') as f: f.write(content) print(f"Saved to {filename}") def create_summary_table(chainrings, cogs, wheel_size, tire_width): """Create a comprehensive summary table with multiple metrics for each combination.""" # Define reference values reference_speed = 20.0 # mph reference_cadence = 100.0 # rpm header = "| Chainring | Cog | Ratio | Gear Inches | Cadence at 20mph | Speed at 100rpm |\n" separator = "| --- | --- | --- | --- | --- | --- |\n" rows = "" for chainring in chainrings: for cog in cogs: # Calculate all metrics ratio = round(chainring / cog, 2) gear_inches = calculate_gear_inches(chainring, cog, wheel_size, tire_width) cadence_at_20mph = calculate_cadence(chainring, cog, wheel_size, tire_width, reference_speed) speed_at_100rpm = calculate_speed(chainring, cog, wheel_size, tire_width, reference_cadence) # Format the row row = f"| {chainring} | {cog} | {ratio:.2f} | {gear_inches:.1f} | {cadence_at_20mph:.1f} | {speed_at_100rpm:.1f} |\n" rows += row return header + separator + rows def main(): # User input for variables print("Bicycle Gear Calculator") print("======================\n") # Get tire and wheel specifications tire_width = float(input("Enter tire width in mm (e.g., 23): ") or 25) wheel_size = float(input("Enter wheel size in mm (e.g., 622 for 700c): ") or 622) # Get chainring and cog specifications chainring_input = input("Enter list of chainring teeth, separated by commas (e.g., 34,36,38,40): ") or "34,36,38,40,42" chainrings = [int(x.strip()) for x in chainring_input.split(",")] cog_input = input("Enter list of rear cog teeth, separated by commas (e.g., 11,12,13,14,16,18,20,22,25,28,32): ") or "11,12,13,14,16,18,20,22,25,28,32" cogs = [int(x.strip()) for x in cog_input.split(",")] # Get target cadences and speeds cadence_input = input("Enter list of target cadences in RPM, separated by commas (e.g., 80,90,100): ") or "80,90,100" cadences = [int(x.strip()) for x in cadence_input.split(",")] speed_input = input("Enter list of target speeds in MPH, separated by commas (e.g., 15,18,20,25): ") or "15,18,20,25" speeds = [float(x.strip()) for x in speed_input.split(",")] # Generate tables print("\nGenerating tables...\n") # Gear inches table gear_inches_table = create_gear_inches_table(chainrings, cogs, wheel_size, tire_width) print("Gear Inches Table:") print(gear_inches_table) # Cadence tables for each target speed cadence_tables = [] for speed in speeds: table = create_cadence_table(chainrings, cogs, wheel_size, tire_width, speed) cadence_tables.append((speed, table)) print(f"\nCadence Table for {speed} MPH:") print(table) # Speed tables for each target cadence speed_tables = [] for cadence in cadences: table = create_speed_table(chainrings, cogs, wheel_size, tire_width, cadence) speed_tables.append((cadence, table)) print(f"\nSpeed Table for {cadence} RPM:") print(table) # Summary table with multiple metrics summary_table = create_summary_table(chainrings, cogs, wheel_size, tire_width) print("\nSummary Table (all metrics):") print(summary_table) # Option to save tables save_option = input("\nDo you want to save tables to files? (y/n): ").lower().strip() if save_option == 'y': # Create a directory for the files if it doesn't exist output_dir = "gear_tables" if not os.path.exists(output_dir): os.makedirs(output_dir) # Save gear inches table gear_inches_filename = os.path.join(output_dir, "gear_inches.md") save_to_file(gear_inches_table, gear_inches_filename) # Save cadence tables for speed, table in cadence_tables: cadence_filename = os.path.join(output_dir, f"cadence_at_{speed}mph.md") save_to_file(table, cadence_filename) # Save speed tables for cadence, table in speed_tables: speed_filename = os.path.join(output_dir, f"speed_at_{cadence}rpm.md") save_to_file(table, speed_filename) # Save summary table summary_filename = os.path.join(output_dir, "summary_table.md") save_to_file(summary_table, summary_filename) if __name__ == "__main__": main()