driving_physics 0.0.0.3 → 0.0.1.1

Sign up to get free protection for your applications and to get access to all the features.
data/test/disk.rb ADDED
@@ -0,0 +1,129 @@
1
+ require 'minitest/autorun'
2
+ require 'driving_physics/disk'
3
+
4
+ D = DrivingPhysics::Disk
5
+
6
+ describe D do
7
+ describe "Disk.volume" do
8
+ it "calculates the volume (m^3) of disk given radius and width" do
9
+ cubic_m = D.volume(1.0, 1.0)
10
+ expect(cubic_m).must_equal Math::PI
11
+
12
+ cubic_m = D.volume(0.35, 0.2)
13
+ expect(cubic_m).must_be_within_epsilon 0.076969
14
+ end
15
+ end
16
+
17
+ describe "Disk.volume_l" do
18
+ it "calculates the volume (L) of a disk given radius and width" do
19
+ liters = D.volume_l(1.0, 1.0)
20
+ expect(liters).must_equal Math::PI * 1000
21
+
22
+ liters = D.volume_l(0.35, 0.2)
23
+ expect(liters).must_be_within_epsilon 76.96902
24
+ end
25
+ end
26
+
27
+ describe "Disk.density" do
28
+ it "calculates the density (kg/L) given mass and volume" do
29
+ expect(D.density(25.0, 25.0)).must_equal 1.0
30
+ expect(D.density(50.0, 25.0)).must_equal 2.0
31
+ end
32
+ end
33
+
34
+ describe "Disk.mass" do
35
+ it "calculates the mass (kg) of a disk given radius, width, and density" do
36
+ skip
37
+ expect(D.mass(0.35, 0.2, D::DENSITY)).must_be_within_epsilon 25.015
38
+ end
39
+ end
40
+
41
+ describe "Disk.rotational_inertia" do
42
+ it "calculates rotational inertia for a disk given radius and mass" do
43
+ expect(D.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
44
+ end
45
+ end
46
+
47
+ describe "Disk.alpha" do
48
+ it "calculates angular acceleration from torque and inertia" do
49
+ scalar_torque = 1000
50
+ inertia = D.rotational_inertia(0.35, 25.0)
51
+ expect(D.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
52
+
53
+ vector_torque = Vector[0, 0, 1000]
54
+ vector_alpha = D.alpha vector_torque, inertia
55
+ expect(vector_alpha).must_be_instance_of Vector
56
+ expect(vector_alpha.size).must_equal 3
57
+ expect(vector_alpha[2]).must_be_within_epsilon 653.06
58
+ end
59
+ end
60
+
61
+ describe "Disk.torque_vector" do
62
+ it "calculates a torque in the 3rd dimension given 2D force and radius" do
63
+ force = Vector[1000, 0]
64
+ radius = Vector[0, 5]
65
+ torque = D.torque_vector(force, radius)
66
+ expect(torque).must_be_instance_of Vector
67
+ expect(torque.size).must_equal 3
68
+ expect(torque[2]).must_be_within_epsilon 5000.0
69
+ end
70
+ end
71
+
72
+ describe "Disk.force_vector" do
73
+ it "calculates a (3D) force given 3D torque and 2D radius" do
74
+ # let's invert the Disk.torque_vector case from above:
75
+ torque = Vector[0, 0, 5000]
76
+ radius = Vector[0, 5]
77
+ force = D.force_vector(torque, radius)
78
+ expect(force).must_be_instance_of Vector
79
+ expect(force.size).must_equal 3
80
+ expect(force[0]).must_be_within_epsilon 1000.0
81
+
82
+ # now let's rotate the radius into the x-dimension
83
+ # right hand rule, positive torque means thumb into screen, clockwise
84
+ # negative-x radius means positive-y force
85
+ torque = Vector[0, 0, 500]
86
+ radius = Vector[-5, 0]
87
+ force = D.force_vector(torque, radius)
88
+ expect(force).must_be_instance_of Vector
89
+ expect(force.size).must_equal 3
90
+ expect(force[1]).must_be_within_epsilon 100.0
91
+ end
92
+ end
93
+
94
+ describe "instance methods" do
95
+ before do
96
+ @env = DrivingPhysics::Environment.new
97
+ @disk = D.new(@env)
98
+ end
99
+
100
+ it "initializes" do
101
+ skip
102
+ expect(@disk).must_be_instance_of D
103
+ expect(@disk.density).must_equal D::DENSITY # sanity check
104
+ expect(@disk.mass).must_be_within_epsilon 25.01
105
+
106
+ with_mass = D.new(@env) { |w|
107
+ w.mass = 99.01
108
+ }
109
+ expect(with_mass.mass).must_equal 99.01
110
+ expect(with_mass.density).wont_equal D::DENSITY
111
+ end
112
+
113
+ it "has a string representation" do
114
+ str = @disk.to_s
115
+ expect(str).must_be_instance_of String
116
+ expect(str.length).must_be(:>, 5)
117
+ end
118
+
119
+ it "has volume" do
120
+ expect(@disk.volume).must_be_within_epsilon 0.07697
121
+ expect(@disk.volume_l).must_be_within_epsilon 76.96902
122
+ end
123
+
124
+ it "has inertia" do
125
+ skip
126
+ expect(@disk.rotational_inertia).must_be_within_epsilon 1.5321
127
+ end
128
+ end
129
+ end
@@ -1,92 +1,92 @@
1
1
  require 'minitest/autorun'
2
- require 'driving_physics/wheel'
2
+ require 'driving_physics/tire'
3
3
 
4
- W = DrivingPhysics::Wheel
4
+ T = DrivingPhysics::Tire
5
5
 
6
- describe W do
7
- describe "Wheel.traction" do
6
+ describe T do
7
+ describe "Tire.traction" do
8
8
  it "calculates traction force from normal force and coeff of friction" do
9
9
  scalar_nf = 9800 # N
10
10
  cof = 1.1
11
- scalar_t = W.traction(scalar_nf, cof)
11
+ scalar_t = T.traction(scalar_nf, cof)
12
12
  expect(scalar_t).must_equal 10780.0
13
13
 
14
14
  vector_nf = Vector[9800, 0]
15
- vector_t = W.traction(vector_nf, cof)
15
+ vector_t = T.traction(vector_nf, cof)
16
16
  expect(vector_t).must_equal Vector[10780.0, 0.0]
17
17
  end
18
18
  end
19
19
 
20
- describe "Wheel.volume" do
20
+ describe "Tire.volume" do
21
21
  it "calculates the volume (m^3) of disk given radius and width" do
22
- cubic_m = W.volume(1.0, 1.0)
22
+ cubic_m = T.volume(1.0, 1.0)
23
23
  expect(cubic_m).must_equal Math::PI
24
24
 
25
- cubic_m = W.volume(0.35, 0.2)
25
+ cubic_m = T.volume(0.35, 0.2)
26
26
  expect(cubic_m).must_be_within_epsilon 0.076969
27
27
  end
28
28
  end
29
29
 
30
- describe "Wheel.volume_l" do
30
+ describe "Tire.volume_l" do
31
31
  it "calculates the volume (L) of a disk given radius and width" do
32
- liters = W.volume_l(1.0, 1.0)
32
+ liters = T.volume_l(1.0, 1.0)
33
33
  expect(liters).must_equal Math::PI * 1000
34
34
 
35
- liters = W.volume_l(0.35, 0.2)
35
+ liters = T.volume_l(0.35, 0.2)
36
36
  expect(liters).must_be_within_epsilon 76.96902
37
37
  end
38
38
  end
39
39
 
40
- describe "Wheel.density" do
40
+ describe "Tire.density" do
41
41
  it "calculates the density (kg/L) given mass and volume" do
42
- expect(W.density(25.0, 25.0)).must_equal 1.0
43
- expect(W.density(50.0, 25.0)).must_equal 2.0
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
44
  end
45
45
  end
46
46
 
47
- describe "Wheel.mass" do
47
+ describe "Tire.mass" do
48
48
  it "calculates the mass (kg) of a disk given radius, width, and density" do
49
- expect(W.mass(0.35, 0.2, W::DENSITY)).must_be_within_epsilon 25.015
49
+ expect(T.mass(0.35, 0.2, T::DENSITY)).must_be_within_epsilon 25.015
50
50
  end
51
51
  end
52
52
 
53
- describe "Wheel.rotational_inertia" do
53
+ describe "Tire.rotational_inertia" do
54
54
  it "calculates rotational inertia for a disk given radius and mass" do
55
- expect(W.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
55
+ expect(T.rotational_inertia(0.35, 25.0)).must_be_within_epsilon 1.53125
56
56
  end
57
57
  end
58
58
 
59
- describe "Wheel.alpha" do
59
+ describe "Tire.alpha" do
60
60
  it "calculates angular acceleration from torque and inertia" do
61
61
  scalar_torque = 1000
62
- inertia = W.rotational_inertia(0.35, 25.0)
63
- expect(W.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
62
+ inertia = T.rotational_inertia(0.35, 25.0)
63
+ expect(T.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
64
64
 
65
65
  vector_torque = Vector[0, 0, 1000]
66
- vector_alpha = W.alpha vector_torque, inertia
66
+ vector_alpha = T.alpha vector_torque, inertia
67
67
  expect(vector_alpha).must_be_instance_of Vector
68
68
  expect(vector_alpha.size).must_equal 3
69
69
  expect(vector_alpha[2]).must_be_within_epsilon 653.06
70
70
  end
71
71
  end
72
72
 
73
- describe "Wheel.torque_vector" do
73
+ describe "Tire.torque_vector" do
74
74
  it "calculates a torque in the 3rd dimension given 2D force and radius" do
75
75
  force = Vector[1000, 0]
76
76
  radius = Vector[0, 5]
77
- torque = W.torque_vector(force, radius)
77
+ torque = T.torque_vector(force, radius)
78
78
  expect(torque).must_be_instance_of Vector
79
79
  expect(torque.size).must_equal 3
80
80
  expect(torque[2]).must_be_within_epsilon 5000.0
81
81
  end
82
82
  end
83
83
 
84
- describe "Wheel.force_vector" do
84
+ describe "Tire.force_vector" do
85
85
  it "calculates a (3D) force given 3D torque and 2D radius" do
86
- # let's invert the Wheel.torque_vector case from above:
86
+ # let's invert the Tire.torque_vector case from above:
87
87
  torque = Vector[0, 0, 5000]
88
88
  radius = Vector[0, 5]
89
- force = W.force_vector(torque, radius)
89
+ force = T.force_vector(torque, radius)
90
90
  expect(force).must_be_instance_of Vector
91
91
  expect(force.size).must_equal 3
92
92
  expect(force[0]).must_be_within_epsilon 1000.0
@@ -96,7 +96,7 @@ describe W do
96
96
  # negative-x radius means positive-y force
97
97
  torque = Vector[0, 0, 500]
98
98
  radius = Vector[-5, 0]
99
- force = W.force_vector(torque, radius)
99
+ force = T.force_vector(torque, radius)
100
100
  expect(force).must_be_instance_of Vector
101
101
  expect(force.size).must_equal 3
102
102
  expect(force[1]).must_be_within_epsilon 100.0
@@ -106,72 +106,76 @@ describe W do
106
106
  describe "instance methods" do
107
107
  before do
108
108
  @env = DrivingPhysics::Environment.new
109
- @w = W.new(@env)
109
+ @tire = T.new(@env)
110
110
  end
111
111
 
112
112
  it "initializes" do
113
- expect(@w).must_be_instance_of W
114
- expect(@w.density).must_equal W::DENSITY # sanity check
115
- expect(@w.mass).must_be_within_epsilon 25.01
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
116
116
 
117
- with_mass = W.new(@env, mass: 99.01)
117
+ with_mass = T.new(@env) { |w|
118
+ w.mass = 99.01
119
+ }
118
120
  expect(with_mass.mass).must_equal 99.01
119
- expect(with_mass.density).wont_equal W::DENSITY
121
+ expect(with_mass.density).wont_equal T::DENSITY
120
122
  end
121
123
 
122
124
  it "has a string representation" do
123
- str = @w.to_s
125
+ str = @tire.to_s
124
126
  expect(str).must_be_instance_of String
125
127
  expect(str.length).must_be(:>, 5)
126
128
  end
127
129
 
128
130
  it "loses radius as it wears" do
129
- expect(@w.radius).must_equal 350.0
130
- @w.wear!(50)
131
- expect(@w.radius).must_equal 300.0
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
132
135
  end
133
136
 
134
137
  it "calculates mass from current radius" do
135
- expect(@w.mass).must_be_within_epsilon 25.01
136
- @w.wear!(50)
137
- expect(@w.mass).must_be_within_epsilon 18.378
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
138
141
  end
139
142
 
140
143
  it "has volume" do
141
- expect(@w.volume).must_be_within_epsilon 0.07697
142
- expect(@w.volume_l).must_be_within_epsilon 76.96902
144
+ expect(@tire.volume).must_be_within_epsilon 0.07697
145
+ expect(@tire.volume_l).must_be_within_epsilon 76.96902
143
146
  end
144
147
 
145
148
  it "has inertia" do
146
- expect(@w.rotational_inertia).must_be_within_epsilon 1.5321
149
+ expect(@tire.rotational_inertia).must_be_within_epsilon 1.5321
147
150
  end
148
151
 
149
152
  it "has traction force based on normal force" do
150
153
  scalar_nf = 9800
151
- expect(@w.traction scalar_nf).must_equal 10780.0
152
- expect(@w.traction scalar_nf, static: false).must_equal 6860.0
154
+ expect(@tire.traction scalar_nf).must_equal 10780.0
155
+ expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
153
156
 
154
157
  vector_nf = Vector[9800, 0]
155
- expect(@w.traction vector_nf).must_equal Vector[10780.0, 0.0]
156
- expect(@w.traction vector_nf, static: false).
158
+ expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
159
+ expect(@tire.traction vector_nf, static: false).
157
160
  must_equal Vector[6860.0, 0.0]
158
161
  end
159
162
 
160
163
  it "determines (e.g. thrust) force based on axle torque" do
161
- expect(@w.force 1000).must_be_within_epsilon 2857.143
162
- @w.wear! 50
163
- expect(@w.force 1000).must_be_within_epsilon 3333.333
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
164
167
  end
165
168
 
166
169
  it "determines tractable torque" do
167
170
  scalar_nf = 9800
168
- expect(@w.tractable_torque scalar_nf).must_be_within_epsilon 3773.0
169
- kin_tq = @w.tractable_torque scalar_nf, static: false
171
+ expect(@tire.tractable_torque scalar_nf).must_be_within_epsilon 3773.0
172
+ kin_tq = @tire.tractable_torque scalar_nf, static: false
170
173
  expect(kin_tq).must_be_within_epsilon 2401.0
171
174
 
172
175
  # not sure about how torque vectors work, but the "math" "works":
173
176
  vector_nf = Vector[9800, 0]
174
- expect(@w.tractable_torque(vector_nf)[0]).must_be_within_epsilon 3773.0
177
+ expect(@tire.tractable_torque(vector_nf)[0]).
178
+ must_be_within_epsilon 3773.0
175
179
  end
176
180
  end
177
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.3
4
+ version: 0.0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -19,20 +19,33 @@ files:
19
19
  - README.md
20
20
  - Rakefile
21
21
  - VERSION
22
+ - demo/car.rb
23
+ - demo/disk.rb
24
+ - demo/gearbox.rb
25
+ - demo/motor.rb
26
+ - demo/powertrain.rb
22
27
  - demo/scalar_force.rb
28
+ - demo/tire.rb
23
29
  - demo/vector_force.rb
24
- - demo/wheel.rb
25
30
  - driving_physics.gemspec
26
31
  - lib/driving_physics.rb
32
+ - lib/driving_physics/car.rb
33
+ - lib/driving_physics/cli.rb
34
+ - lib/driving_physics/disk.rb
27
35
  - lib/driving_physics/environment.rb
36
+ - lib/driving_physics/gearbox.rb
28
37
  - lib/driving_physics/imperial.rb
38
+ - lib/driving_physics/motor.rb
39
+ - lib/driving_physics/power.rb
40
+ - lib/driving_physics/powertrain.rb
29
41
  - lib/driving_physics/scalar_force.rb
42
+ - lib/driving_physics/tire.rb
30
43
  - lib/driving_physics/vector_force.rb
31
- - lib/driving_physics/wheel.rb
44
+ - test/disk.rb
32
45
  - test/driving_physics.rb
33
46
  - test/scalar_force.rb
47
+ - test/tire.rb
34
48
  - test/vector_force.rb
35
- - test/wheel.rb
36
49
  homepage: https://github.com/rickhull/driving_physics
37
50
  licenses:
38
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
- }