Initial Commit, first submission to SRAM

This commit is contained in:
2021-08-23 18:06:05 -05:00
commit 71fffe1cb6
2 changed files with 231 additions and 0 deletions

163
bicycle_drive_train.py Normal file
View File

@@ -0,0 +1,163 @@
from typing import List, Tuple
class BikeDriveTrain:
"""
Class representing a bicycle drive train. Only a single cog on the front and single cog on the rear can be selected
at one time.
Note
----
Gear combination and ratio types are represented as a Tuple[int, int, float] with the integers representing front
cog, and rear cog respectively, and the float representing the gear ratio. In the future, it would make sense for
this to be a named tuple, alias of namedtuple, or even class of its own to be more explicit.
"""
def __init__(self, front_cogs: List[int], rear_cogs: List[int]):
"""
Parameters
----------
front_cogs: list[int]
List of integers representing tooth counts for cogs on front crank.
rear_cogs: list[int]
List of integers representing tooth counts for cogs on rear cassette.
"""
#TODO Error handling for invalid inputs. Consider adding warnings for nonsensical gears/gear combinations
# Assign respective cogs to instance.
# ASSUMPTION: Gear sets won't need change after object creation, enforced as a read-only property.
self._front_cogs = front_cogs
self._rear_cogs = rear_cogs
# Calculate the ratios for this drive train.
# ASSUMPTION: Since front_cogs and rear_cogs won't change after instantiation, neither will the gear ratios. As
# such, we only have to do this once, so it makes sense to do it at instantiation.
# ASSUMPTION: Practically, real bicycles will have a small set of gearing combinations, so this method is
# computationally trivial.
gear_combinations_and_ratios = []
for front_cog in front_cogs:
for rear_cog in rear_cogs:
gear_combinations_and_ratios.append((front_cog, rear_cog, front_cog / rear_cog))
self._gear_combinations_and_ratios = gear_combinations_and_ratios
# Getters for the cogs
@property
def front_cogs(self):
"""
list of int: List of integers representing tooth counts for cogs on front crank.
"""
return self._front_cogs
@property
def rear_cogs(self):
"""
list of int: List of integers representing tooth counts for cogs on rear cassette.
"""
return self._rear_cogs
@property
def gear_combinations_and_ratios(self) -> List[Tuple[int, int, float]]:
"""
list of tuple of (int, int, float): List of tuples describing the possible gear combinations and their
respective gear ratio in the form (front_cog, rear_cog, gear ratio).
"""
# ASSUMPTION: This information will be useful to uses of the class. It is a convenience function for the
# class itself
return self._gear_combinations_and_ratios
def get_gear_combination(self, target_ratio: float) -> Tuple[int, int, float]:
"""
Find the gear combination and its respective gear ratio that is nearest (but not over) target_ratio
Parameters
----------
target_ratio
A float representing the desired gear ratio.
Returns
-------
A tuple describing the gear combination and its respective gear ratio that is nearest (but not over)
target_ratio in the form (front_cog, rear_cog, gear ratio).
"""
# first sort gear_combination_and_ratios from smallest to largest.
candidate_gear_combinations_and_ratios = sorted(self.gear_combinations_and_ratios,
key=lambda gear_combination_and_ratio:
gear_combination_and_ratio[2])
# then eliminate gear ratios that are greater than the target.
# #TODO error handling if all elements are eliminated
candidate_gear_combinations_and_ratios = [(front_cog, rear_cog, gear_ratio) for
(front_cog, rear_cog, gear_ratio) in
candidate_gear_combinations_and_ratios if gear_ratio < target_ratio]
# and the nearest without going over ratio is the last element of the candidate list
target_gear_combination_and_ratio = candidate_gear_combinations_and_ratios[-1]
return target_gear_combination_and_ratio
pass
def get_shift_sequence(self, target_ratio: float, initial_gear_combination: Tuple[int, int]) -> \
List[Tuple[int, int, float]]:
"""
A method that returns a shift sequence to traverse from an initial gear combination to a gear combination with
the closest ratio that is less than or equal to the target ratio, following first shifting the front to the
final gear, then shift the rear to the final gear.
Parameters
----------
target_ratio
A float representing the desired gear ratio.
initial_gear_combination
The starting gear combination in the form of (front_gear, rear_gear) where front_gear and rear_gear are
integers describing the number of teeth in specified gear.
Returns
-------
List of tuple of int, int, float: Steps in gear shifting sequence in the form
(front_cog, rear_cog, gear ratio)
"""
target_gear_combination_and_ratio = self.get_gear_combination(target_ratio)
# TODO implement this method, using the rough steps below.
# first determine if it is a down-shift or an up-shift.
# sort shifting steps (large cog first, then small cog) depending on whether it's a down-shift or up-shift.
# filter the list depending on target ratio, starting with the initial gear combination and stopping when the
# closest ratio to the target is achieved.
# return list
# TODO In the meantime, this function will return a "not implemented" error.
return self.gear_combinations_and_ratios
def produce_formatted_shift_sequence(self, target_ratio: float, initial_gear_combination: Tuple[int, int]):
"""
A method to produce a formatted shift sequence for a given target ratio and initial gear combination.
Parameters
----------
target_ratio
A float representing the desired gear ratio.
initial_gear_combination
The starting gear combination in the form of (front_gear, rear_gear) where front_gear and rear_gear are
integers describing the number of teeth in specified gear.
Returns
-------
None: Method only prints out the sequence to the console.
"""
# get the sequence using method.
sequence = self.get_shift_sequence(target_ratio, initial_gear_combination)
# print the sequence using string formatter
for i, (front_gear, rear_gear, ratio) in enumerate(sequence):
print(f"{i}: F:{front_gear}, R:{rear_gear}, {ratio:3f}")