driving_physics 0.0.0.3 → 0.0.1.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.
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
- }