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