driving_physics 0.0.2.1 → 0.0.3.1
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.
- checksums.yaml +4 -4
- data/README.md +17 -14
- data/Rakefile +3 -3
- data/VERSION +1 -1
- data/demo/car.rb +83 -73
- data/demo/motor.rb +7 -4
- data/demo/mruby/car.rb +184 -0
- data/demo/mruby/motor.rb +7 -4
- data/demo/pid_controller.rb +143 -0
- data/demo/powertrain.rb +1 -1
- data/driving_physics.gemspec +2 -1
- data/lib/driving_physics/car.rb +13 -4
- data/lib/driving_physics/cli.rb +7 -35
- data/lib/driving_physics/cockpit.rb +167 -0
- data/lib/driving_physics/disk.rb +5 -25
- data/lib/driving_physics/environment.rb +2 -0
- data/lib/driving_physics/gearbox.rb +60 -30
- data/lib/driving_physics/motor.rb +166 -58
- data/lib/driving_physics/mruby.rb +3 -0
- data/lib/driving_physics/pid_controller.rb +111 -0
- data/lib/driving_physics/powertrain.rb +8 -13
- data/lib/driving_physics/scalar_force.rb +2 -1
- data/lib/driving_physics/timer.rb +34 -0
- data/lib/driving_physics/vector_force.rb +23 -0
- data/lib/driving_physics.rb +28 -22
- data/test/disk.rb +23 -62
- data/test/gearbox.rb +36 -0
- data/test/motor.rb +236 -0
- data/test/powertrain.rb +21 -0
- data/test/scalar_force.rb +3 -6
- data/test/tire.rb +25 -59
- data/test/vector_force.rb +76 -37
- metadata +26 -5
- data/demo/mruby/disk.rb +0 -81
data/test/disk.rb
CHANGED
@@ -1,116 +1,78 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'driving_physics/disk'
|
3
|
+
require 'matrix'
|
3
4
|
|
4
|
-
|
5
|
+
include DrivingPhysics
|
5
6
|
|
6
|
-
describe
|
7
|
+
describe Disk do
|
7
8
|
describe "Disk.volume" do
|
8
9
|
it "calculates the volume (m^3) of disk given radius and width" do
|
9
|
-
cubic_m =
|
10
|
+
cubic_m = Disk.volume(1.0, 1.0)
|
10
11
|
expect(cubic_m).must_equal Math::PI
|
11
12
|
|
12
|
-
cubic_m =
|
13
|
+
cubic_m = Disk.volume(0.35, 0.2)
|
13
14
|
expect(cubic_m).must_be_within_epsilon 0.076969
|
14
15
|
end
|
15
16
|
end
|
16
17
|
|
17
18
|
describe "Disk.volume_l" do
|
18
19
|
it "calculates the volume (L) of a disk given radius and width" do
|
19
|
-
liters =
|
20
|
+
liters = Disk.volume_l(1.0, 1.0)
|
20
21
|
expect(liters).must_equal Math::PI * 1000
|
21
22
|
|
22
|
-
liters =
|
23
|
+
liters = Disk.volume_l(0.35, 0.2)
|
23
24
|
expect(liters).must_be_within_epsilon 76.96902
|
24
25
|
end
|
25
26
|
end
|
26
27
|
|
27
28
|
describe "Disk.density" do
|
28
29
|
it "calculates the density (kg/L) given mass and volume" do
|
29
|
-
expect(
|
30
|
-
expect(
|
30
|
+
expect(Disk.density(25.0, 25.0)).must_equal 1.0
|
31
|
+
expect(Disk.density(50.0, 25.0)).must_equal 2.0
|
31
32
|
end
|
32
33
|
end
|
33
34
|
|
34
35
|
describe "Disk.mass" do
|
35
36
|
it "calculates the mass (kg) of a disk given radius, width, and density" do
|
36
|
-
|
37
|
-
expect(D.mass(0.35, 0.2, D::DENSITY)).must_be_within_epsilon 25.015
|
37
|
+
expect(Disk.mass(0.35, 0.2, Disk::DENSITY)).must_be_within_epsilon 76.969
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
41
|
describe "Disk.rotational_inertia" do
|
42
42
|
it "calculates rotational inertia for a disk given radius and mass" do
|
43
|
-
expect(
|
43
|
+
expect(Disk.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
47
|
describe "Disk.alpha" do
|
48
48
|
it "calculates angular acceleration from torque and inertia" do
|
49
49
|
scalar_torque = 1000
|
50
|
-
inertia =
|
51
|
-
expect(
|
50
|
+
inertia = Disk.rotational_inertia(0.35, 25.0)
|
51
|
+
expect(Disk.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
|
52
52
|
|
53
|
-
skip
|
53
|
+
skip unless DrivingPhysics.has_vector?
|
54
54
|
vector_torque = Vector[0, 0, 1000]
|
55
|
-
vector_alpha =
|
55
|
+
vector_alpha = Disk.alpha vector_torque, inertia
|
56
56
|
expect(vector_alpha).must_be_instance_of Vector
|
57
57
|
expect(vector_alpha.size).must_equal 3
|
58
58
|
expect(vector_alpha[2]).must_be_within_epsilon 653.06
|
59
59
|
end
|
60
60
|
end
|
61
61
|
|
62
|
-
describe "Disk.torque_vector" do
|
63
|
-
it "calculates a torque in the 3rd dimension given 2D force and radius" do
|
64
|
-
skip # Vector
|
65
|
-
force = Vector[1000, 0]
|
66
|
-
radius = Vector[0, 5]
|
67
|
-
torque = D.torque_vector(force, radius)
|
68
|
-
expect(torque).must_be_instance_of Vector
|
69
|
-
expect(torque.size).must_equal 3
|
70
|
-
expect(torque[2]).must_be_within_epsilon 5000.0
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
describe "Disk.force_vector" do
|
75
|
-
it "calculates a (3D) force given 3D torque and 2D radius" do
|
76
|
-
skip # Vector
|
77
|
-
# let's invert the Disk.torque_vector case from above:
|
78
|
-
torque = Vector[0, 0, 5000]
|
79
|
-
radius = Vector[0, 5]
|
80
|
-
force = D.force_vector(torque, radius)
|
81
|
-
expect(force).must_be_instance_of Vector
|
82
|
-
expect(force.size).must_equal 3
|
83
|
-
expect(force[0]).must_be_within_epsilon 1000.0
|
84
|
-
|
85
|
-
# now let's rotate the radius into the x-dimension
|
86
|
-
# right hand rule, positive torque means thumb into screen, clockwise
|
87
|
-
# negative-x radius means positive-y force
|
88
|
-
torque = Vector[0, 0, 500]
|
89
|
-
radius = Vector[-5, 0]
|
90
|
-
force = D.force_vector(torque, radius)
|
91
|
-
expect(force).must_be_instance_of Vector
|
92
|
-
expect(force.size).must_equal 3
|
93
|
-
expect(force[1]).must_be_within_epsilon 100.0
|
94
|
-
end
|
95
|
-
end
|
96
|
-
|
97
62
|
describe "instance methods" do
|
98
63
|
before do
|
99
64
|
@env = DrivingPhysics::Environment.new
|
100
|
-
@disk =
|
65
|
+
@disk = Disk.new(@env)
|
101
66
|
end
|
102
67
|
|
103
68
|
it "initializes" do
|
104
|
-
|
105
|
-
expect(@disk).
|
106
|
-
expect(@disk.
|
107
|
-
|
108
|
-
|
109
|
-
with_mass = D.new(@env) { |w|
|
110
|
-
w.mass = 99.01
|
111
|
-
}
|
69
|
+
expect(@disk).must_be_instance_of Disk
|
70
|
+
expect(@disk.density).must_equal Disk::DENSITY # sanity check
|
71
|
+
expect(@disk.mass).must_be_within_epsilon 76.969
|
72
|
+
|
73
|
+
with_mass = Disk.new(@env) { |w| w.mass = 99.01 }
|
112
74
|
expect(with_mass.mass).must_equal 99.01
|
113
|
-
expect(with_mass.density).wont_equal
|
75
|
+
expect(with_mass.density).wont_equal Disk::DENSITY
|
114
76
|
end
|
115
77
|
|
116
78
|
it "has a string representation" do
|
@@ -125,8 +87,7 @@ describe D do
|
|
125
87
|
end
|
126
88
|
|
127
89
|
it "has inertia" do
|
128
|
-
|
129
|
-
expect(@disk.rotational_inertia).must_be_within_epsilon 1.5321
|
90
|
+
expect(@disk.rotational_inertia).must_be_within_epsilon 4.714
|
130
91
|
end
|
131
92
|
end
|
132
93
|
end
|
data/test/gearbox.rb
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'driving_physics/gearbox'
|
3
|
+
|
4
|
+
include DrivingPhysics
|
5
|
+
|
6
|
+
describe Gearbox do
|
7
|
+
it "has gear ratios" do
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has a final drive" do
|
11
|
+
end
|
12
|
+
|
13
|
+
it "has a neutral gear" do
|
14
|
+
end
|
15
|
+
|
16
|
+
it "has a clutch with state" do
|
17
|
+
end
|
18
|
+
|
19
|
+
it "has mass" do
|
20
|
+
end
|
21
|
+
|
22
|
+
it "has rotating mass" do
|
23
|
+
end
|
24
|
+
|
25
|
+
it "converts crank torque to axle torque" do
|
26
|
+
end
|
27
|
+
|
28
|
+
it "has losses due to inertia and friction" do
|
29
|
+
end
|
30
|
+
|
31
|
+
it "converts crank omega to axle omega (and vice versa)" do
|
32
|
+
end
|
33
|
+
|
34
|
+
it "varies torque delivery based on gear ratio and clutch" do
|
35
|
+
end
|
36
|
+
end
|
data/test/motor.rb
ADDED
@@ -0,0 +1,236 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'driving_physics/motor'
|
3
|
+
|
4
|
+
include DrivingPhysics
|
5
|
+
|
6
|
+
describe "interpolate" do
|
7
|
+
it "validates xs and ys match in size" do
|
8
|
+
xs = [0, 5, 10, 15, 20]
|
9
|
+
ys = [10, 10, 10, 10, 5]
|
10
|
+
expect(DrivingPhysics.interpolate(1, xs: xs, ys: ys)).must_equal 10
|
11
|
+
expect { DrivingPhysics.interpolate(1, xs: xs + [10], ys: ys) }.must_raise
|
12
|
+
expect { DrivingPhysics.interpolate(1, xs: xs, ys: ys + [10]) }.must_raise
|
13
|
+
end
|
14
|
+
|
15
|
+
it "validates x is within the range of xs" do
|
16
|
+
xs = [0, 5, 10, 15, 20]
|
17
|
+
ys = [10, 10, 10, 10, 5]
|
18
|
+
expect(DrivingPhysics.interpolate(1, xs: xs, ys: ys)).must_equal 10
|
19
|
+
expect { DrivingPhysics.interpolate(-1, xs: xs, ys: ys) }.must_raise
|
20
|
+
expect { DrivingPhysics.interpolate(25, xs: xs, ys: ys) }.must_raise
|
21
|
+
end
|
22
|
+
|
23
|
+
it "validates the xs are in ascending order" do
|
24
|
+
xs = [0, 5, 10, 15, 20]
|
25
|
+
ys = [10, 10, 10, 10, 5]
|
26
|
+
expect(DrivingPhysics.interpolate(1, xs: xs, ys: ys)).must_equal 10
|
27
|
+
xs[1] = 15
|
28
|
+
expect {
|
29
|
+
DrivingPhysics.interpolate(16, xs: xs, ys: ys)
|
30
|
+
}.must_raise
|
31
|
+
end
|
32
|
+
|
33
|
+
it "performs linear interpolation to yield a y value for a valid x" do
|
34
|
+
xs = [0, 5, 10, 15, 20]
|
35
|
+
ys = [5, 5, 10, 10, 15]
|
36
|
+
|
37
|
+
expect(DrivingPhysics.interpolate(0, xs: xs, ys: ys)).must_equal 5
|
38
|
+
expect(DrivingPhysics.interpolate(1, xs: xs, ys: ys)).must_equal 5
|
39
|
+
expect(DrivingPhysics.interpolate(4, xs: xs, ys: ys)).must_equal 5
|
40
|
+
expect(DrivingPhysics.interpolate(5, xs: xs, ys: ys)).must_equal 5
|
41
|
+
expect(DrivingPhysics.interpolate(6, xs: xs, ys: ys)).must_equal 6
|
42
|
+
expect(DrivingPhysics.interpolate(7, xs: xs, ys: ys)).must_equal 7
|
43
|
+
expect(DrivingPhysics.interpolate(8, xs: xs, ys: ys)).must_equal 8
|
44
|
+
expect(DrivingPhysics.interpolate(9, xs: xs, ys: ys)).must_equal 9
|
45
|
+
expect(DrivingPhysics.interpolate(10, xs: xs, ys: ys)).must_equal 10
|
46
|
+
expect(DrivingPhysics.interpolate(11, xs: xs, ys: ys)).must_equal 10
|
47
|
+
expect(DrivingPhysics.interpolate(12, xs: xs, ys: ys)).must_equal 10
|
48
|
+
expect(DrivingPhysics.interpolate(14, xs: xs, ys: ys)).must_equal 10
|
49
|
+
expect(DrivingPhysics.interpolate(15, xs: xs, ys: ys)).must_equal 10
|
50
|
+
expect(DrivingPhysics.interpolate(16, xs: xs, ys: ys)).must_equal 11
|
51
|
+
expect(DrivingPhysics.interpolate(17, xs: xs, ys: ys)).must_equal 12
|
52
|
+
expect(DrivingPhysics.interpolate(18, xs: xs, ys: ys)).must_equal 13
|
53
|
+
expect(DrivingPhysics.interpolate(19, xs: xs, ys: ys)).must_equal 14
|
54
|
+
expect(DrivingPhysics.interpolate(20, xs: xs, ys: ys)).must_equal 15
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# shorthand
|
59
|
+
def tc(rpms, torques)
|
60
|
+
TorqueCurve.new(rpms: rpms, torques: torques)
|
61
|
+
end
|
62
|
+
|
63
|
+
describe TorqueCurve do
|
64
|
+
before do
|
65
|
+
@default = TorqueCurve.new
|
66
|
+
@rpms = [500, 1000, 3500, 6000, 7000, 7100]
|
67
|
+
@torques = [ 0, 100, 300, 300, 250, 0]
|
68
|
+
@minr = [0, 1]
|
69
|
+
@mint = [0, 0]
|
70
|
+
end
|
71
|
+
|
72
|
+
it "maps rpm values to torque values" do
|
73
|
+
expect(@default.torque(3500)).must_be(:>, 100)
|
74
|
+
end
|
75
|
+
|
76
|
+
describe "initialize" do
|
77
|
+
it "accepts two arrays: rpms, and the corresponding torques" do
|
78
|
+
expect(tc(@rpms, @torques)).must_be_kind_of(TorqueCurve)
|
79
|
+
end
|
80
|
+
|
81
|
+
it "has defaults for rpms and torques" do
|
82
|
+
expect(@default).must_be_kind_of(TorqueCurve)
|
83
|
+
end
|
84
|
+
|
85
|
+
it "validates size match for rpms and torques" do
|
86
|
+
expect( tc( @minr, @mint) ).must_be_kind_of TorqueCurve
|
87
|
+
expect { tc( [0], @mint) }.must_raise
|
88
|
+
expect { tc([0,1,2], @mint) }.must_raise
|
89
|
+
expect { tc(@minr, [0]) }.must_raise
|
90
|
+
expect { tc(@minr, [0,1,0]) }.must_raise
|
91
|
+
|
92
|
+
rpms = [100, 2000, 30_000]
|
93
|
+
torques = [0, 500, 0]
|
94
|
+
|
95
|
+
expect( tc( rpms, torques) ).must_be_kind_of TorqueCurve
|
96
|
+
expect { tc( rpms, [0, 0]) }.must_raise
|
97
|
+
expect { tc([1,2], torques) }.must_raise
|
98
|
+
expect { tc([1,2,3,4], torques) }.must_raise
|
99
|
+
end
|
100
|
+
|
101
|
+
|
102
|
+
describe "rpms" do
|
103
|
+
it "validates rpms are positive" do
|
104
|
+
expect( tc( @minr, @mint) ).must_be_kind_of TorqueCurve
|
105
|
+
expect { tc([-10, 10], @mint) }.must_raise
|
106
|
+
end
|
107
|
+
|
108
|
+
it "validates rpms are in ascending order" do
|
109
|
+
expect( tc( @minr, @mint) ).must_be_kind_of TorqueCurve
|
110
|
+
expect { tc([0, 0], @mint) }.must_raise
|
111
|
+
expect( tc([99, 100], @mint) ).must_be_kind_of TorqueCurve
|
112
|
+
expect { tc([100, 99], @mint) }.must_raise
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
describe "torques" do
|
117
|
+
it "validates that torques start and end at zero" do
|
118
|
+
expect( tc(@minr, @mint) ).must_be_kind_of TorqueCurve
|
119
|
+
expect { tc(@minr, [1,0]) }.must_raise
|
120
|
+
expect { tc(@minr, [0,1]) }.must_raise
|
121
|
+
expect { tc(@minr, [1,1]) }.must_raise
|
122
|
+
|
123
|
+
expect( tc([1,2,3], [0,500,0]) ).must_be_kind_of TorqueCurve
|
124
|
+
expect { tc([1,2,3], [1,500,0]) }.must_raise
|
125
|
+
expect { tc([1,2,3], [0,500,1]) }.must_raise
|
126
|
+
end
|
127
|
+
|
128
|
+
it "validates that torques are positive" do
|
129
|
+
expect( tc([1,2,3], [0, 5,0]) ).must_be_kind_of TorqueCurve
|
130
|
+
expect { tc([1,2,3], [0,-5,0]) }.must_raise
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
it "finds the peak torque and corresponding RPM" do
|
136
|
+
rpm, torque = *@default.peak
|
137
|
+
expect(rpm).must_be(:>, 1000)
|
138
|
+
expect(rpm).must_be(:<, 10_000)
|
139
|
+
expect(torque).must_be(:>, 0)
|
140
|
+
expect(torque).must_be(:<, 10_000)
|
141
|
+
end
|
142
|
+
|
143
|
+
it "interpolates to find the torque for a valid RPM" do
|
144
|
+
tc = tc(@rpms, @torques)
|
145
|
+
r1 = @rpms[1]
|
146
|
+
r2 = @rpms[2]
|
147
|
+
|
148
|
+
expect(tc.torque(r1)).must_equal @torques[1]
|
149
|
+
expect(tc.torque(r2)).must_equal @torques[2]
|
150
|
+
|
151
|
+
r3 = (r1 + r2) / 2
|
152
|
+
t3 = tc.torque(r3)
|
153
|
+
t3_expect = (@torques[1] + @torques[2]) / 2.0
|
154
|
+
|
155
|
+
expect(t3).must_be :>, @torques[1]
|
156
|
+
expect(t3).must_be :<, @torques[2]
|
157
|
+
expect(t3).must_be :>=, t3_expect.floor
|
158
|
+
expect(t3).must_be :<=, t3_expect.ceil
|
159
|
+
end
|
160
|
+
|
161
|
+
it "tracks min, idle, redline, and max rpms" do
|
162
|
+
expect(@default.min).must_be :>, 0
|
163
|
+
expect(@default.min).must_be :<, 1000
|
164
|
+
expect(@default.idle).must_be :>, @default.min
|
165
|
+
expect(@default.idle).must_be :<, 3000
|
166
|
+
expect(@default.redline).must_be :>, @default.idle
|
167
|
+
expect(@default.redline).must_be :<, 20_000
|
168
|
+
expect(@default.max).must_be :>, @default.redline
|
169
|
+
expect(@default.max).must_be :<, 20_000
|
170
|
+
end
|
171
|
+
end
|
172
|
+
|
173
|
+
describe Motor do
|
174
|
+
before do
|
175
|
+
@default = Motor.new(Environment.new)
|
176
|
+
end
|
177
|
+
|
178
|
+
it "has a throttle with state" do
|
179
|
+
expect(@default.throttle).must_equal 0.0
|
180
|
+
@default.throttle = 0.5
|
181
|
+
expect(@default.throttle).must_equal 0.5
|
182
|
+
expect(@default.throttle_pct).must_equal "50.0%"
|
183
|
+
|
184
|
+
expect { @default.throttle = 1.5 }.must_raise
|
185
|
+
end
|
186
|
+
|
187
|
+
it "has mass" do
|
188
|
+
expect(@default.mass).must_be :>, 0
|
189
|
+
expect(@default.fixed_mass).must_be :>, 0
|
190
|
+
expect(@default.rotating_mass).must_be :>, 0
|
191
|
+
end
|
192
|
+
|
193
|
+
it "has a torque curve" do
|
194
|
+
expect(@default.torque_curve).must_be_kind_of TorqueCurve
|
195
|
+
end
|
196
|
+
|
197
|
+
it "has idle RPM and redline RPM" do
|
198
|
+
expect(@default.idle).must_be :>, 500
|
199
|
+
expect(@default.idle).must_be :<, 1500
|
200
|
+
|
201
|
+
expect(@default.redline).must_be :>, @default.idle
|
202
|
+
expect(@default.redline).must_be :<, 10_000
|
203
|
+
end
|
204
|
+
|
205
|
+
it "has inertia and energy" do
|
206
|
+
expect(@default.inertia).must_be :>, 0
|
207
|
+
expect(@default.energy(99)).must_be :>, 0
|
208
|
+
end
|
209
|
+
|
210
|
+
it "determines crank alpha from torque" do
|
211
|
+
expect(@default.alpha(50)).must_be :>, 0
|
212
|
+
end
|
213
|
+
|
214
|
+
it "determines torque from crank alpha" do
|
215
|
+
a = @default.alpha(50, omega: 20)
|
216
|
+
t = @default.implied_torque(a)
|
217
|
+
# frictional losses
|
218
|
+
expect(t).must_be :>, 40
|
219
|
+
expect(t).must_be :<, 50
|
220
|
+
end
|
221
|
+
|
222
|
+
it "determines torque based on torque curve, RPM and throttle" do
|
223
|
+
@default.throttle = 1.0
|
224
|
+
expect(@default.torque(1000)).must_be :>, 0
|
225
|
+
expect(@default.torque(3500)).must_be :>, @default.torque(1000)
|
226
|
+
end
|
227
|
+
|
228
|
+
it "has an engine braking effect when the throttle is closed" do
|
229
|
+
@default.throttle = 0
|
230
|
+
expect(@default.torque(3500)).must_be :<, 0
|
231
|
+
end
|
232
|
+
|
233
|
+
it "has a starter motor to get running" do
|
234
|
+
expect(@default.starter_torque).must_be :>, 0
|
235
|
+
end
|
236
|
+
end
|
data/test/powertrain.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'minitest/autorun'
|
2
|
+
require 'driving_physics/powertrain'
|
3
|
+
|
4
|
+
include DrivingPhysics
|
5
|
+
|
6
|
+
describe Powertrain do
|
7
|
+
it "has a motor and a gearbox" do
|
8
|
+
end
|
9
|
+
|
10
|
+
it "has mass" do
|
11
|
+
end
|
12
|
+
|
13
|
+
it "coverts rpm to axle torque" do
|
14
|
+
end
|
15
|
+
|
16
|
+
it "converts rpm to axle omega" do
|
17
|
+
end
|
18
|
+
|
19
|
+
it "determines crank rpm from axle_omega" do
|
20
|
+
end
|
21
|
+
end
|
data/test/scalar_force.rb
CHANGED
@@ -12,8 +12,7 @@ describe ScalarForce do
|
|
12
12
|
# ROT_COF's value is from observing that rotational resistance
|
13
13
|
# matches air resistance at roughly 30 m/s in street cars
|
14
14
|
it "approximates a reasonable rotational resistance constant" do
|
15
|
-
|
16
|
-
must_be_within_epsilon(-1 * ROT_COF)
|
15
|
+
_(30 * ScalarForce.air_resistance(1)).must_be_within_epsilon(-1 * ROT_COF)
|
17
16
|
end
|
18
17
|
|
19
18
|
it "approximates a positive drag force" do
|
@@ -21,12 +20,10 @@ describe ScalarForce do
|
|
21
20
|
end
|
22
21
|
|
23
22
|
it "approximates a positive rotational resistance force" do
|
24
|
-
|
25
|
-
must_be_within_epsilon(-383.13)
|
23
|
+
_(ScalarForce.rotational_resistance 30).must_be_within_epsilon(-383.13)
|
26
24
|
end
|
27
25
|
|
28
26
|
it "approximates a positive rolling resistance force" do
|
29
|
-
|
30
|
-
expect(ScalarForce.rolling_resistance nf).must_be_within_epsilon(-98.0)
|
27
|
+
_(ScalarForce.rolling_resistance(1000 * G)).must_be_within_epsilon(-98.0)
|
31
28
|
end
|
32
29
|
end
|
data/test/tire.rb
CHANGED
@@ -1,128 +1,94 @@
|
|
1
1
|
require 'minitest/autorun'
|
2
2
|
require 'driving_physics/tire'
|
3
|
+
require 'driving_physics/vector_force'
|
3
4
|
|
4
|
-
|
5
|
+
include DrivingPhysics
|
5
6
|
|
6
|
-
describe
|
7
|
+
describe Tire do
|
7
8
|
describe "Tire.traction" do
|
8
9
|
it "calculates traction force from normal force and coeff of friction" do
|
9
10
|
scalar_nf = 9800 # N
|
10
11
|
cof = 1.1
|
11
|
-
scalar_t =
|
12
|
+
scalar_t = Tire.traction(scalar_nf, cof)
|
12
13
|
expect(scalar_t).must_equal 10780.0
|
13
14
|
|
14
|
-
skip
|
15
|
+
skip unless DrivingPhysics.has_vector?
|
15
16
|
vector_nf = Vector[9800, 0]
|
16
|
-
vector_t =
|
17
|
+
vector_t = Tire.traction(vector_nf, cof)
|
17
18
|
expect(vector_t).must_equal Vector[10780.0, 0.0]
|
18
19
|
end
|
19
20
|
end
|
20
21
|
|
21
22
|
describe "Tire.volume" do
|
22
23
|
it "calculates the volume (m^3) of disk given radius and width" do
|
23
|
-
cubic_m =
|
24
|
+
cubic_m = Tire.volume(1.0, 1.0)
|
24
25
|
expect(cubic_m).must_equal Math::PI
|
25
26
|
|
26
|
-
cubic_m =
|
27
|
+
cubic_m = Tire.volume(0.35, 0.2)
|
27
28
|
expect(cubic_m).must_be_within_epsilon 0.076969
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
32
|
describe "Tire.volume_l" do
|
32
33
|
it "calculates the volume (L) of a disk given radius and width" do
|
33
|
-
liters =
|
34
|
+
liters = Tire.volume_l(1.0, 1.0)
|
34
35
|
expect(liters).must_equal Math::PI * 1000
|
35
36
|
|
36
|
-
liters =
|
37
|
+
liters = Tire.volume_l(0.35, 0.2)
|
37
38
|
expect(liters).must_be_within_epsilon 76.96902
|
38
39
|
end
|
39
40
|
end
|
40
41
|
|
41
42
|
describe "Tire.density" do
|
42
43
|
it "calculates the density (kg/L) given mass and volume" do
|
43
|
-
expect(
|
44
|
-
expect(
|
44
|
+
expect(Tire.density(25.0, 25.0)).must_equal 1.0
|
45
|
+
expect(Tire.density(50.0, 25.0)).must_equal 2.0
|
45
46
|
end
|
46
47
|
end
|
47
48
|
|
48
49
|
describe "Tire.mass" do
|
49
50
|
it "calculates the mass (kg) of a disk given radius, width, and density" do
|
50
|
-
expect(
|
51
|
+
expect(Tire.mass(0.35, 0.2, Tire::DENSITY)).must_be_within_epsilon 25.015
|
51
52
|
end
|
52
53
|
end
|
53
54
|
|
54
55
|
describe "Tire.rotational_inertia" do
|
55
56
|
it "calculates rotational inertia for a disk given radius and mass" do
|
56
|
-
expect(
|
57
|
+
expect(Tire.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
|
57
58
|
end
|
58
59
|
end
|
59
60
|
|
60
61
|
describe "Tire.alpha" do
|
61
62
|
it "calculates angular acceleration from torque and inertia" do
|
62
63
|
scalar_torque = 1000
|
63
|
-
inertia =
|
64
|
-
expect(
|
64
|
+
inertia = Tire.rotational_inertia(0.35, 25.0)
|
65
|
+
expect(Tire.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
|
65
66
|
|
66
|
-
skip
|
67
|
+
skip unless DrivingPhysics.has_vector?
|
67
68
|
vector_torque = Vector[0, 0, 1000]
|
68
|
-
vector_alpha =
|
69
|
+
vector_alpha = Tire.alpha vector_torque, inertia
|
69
70
|
expect(vector_alpha).must_be_instance_of Vector
|
70
71
|
expect(vector_alpha.size).must_equal 3
|
71
72
|
expect(vector_alpha[2]).must_be_within_epsilon 653.06
|
72
73
|
end
|
73
74
|
end
|
74
75
|
|
75
|
-
describe "Tire.torque_vector" do
|
76
|
-
it "calculates a torque in the 3rd dimension given 2D force and radius" do
|
77
|
-
skip # Vector
|
78
|
-
force = Vector[1000, 0]
|
79
|
-
radius = Vector[0, 5]
|
80
|
-
torque = T.torque_vector(force, radius)
|
81
|
-
expect(torque).must_be_instance_of Vector
|
82
|
-
expect(torque.size).must_equal 3
|
83
|
-
expect(torque[2]).must_be_within_epsilon 5000.0
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
describe "Tire.force_vector" do
|
88
|
-
it "calculates a (3D) force given 3D torque and 2D radius" do
|
89
|
-
# let's invert the Tire.torque_vector case from above:
|
90
|
-
skip # Vector
|
91
|
-
torque = Vector[0, 0, 5000]
|
92
|
-
radius = Vector[0, 5]
|
93
|
-
force = T.force_vector(torque, radius)
|
94
|
-
expect(force).must_be_instance_of Vector
|
95
|
-
expect(force.size).must_equal 3
|
96
|
-
expect(force[0]).must_be_within_epsilon 1000.0
|
97
|
-
|
98
|
-
# now let's rotate the radius into the x-dimension
|
99
|
-
# right hand rule, positive torque means thumb into screen, clockwise
|
100
|
-
# negative-x radius means positive-y force
|
101
|
-
torque = Vector[0, 0, 500]
|
102
|
-
radius = Vector[-5, 0]
|
103
|
-
force = T.force_vector(torque, radius)
|
104
|
-
expect(force).must_be_instance_of Vector
|
105
|
-
expect(force.size).must_equal 3
|
106
|
-
expect(force[1]).must_be_within_epsilon 100.0
|
107
|
-
end
|
108
|
-
end
|
109
|
-
|
110
76
|
describe "instance methods" do
|
111
77
|
before do
|
112
78
|
@env = DrivingPhysics::Environment.new
|
113
|
-
@tire =
|
79
|
+
@tire = Tire.new(@env)
|
114
80
|
end
|
115
81
|
|
116
82
|
it "initializes" do
|
117
|
-
expect(@tire).must_be_instance_of
|
118
|
-
expect(@tire.density).must_equal
|
83
|
+
expect(@tire).must_be_instance_of Tire
|
84
|
+
expect(@tire.density).must_equal Tire::DENSITY # sanity check
|
119
85
|
expect(@tire.mass).must_be_within_epsilon 25.01
|
120
86
|
|
121
|
-
with_mass =
|
87
|
+
with_mass = Tire.new(@env) { |w|
|
122
88
|
w.mass = 99.01
|
123
89
|
}
|
124
90
|
expect(with_mass.mass).must_equal 99.01
|
125
|
-
expect(with_mass.density).wont_equal
|
91
|
+
expect(with_mass.density).wont_equal Tire::DENSITY
|
126
92
|
end
|
127
93
|
|
128
94
|
it "has a string representation" do
|
@@ -158,7 +124,7 @@ describe T do
|
|
158
124
|
expect(@tire.traction scalar_nf).must_equal 10780.0
|
159
125
|
expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
|
160
126
|
|
161
|
-
skip
|
127
|
+
skip unless DrivingPhysics.has_vector?
|
162
128
|
vector_nf = Vector[9800, 0]
|
163
129
|
expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
|
164
130
|
expect(@tire.traction vector_nf, static: false).
|
@@ -178,7 +144,7 @@ describe T do
|
|
178
144
|
expect(kin_tq).must_be_within_epsilon 2401.0
|
179
145
|
|
180
146
|
# not sure about how torque vectors work, but the "math" "works":
|
181
|
-
skip
|
147
|
+
skip unless DrivingPhysics.has_vector?
|
182
148
|
vector_nf = Vector[9800, 0]
|
183
149
|
expect(@tire.tractable_torque(vector_nf)[0]).
|
184
150
|
must_be_within_epsilon 3773.0
|