driving_physics 0.0.0.2 → 0.0.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/test/tire.rb CHANGED
@@ -1,125 +1,181 @@
1
- require 'driving_physics/tire'
2
1
  require 'minitest/autorun'
2
+ require 'driving_physics/tire'
3
3
 
4
- include DrivingPhysics
5
-
6
- describe Tire do
7
- TP = Tire::TemperatureProfile
4
+ T = DrivingPhysics::Tire
8
5
 
9
- describe TP do
10
- before do
11
- @tp = TP.new
12
- end
6
+ describe T do
7
+ describe "Tire.traction" do
8
+ it "calculates traction force from normal force and coeff of friction" do
9
+ scalar_nf = 9800 # N
10
+ cof = 1.1
11
+ scalar_t = T.traction(scalar_nf, cof)
12
+ expect(scalar_t).must_equal 10780.0
13
13
 
14
- it "initializes with two same-sized arrays" do
15
- expect(@tp).must_be_kind_of TP
16
- expect(TP.new([0,1,2,3], [0,1.0,0.7,0.4])).wont_be_nil
17
- expect { TP.new('', '') }.must_raise ArgumentError
18
- expect { TP.new([]) }.must_raise ArgumentError
19
- expect { TP.new([0], []) }.must_raise ArgumentError
20
- expect { TP.new([], [0.0]) }.must_raise ArgumentError
14
+ vector_nf = Vector[9800, 0]
15
+ vector_t = T.traction(vector_nf, cof)
16
+ expect(vector_t).must_equal Vector[10780.0, 0.0]
21
17
  end
18
+ end
22
19
 
23
- it "determines a grip number from a temp number" do
24
- { -500 => TP::MIN_GRIP,
25
- -100 => 0.1,
26
- -0.0001 => 0.1,
27
- 0.0 => 0.5
28
- }.each { |temp, gf| expect(@tp.grip_factor(temp)).must_equal gf }
29
- end
20
+ describe "Tire.volume" do
21
+ it "calculates the volume (m^3) of disk given radius and width" do
22
+ cubic_m = T.volume(1.0, 1.0)
23
+ expect(cubic_m).must_equal Math::PI
30
24
 
31
- it "has a critical_temp above the temp for 100%" do
32
- expect(@tp.critical_temp).must_be(:>, 90)
33
- expect(@tp.critical_temp).must_equal 105
25
+ cubic_m = T.volume(0.35, 0.2)
26
+ expect(cubic_m).must_be_within_epsilon 0.076969
34
27
  end
28
+ end
35
29
 
36
- it "has a map that increases to 100% and decreases below 80%" do
37
- expect {
38
- TP.new([0,1,2,3,4], [0.0,0.1,0.2,0.3,0.4])
39
- }.must_raise TP::Error
30
+ describe "Tire.volume_l" do
31
+ it "calculates the volume (L) of a disk given radius and width" do
32
+ liters = T.volume_l(1.0, 1.0)
33
+ expect(liters).must_equal Math::PI * 1000
40
34
 
41
- expect {
42
- TP.new([0,1,2,3,4], [0.0, 1.0, 0.99, 0.98, 0.97])
43
- }.must_raise TP::Error
35
+ liters = T.volume_l(0.35, 0.2)
36
+ expect(liters).must_be_within_epsilon 76.96902
44
37
  end
45
38
  end
46
39
 
47
- before do
48
- @t = Tire.new
40
+ describe "Tire.density" do
41
+ it "calculates the density (kg/L) given mass and volume" do
42
+ expect(T.density(25.0, 25.0)).must_equal 1.0
43
+ expect(T.density(50.0, 25.0)).must_equal 2.0
44
+ end
49
45
  end
50
46
 
51
- it "initializes with default values without a block" do
52
- expect(@t).must_be_kind_of Tire
47
+ describe "Tire.mass" do
48
+ it "calculates the mass (kg) of a disk given radius, width, and density" do
49
+ expect(T.mass(0.35, 0.2, T::DENSITY)).must_be_within_epsilon 25.015
50
+ end
53
51
  end
54
52
 
