lib/driving_physics/gearbox.rb in driving_physics-0.0.2.1 vs lib/driving_physics/gearbox.rb in driving_physics-0.0.3.1

- old
+ new

@@ -11,20 +11,34 @@ # 5000 RPM is around 375 MPH with a standard wheel/tire # 3.73 final drive reduces that to around 100 mph in e.g. 5th gear (1.0) # Likewise, 1st gear is a _smaller_ gear ratio than 3rd class Gearbox class Disengaged < RuntimeError; end + class ClutchDisengage < Disengaged; end RATIOS = [1/5r, 2/5r, 5/9r, 5/7r, 1r, 5/4r] FINAL_DRIVE = 11/41r # 1/3.73 + REVERSE = -1 + REVERSE_RATIO = -1/10r - attr_accessor :gear, :ratios, :final_drive, :spinner, :fixed_mass + attr_accessor :ratios, :final_drive, :spinner, :fixed_mass + attr_reader :gear, :clutch + def self.gear_interval!(gear, min: REVERSE, max:) + if gear < min or gear > max + raise(ArgumentError, format("gear %s should be between %d and %d", + gear.inspect, min, max)) + end + raise(ArgumentError, "gear should be an integer") if !gear.is_a? Integer + gear + end + def initialize(env) @ratios = RATIOS @final_drive = FINAL_DRIVE - @gear = 0 # neutral + @gear = 0 # neutral + @clutch = 1.0 # fully engaged (clutch pedal out) # represent all rotating mass @spinner = Disk.new(env) { |m| m.radius = 0.15 m.base_friction = 5.0/1000 @@ -34,10 +48,14 @@ @fixed_mass = 30 # kg yield self if block_given? end + def clutch=(val) + @clutch = DrivingPhysics.unit_interval! val + end + # given torque, apply friction, determine alpha def alpha(torque, omega: 0) @spinner.alpha(torque + @spinner.rotating_friction(omega)) end @@ -51,59 +69,71 @@ def mass @fixed_mass + @spinner.mass end + def gear=(val) + @gear = self.class.gear_interval!(val, max: self.top_gear) + end + def top_gear @ratios.length end def to_s - [format("Ratios: %s", @ratios.inspect), + [self.inputs, + format("Ratios: %s", @ratios.inspect), format(" Final: %s Mass: %.1f kg Rotating: %.1f kg", @final_drive.inspect, self.mass, @spinner.mass), ].join("\n") end + def inputs + format("Gear: %d Clutch: %.1f%%", @gear, @clutch * 100) + end + def ratio(gear = nil) gear ||= @gear - raise(Disengaged, "Cannot determine gear ratio") if @gear == 0 - @ratios.fetch(gear - 1) * @final_drive + case gear + when REVERSE + REVERSE_RATIO * @final_drive + when 0 + raise(Disengaged, "Cannot determine gear ratio") + else + @ratios.fetch(gear - 1) * @final_drive + end end def axle_torque(crank_torque) - crank_torque / self.ratio + crank_torque * @clutch / self.ratio end - def axle_omega(crank_rpm) - DrivingPhysics.omega(crank_rpm) * self.ratio + def output_torque(crank_torque, crank_rpm, axle_omega: nil) + axle_alpha = self.alpha(self.axle_torque(crank_torque), + omega: self.axle_omega(crank_rpm, + axle_omega: axle_omega)) + self.implied_torque(axle_alpha) end - def crank_rpm(axle_omega) - DrivingPhysics.rpm(axle_omega) / self.ratio - end - - def match_rpms(old_rpm, new_rpm) - diff = new_rpm - old_rpm - proportion = diff.to_f / old_rpm - if proportion.abs < 0.01 - [:ok, proportion] - elsif proportion.abs < 0.1 - [:slip, proportion] - elsif @gear == 1 and new_rpm < old_rpm and old_rpm <= 1500 - [:get_rolling, proportion] - else - [:mismatch, proportion] + # take into account the old axle_omega and @clutch + def axle_omega(crank_rpm, axle_omega: nil) + new_axle_omega = DrivingPhysics.omega(crank_rpm) * self.ratio + if axle_omega.nil? + raise(ClutchDisengage, "cannot determine axle omega") if @clutch != 1.0 + return new_axle_omega end + diff = new_axle_omega - axle_omega + axle_omega + diff * @clutch end - def next_gear(rpm, floor: 2500, ceiling: 6400) - if rpm < floor and @gear > 1 - @gear - 1 - elsif rpm > ceiling and @gear < self.top_gear - @gear + 1 - else - @gear + # take into account the old crank_rpm and @clutch + # crank will tolerate mismatch more than axle + def crank_rpm(axle_omega, crank_rpm: nil) + new_crank_rpm = DrivingPhysics.rpm(axle_omega) / self.ratio + if crank_rpm.nil? + raise(ClutchDisengage, "cannot determine crank rpm") if @clutch != 1.0 + return new_crank_rpm end + crank_rpm + (new_crank_rpm - crank_rpm) * @clutch end end end