55
- it "accepts a block to initialize with custom values" do
56
- t = Tire.new { |x|
57
- x.tread_mm = 9999
58
- x.g_factor = -0.1234
59
- }
60
-
61
- expect(t.tread_mm).must_equal 9999
62
- expect(t.g_factor).must_equal(-0.1234)
53
+ describe "Tire.rotational_inertia" do
54
+ it "calculates rotational inertia for a disk given radius and mass" do
55
+ expect(T.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
56
+ end
63
57
  end
64
58
 
65
- it "knows when the tread is gone" do
66
- expect(@t.tread_left?).must_equal true
67
-
68
- @t.condition.tread_mm = 0.00001
69
- expect(@t.tread_left?).must_equal true
70
-
71
- @t.condition.tread_mm = 0.0
72
- expect(@t.tread_left?).must_equal false
59
+ describe "Tire.alpha" do
60
+ it "calculates angular acceleration from torque and inertia" do
61
+ scalar_torque = 1000
62
+ inertia = T.rotational_inertia(0.35, 25.0)
63
+ expect(T.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
64
+
65
+ vector_torque = Vector[0, 0, 1000]
66
+ vector_alpha = T.alpha vector_torque, inertia
67
+ expect(vector_alpha).must_be_instance_of Vector
68
+ expect(vector_alpha.size).must_equal 3
69
+ expect(vector_alpha[2]).must_be_within_epsilon 653.06
70
+ end
73
71
  end
74
72
 
75
- it "has 50% grip when down to the cords" do
76
- expect(@t.tread_factor).must_equal 1.0
73
+ describe "Tire.torque_vector" do
74
+ it "calculates a torque in the 3rd dimension given 2D force and radius" do
75
+ force = Vector[1000, 0]
76
+ radius = Vector[0, 5]
77
+ torque = T.torque_vector(force, radius)
78
+ expect(torque).must_be_instance_of Vector
79
+ expect(torque.size).must_equal 3
80
+ expect(torque[2]).must_be_within_epsilon 5000.0
81
+ end
82
+ end
77
83
 
78
- @t.condition.tread_mm = 0.0
79
- expect(@t.tread_factor).must_be_within_epsilon 0.5
84
+ describe "Tire.force_vector" do
85
+ it "calculates a (3D) force given 3D torque and 2D radius" do
86
+ # let's invert the Tire.torque_vector case from above:
87
+ torque = Vector[0, 0, 5000]
88
+ radius = Vector[0, 5]
89
+ force = T.force_vector(torque, radius)
90
+ expect(force).must_be_instance_of Vector
91
+ expect(force.size).must_equal 3
92
+ expect(force[0]).must_be_within_epsilon 1000.0
93
+
94
+ # now let's rotate the radius into the x-dimension
95
+ # right hand rule, positive torque means thumb into screen, clockwise
96
+ # negative-x radius means positive-y force
97
+ torque = Vector[0, 0, 500]
98
+ radius = Vector[-5, 0]
99
+ force = T.force_vector(torque, radius)
100
+ expect(force).must_be_instance_of Vector
101
+ expect(force.size).must_equal 3
102
+ expect(force[1]).must_be_within_epsilon 100.0
103
+ end
80
104
  end
81
105
 
82
- it "has less than 10% tread factor when the cords start to wear" do
83
- expect(@t.tread_factor).must_equal 1.0
106
+ describe "instance methods" do
107
+ before do
108
+ @env = DrivingPhysics::Environment.new
109
+ @tire = T.new(@env)
110
+ end
84
111
 
85
- @t.condition.tread_mm = 5.0
86
- expect(@t.tread_factor).must_equal 1.0
112
+ it "initializes" do
113
+ expect(@tire).must_be_instance_of T
114
+ expect(@tire.density).must_equal T::DENSITY # sanity check
115
+ expect(@tire.mass).must_be_within_epsilon 25.01
87
116
 
88
- @t.condition.tread_mm = 0.0
89
- expect(@t.tread_factor).must_be_within_epsilon 0.5
117
+ with_mass = T.new(@env) { |w|
118
+ w.mass = 99.01
119
+ }
120
+ expect(with_mass.mass).must_equal 99.01
121
+ expect(with_mass.density).wont_equal T::DENSITY
122
+ end
90
123
 
91
- @t.condition.cords_mm = 0.9
92
- expect(@t.tread_factor).must_be_within_epsilon 0.45
93
- end
124
+ it "has a string representation" do
125
+ str = @tire.to_s
126
+ expect(str).must_be_instance_of String
127
+ expect(str.length).must_be(:>, 5)
128
+ end
94
129
 
95
- it "has decreasing heat cycle factor" do
96
- expect(@t.condition.heat_cycles).must_equal 0
97
- expect(@t.heat_cycle_factor).must_equal 1.0
130
+ it "loses radius as it wears" do
131
+ old_r = @tire.radius
132
+ wear_amt = 50/1000r
133
+ @tire.wear! wear_amt
134
+ expect(@tire.radius).must_equal old_r - wear_amt
135
+ end
98
136
 
99
- @t.condition.heat_cycles = 20
100
- expect(@t.heat_cycle_factor).must_be(:<, 1.0)
101
- end
137
+ it "calculates mass from current radius" do
138
+ expect(@tire.mass).must_be_within_epsilon 25.01
139
+ @tire.wear!(50/1000r)
140
+ expect(@tire.mass).must_be_within_epsilon 18.378
141
+ end
102
142
 
103
- it "has a temp factor according to temperature profile" do
104
- expect(@t.condition.temp_c).must_equal 25
105
- expect(@t.temp_factor).must_equal 0.75
143
+ it "has volume" do
144
+ expect(@tire.volume).must_be_within_epsilon 0.07697
145
+ expect(@tire.volume_l).must_be_within_epsilon 76.96902
146
+ end
106
147
 
107
- @t.condition.temp_c = 90
108
- expect(@t.temp_factor).must_equal 1.0
109
- end
148
+ it "has inertia" do
149
+ expect(@tire.rotational_inertia).must_be_within_epsilon 1.5321
150
+ end
110
151
 
111
- it "incorporates temp, heat_cycles, and tread depth into available grip" do
112
- @t.condition.temp_c = 60
113
- expect(@t.temp_factor).must_be_within_epsilon 0.8
152
+ it "has traction force based on normal force" do
153
+ scalar_nf = 9800
154
+ expect(@tire.traction scalar_nf).must_equal 10780.0
155
+ expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
114
156
 
115
- @t.condition.heat_cycles = 10
116
- expect(@t.heat_cycle_factor).must_be_within_epsilon 0.96
157
+ vector_nf = Vector[9800, 0]
158
+ expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
159
+ expect(@tire.traction vector_nf, static: false).
160
+ must_equal Vector[6860.0, 0.0]
161
+ end
162
+
163
+ it "determines (e.g. thrust) force based on axle torque" do
164
+ expect(@tire.force 1000).must_be_within_epsilon 2857.143
165
+ @tire.wear! 50/1000r
166
+ expect(@tire.force 1000).must_be_within_epsilon 3333.333
167
+ end
117
168
 
118
- @t.condition.tread_mm = 5.0
119
- expect(@t.tread_factor).must_equal 1.0
120
- expect(@t.max_g).must_be_within_epsilon 0.768
169
+ it "determines tractable torque" do
170
+ scalar_nf = 9800
171
+ expect(@tire.tractable_torque scalar_nf).must_be_within_epsilon 3773.0
172
+ kin_tq = @tire.tractable_torque scalar_nf, static: false
173
+ expect(kin_tq).must_be_within_epsilon 2401.0
121
174
 
122
- @t.condition.tread_mm = 0.0
123
- expect(@t.max_g).must_be_within_epsilon 0.384
175
+ # not sure about how torque vectors work, but the "math" "works":
176
+ vector_nf = Vector[9800, 0]
177
+ expect(@tire.tractable_torque(vector_nf)[0]).
178
+ must_be_within_epsilon 3773.0
179
+ end
124
180
  end
125
181
  end
data/test/vector_force.rb CHANGED
@@ -74,9 +74,15 @@ describe VectorForce do
74
74
  end
75
75
 
76
76
  it "calculates the rotational resistance as a function of velocity" do
77
+ rr = VectorForce.rotational_resistance(@v, rot_const: 0)
78
+ rr2 = VectorForce.rotational_resistance(@v * 2, rot_const: 0)
79
+ expect(rr2).must_equal rr * 2
80
+
81
+ # now with rot_const != 0, the relationship is skewed
77
82
  rr = VectorForce.rotational_resistance(@v)
78
83
  rr2 = VectorForce.rotational_resistance(@v * 2)
79
- expect(rr2).must_equal rr * 2
84
+ expect(rr2).wont_equal rr * 2 # because of rot_const
85
+ expect(rr2.magnitude).must_be(:<, (rr * 2).magnitude)
80
86
  end
81
87
 
82
88
  it "sums resistance forces" do
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: driving_physics
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.0.2
4
+ version: 0.0.1.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -20,25 +20,32 @@ files:
20
20
  - Rakefile
21
21
  - VERSION
22
22
  - demo/car.rb
23
+ - demo/disk.rb
24
+ - demo/gearbox.rb
25
+ - demo/motor.rb
26
+ - demo/powertrain.rb
23
27
  - demo/scalar_force.rb
24
28
  - demo/tire.rb
25
29
  - demo/vector_force.rb
26
- - demo/wheel.rb
27
30
  - driving_physics.gemspec
28
31
  - lib/driving_physics.rb
29
32
  - lib/driving_physics/car.rb
33
+ - lib/driving_physics/cli.rb
34
+ - lib/driving_physics/disk.rb
30
35
  - lib/driving_physics/environment.rb
36
+ - lib/driving_physics/gearbox.rb
31
37
  - lib/driving_physics/imperial.rb
38
+ - lib/driving_physics/motor.rb
39
+ - lib/driving_physics/power.rb
40
+ - lib/driving_physics/powertrain.rb
32
41
  - lib/driving_physics/scalar_force.rb
33
42
  - lib/driving_physics/tire.rb
34
43
  - lib/driving_physics/vector_force.rb
35
- - lib/driving_physics/wheel.rb
36
- - test/car.rb
44
+ - test/disk.rb
37
45
  - test/driving_physics.rb
38
46
  - test/scalar_force.rb
39
47
  - test/tire.rb
40
48
  - test/vector_force.rb
41
- - test/wheel.rb
42
49
  homepage: https://github.com/rickhull/driving_physics
43
50
  licenses:
44
51
  - LGPL-3.0
data/demo/wheel.rb DELETED
@@ -1,84 +0,0 @@
1
- require 'driving_physics/wheel'
2
-
3
- include DrivingPhysics
4
-
5
- env = Environment.new
6
- wheel = Wheel.new(env, mass: 25.0)
7
-
8
- puts env
9
- puts wheel
10
-
11
- # 1000 kg car
12
- # 4 tires
13
- # 250 kg per tire plus tire mass
14
-
15
- supported_mass = 1000 # kg
16
- total_mass = supported_mass + 4 * wheel.mass
17
- corner_mass = Rational(total_mass) / 4
18
- normal_force = corner_mass * env.g
19
- axle_torque = 1000 # N*m
20
- friction_loss = 0.05 # 5% friction / hysteresis loss
21
-
22
- puts [format("Corner mass: %d kg", corner_mass),
23
- format("Normal force: %.1f N", normal_force),
24
- format("Axle torque: %d Nm", axle_torque),
25
- ].join("\n")
26
- puts
27
-
28
- traction = wheel.traction(normal_force)
29
- drive_force = wheel.force(axle_torque)
30
- inertial_loss = wheel.inertial_loss(axle_torque, supported_mass)
31
- friction_loss *= axle_torque # 5% of the axle torque
32
-
33
- # drive force = (axle torque - inertia - friction) limited by traction
34
-
35
- net_axle_torque = axle_torque - inertial_loss - friction_loss
36
- net_drive_force = wheel.force(net_axle_torque)
37
- net_drive_force = traction if net_drive_force > traction # traction limited
38
-
39
- acc = DrivingPhysics.acc(net_drive_force, supported_mass) # translational
40
-
41
- puts [format("Traction: %.1f N", traction),
42
- format("Drive force: %.1f N", drive_force),
43
- format("Inertial loss: %.1f Nm", inertial_loss),
44
- format("Friction loss: %.1f Nm", friction_loss),
45
- format("Net Axle Torque: %.1f Nm", net_axle_torque),
46
- format("Net Drive Force: %.1f N", net_drive_force),
47
- format("Acceleration: %.1f m/s/s", acc),
48
- format("Alpha: %.2f r/s/s", acc / wheel.radius_m),
49
- ].join("\n")
50
- puts
51
-
52
- duration = 100 # sec
53
-
54
- dist = 0.0 # meters
55
- speed = 0.0 # meters/s
56
-
57
- theta = 0.0 # radians
58
- omega = 0.0 # radians/s
59
-
60
- (duration * env.hz).times { |i|
61
- # accumulate frictional losses with speed (omega)
62
- omega_loss_cof = [wheel.omega_friction * omega, 1.0].min
63
- slowed_acc = acc - acc * omega_loss_cof
64
-
65
- # translational kinematics
66
- speed += slowed_acc * env.tick
67
- dist += speed * env.tick
68
-
69
- # rotational kinematics
70
- alpha = slowed_acc / wheel.radius_m
71
- omega += alpha * env.tick
72
- theta += omega * env.tick
73
-
74
- if i < 10 or
75
- (i < 10_000 and i%1000 == 0) or
76
- (i % 10_000 == 0)
77
- puts DrivingPhysics.elapsed_display(i)
78
- puts format("Wheel: %.1f r %.2f r/s %.3f r/s^2", theta, omega, alpha)
79
- puts format(" Car: %.1f m %.2f m/s %.3f m/s^2", dist, speed, slowed_acc)
80
- puts format("Omega Frictional Loss: %.1f%%", omega_loss_cof * 100)
81
- puts "Press [enter]"
82
- gets
83
- end
84
- }
@@ -1,191 +0,0 @@
1
- require 'driving_physics/environment'
2
- require 'driving_physics/vector_force'
3
-
4
- module DrivingPhysics
5
- # Rotational complements to acc/vel/pos
6
- # alpha - angular acceleration
7
- # omega - angular velocity (radians / s)
8
- # theta - radians
9
-
10
- class Wheel
11
- # Note, this is not the density of solid rubber. This density
12
- # yields a sensible mass for a wheel / tire combo at common radius
13
- # and width, assuming a uniform density
14
- # e.g. 25kg at 350mm R x 200mm W
15
- #
16
- DENSITY = 0.325 # kg / L
17
-
18
- # * the traction force opposes the axle torque / drive force
19
- # thus, driving the car forward
20
- # * if the drive force exceeds the traction force, slippage occurs
21
- # * slippage reduces the available traction force further
22
- # * if the drive force is not reduced, the slippage increases
23
- # until resistance forces equal the drive force
24
- def self.traction(normal_force, cof)
25
- normal_force * cof
26
- end
27
-
28
- def self.force(axle_torque, radius_m)
29
- axle_torque / radius_m.to_f
30
- end
31
-
32
- # in m^3
33
- def self.volume(radius_m, width_m)
34
- Math::PI * radius_m ** 2 * width_m.to_f
35
- end
36
-
37
- # in L
38
- def self.volume_l(radius_m, width_m)
39
- volume(radius_m, width_m) * 1000
40
- end
41
-
42
- def self.density(mass, volume_l)
43
- mass.to_f / volume_l
44
- end
45
-
46
- def self.mass(radius_m, width_m, density)
47
- density * volume_l(radius_m, width_m)
48
- end
49
-
50
- # I = 1/2 (m)(r^2) for a disk
51
- def self.rotational_inertia(radius_m, mass)
52
- mass * radius_m**2 / 2.0
53
- end
54
- class << self
55
- alias_method(:moment_of_inertia, :rotational_inertia)
56
- end
57
-
58
- # angular acceleration
59
- def self.alpha(torque, inertia)
60
- torque / inertia
61
- end
62
-
63
- def self.tangential(rotational, radius_m)
64
- rotational * radius_m
65
- end
66
- class << self
67
- alias_method(:tangential_a, :tangential)
68
- alias_method(:tangential_v, :tangential)
69
- alias_method(:tangential_p, :tangential)
70
- end
71
-
72
- # vectors only
73
- def self.torque_vector(force, radius)
74
- if !force.is_a?(Vector) or force.size != 2
75
- raise(ArgumentError, "force must be a 2D vector")
76
- end
77
- if !radius.is_a?(Vector) or radius.size != 2
78
- raise(ArgumentError, "radius must be a 2D vector")
79
- end
80
- force = Vector[force[0], force[1], 0]
81
- radius = Vector[radius[0], radius[1], 0]
82
- force.cross(radius)
83
- end
84
-
85
- # vectors only
86
- def self.force_vector(torque, radius)
87
- if !torque.is_a?(Vector) or torque.size != 3
88
- raise(ArgumentError, "torque must be a 3D vector")
89
- end
90
- if !radius.is_a?(Vector) or radius.size != 2
91
- raise(ArgumentError, "radius must be a 2D vector")
92
- end
93
- radius = Vector[radius[0], radius[1], 0]
94
- radius.cross(torque) / radius.dot(radius)
95
- end
96
-
97
- attr_reader :env, :radius, :radius_m, :width, :width_m, :density, :temp,
98
- :mu_s, :mu_k, :omega_friction
99
-
100
- def initialize(env,
101
- radius: 350, width: 200, density: DENSITY,
102
- temp: nil, mass: nil,
103
- mu_s: 1.1, mu_k: 0.7,
104
- omega_friction: 0.002)
105
- @env = env
106
- @radius = radius.to_f # mm
107
- @radius_m = @radius / 1000
108
- @width = width.to_f # mm
109
- @width_m = @width / 1000
110
- @mu_s = mu_s.to_f # static friction
111
- @mu_k = mu_k.to_f # kinetic friction
112
- @omega_friction = omega_friction # scales with speed
113
- @density = mass.nil? ? density : self.class.density(mass, volume_l)
114
- @temp = temp.to_f || @env.air_temp
115
- end
116
-
117
- def to_s
118
- [[format("%d mm (R) x %d mm (W)", @radius, @width),
119
- format("Mass: %.1f kg %.3f kg/L", self.mass, @density),
120
- format("cF: %.1f / %.1f", @mu_s, @mu_k),
121
- ].join(" | "),
122
- [format("Temp: %.1f C", @temp),
123
- ].join(" | "),
124
- ].join("\n")
125
- end
126
-
127
- def wear!(amount_mm)
128
- @radius -= amount_mm
129
- @radius_m = @radius / 1000
130
- end
131
-
132
- def heat!(amount_deg_c)
133
- @temp += amount_deg_c
134
- end
135
-
136
- def mass
137
- self.class.mass(@radius_m, @width_m, @density)
138
- end
139
-
140
- # in m^3
141
- def volume
142
- self.class.volume(@radius_m, @width_m)
143
- end
144
-
145
- # in L
146
- def volume_l
147
- self.class.volume_l(@radius_m, @width_m)
148
- end
149
-
150
- def rotational_inertia
151
- self.class.rotational_inertia(@radius_m, self.mass)
152
- end
153
- alias_method(:moment_of_inertia, :rotational_inertia)
154
-
155
- def traction(nf, static: true)
156
- self.class.traction(nf, static ? @mu_s : @mu_k)
157
- end
158
-
159
- def force(axle_torque)
160
- self.class.force(axle_torque, @radius_m)
161
- end
162
-
163
- # how much torque to accelerate rotational inertia at alpha
164
- def inertial_torque(alpha)
165
- alpha * self.rotational_inertia
166
- end
167
-
168
- # this doesn't take inertial losses or internal frictional losses
169
- # into account. input torque required to saturate traction will be
170
- # higher than what this method returns
171
- def tractable_torque(nf, static: true)
172
- traction(nf, static: static) * @radius_m
173
- end
174
-
175
- # inertial loss in terms of axle torque when used as a drive wheel
176
- def inertial_loss(axle_torque, total_driven_mass)
177
- drive_force = self.force(axle_torque)
178
- force_loss = 0
179
- # The force loss depends on the acceleration, but the acceleration
180
- # depends on the force loss. Converge the value via 5 round trips.
181
- # This is a rough way to compute an integral and should be accurate
182
- # to 8+ digits.
183
- 5.times {
184
- acc = DrivingPhysics.acc(drive_force - force_loss, total_driven_mass)
185
- alpha = acc / @radius_m
186
- force_loss = self.inertial_torque(alpha) / @radius_m
187
- }
188
- force_loss * @radius_m
189
- end
190
- end
191
- end