driving_physics 0.0.0.2 → 0.0.1.2

Sign up to get free protection for your applications and to get access to all the features.
data/demo/tire.rb CHANGED
@@ -1,169 +1,87 @@
1
1
  require 'driving_physics/tire'
2
+ require 'driving_physics/imperial'
3
+ require 'driving_physics/cli'
2
4
 
3
5
  include DrivingPhysics
4
6
 
5
- t = Tire.new
6
- t.condition.debug_temp = false
7
-
8
- # drive time
9
- mins = 45
10
-
11
- drive_map = {
12
- acc: {
13
- label: "ACCELERATING",
14
- g: 0.5,
15
- fuel: 0.0002, # kg consumed / tick
16
- small_slide: 1, # m/s of wheelspin
17
- big_slide: 10,
18
- seconds: 1,
19
- next_sector: :brake,
20
- },
21
- brake: {
22
- label: "BRAKING",
23
- g: 1.0,
24
- fuel: 0.00001,
25
- small_slide: 0.5,
26
- big_slide: 2,
27
- seconds: 1,
28
- next_sector: :corner,
29
- },
30
- corner: {
31
- label: "CORNERING",
32
- g: 0.8,
33
- fuel: 0.0001,
34
- small_slide: 0.5,
35
- big_slide: 5,
36
- seconds: 2,
37
- next_sector: :acc,
38
- },
39
- }
40
-
41
- slide_speed = 0
42
- sliding = false
43
- mass = 900.0
44
- ambient_temp = 25
45
- critical_temp = 100
46
-
47
- drive_time = 0 # how many ticks elapsed in the current sector
48
- sector_map = drive_map[:acc]
49
- puts "ACCELERATING"
50
- puts "---"
51
-
52
- cooldown = false
53
- pushing = false
54
-
55
- # 100 ticks / sec
56
- (mins * 60 * 100).times { |i|
57
- drive_time += 1
58
-
59
- dynamic_g = sector_map[:g]
60
-
61
- if t.condition.temp_c <= 80 and cooldown
62
- puts "ENDING COOLDOWN"
63
- cooldown = false
64
- elsif t.condition.temp_c >= 110 and Random.rand(100) >= 90
65
- puts "COOLING DOWN"
66
- cooldown = true
67
- end
68
- dynamic_g -= 0.2 if cooldown
69
-
70
- if pushing
71
- # stop pushing at very high temp
72
- # 1/1000 chance to stop pushing
73
- if cooldown
74
- puts "ENDING PUSH BECAUSE COOLDOWN"
75
- pushing = false
76
- else
77
- if t.condition.temp_c >= critical_temp and Random.rand(1000) >= 999
78
- puts "ENDING PUSH"
79
- pushing = false
80
- else
81
- dynamic_g += 0.2
82
- end
83
- end
84
- else
85
- if !cooldown and
86
- t.condition.temp_c <= critical_temp and
87
- Random.rand(1000) >= 999
88
-
89
- puts "PUSHING!"
90
- pushing = true
91
- dynamic_g += 0.2
92
- end
93
- end
94
-
95
- if sliding
96
- # 5% chance to end the slide
97
- if Random.rand(100) >= 95
98
- puts " -= CAUGHT THE SLIDE! =-"
99
- sliding = false
100
- slide_speed = 0
101
- end
102
- else
103
- # 1% chance to start a small slide
104
- # 0.1% chance to start a big slide
105
- if Random.rand(100) >= 99
106
- puts " -= SMALL SLIDE! =-"
107
- sliding = true
108
- slide_speed = sector_map[:small_slide]
109
- elsif Random.rand(1000) >= 999
110
- puts " -= BIG SLIDE! =-"
111
- sliding = true
112
- slide_speed = sector_map[:big_slide]
113
- end
114
- end
115
-
116
- # fuel consumption
117
- # 5L of fuel should last 5 minutes
118
- # ~3.5 kg of fuel consumption
119
- # 1.2e-4 kg / tick
120
- mass -= sector_map[:fuel]
121
-
122
- begin
123
- t.condition.tick!(ambient_temp: ambient_temp, g: dynamic_g,
124
- slide_speed: slide_speed,
125
- mass: mass, tire_mass: 12, critical_temp: critical_temp)
126
-
127
- if i % 10 == 0
128
- condition = if pushing
129
- "Pushing"
130
- elsif cooldown
131
- "Cooldown"
132
- else
133
- "Normal"
134
- end
135
-
136
- puts [sector_map[:label].ljust(12, ' '),
137
- '%.2f' % dynamic_g,
138
- '%.1f' % slide_speed,
139
- '%.3f' % t.condition.temp_c,
140
- ].join(' ')
141
- end
142
- if i % 600 == 0
143
- puts
144
- puts "Condition: #{condition}"
145
- puts "Mass: #{'%.2f' % mass} kg"
146
- if t.condition.tread_mm > 0
147
- puts "Tread remaining: #{'%.3f' % t.condition.tread_mm}"
148
- else
149
- puts "Cords remaining: #{'%.3f' % t.condition.cords_mm}"
150
- end
151
- puts "Heat cycles: #{t.condition.heat_cycles}"
152
- puts DrivingPhysics.elapsed_display(i * 10)
153
- puts "[Enter] to continue"
154
- gets
155
- end
156
- rescue Tire::Condition::Error => e
157
- puts "FATAL:"
158
- puts [e.class, e.message].join(': ')
159
- break
160
- end
161
-
162
- if drive_time > sector_map[:seconds] * 100
163
- sector_map = drive_map[sector_map[:next_sector]]
164
- drive_time = 0
7
+ env = Environment.new
8
+ tire = Tire.new(env)
9
+ puts env
10
+ puts tire
11
+ puts
12
+
13
+ duration = 100 # sec
14
+ axle_torque = 500 # N*m
15
+ supported_mass = 1500 # kg
16
+
17
+ puts "Given:"
18
+ puts "* #{axle_torque} Nm axle torque"
19
+ puts "* #{supported_mass} kg supported mass"
20
+ puts "* 4 tires"
21
+ puts "* #{duration} seconds"
22
+ puts
23
+
24
+ total_mass = supported_mass + 4 * tire.mass
25
+ corner_mass = Rational(total_mass) / 4
26
+ normal_force = corner_mass * env.g
27
+
28
+ puts "Therefore:"
29
+ puts format("* %.1f kg total mass", total_mass)
30
+ puts format("* %.1f kg per corner", corner_mass)
31
+ puts format("* %.1f N normal force", normal_force)
32
+ puts
33
+
34
+ traction = tire.traction(normal_force)
35
+ drive_force = tire.force(axle_torque)
36
+
37
+ puts "Tires:"
38
+ puts format("* %.1f N traction", traction)
39
+ puts format("* %.1f N drive force", drive_force)
40
+
41
+ CLI.pause
42
+
43
+ acc = 0.0 # meters/s/s
44
+ speed = 0.0 # meters/s
45
+ dist = 0.0 # meters
46
+
47
+ alpha = 0.0 # radians/s/s
48
+ omega = 0.0 # radians/s
49
+ theta = 0.0 # radians
50
+
51
+ start = Timer.now
52
+ paused = 0.0
53
+ num_ticks = duration * env.hz + 1
54
+
55
+ num_ticks.times { |i|
56
+ torque = tire.net_tractable_torque(axle_torque,
57
+ mass: total_mass,
58
+ omega: omega,
59
+ normal_force: normal_force)
60
+ force = tire.force(torque)
61
+
62
+ # translational kinematics
63
+ acc = DrivingPhysics.acc(force, total_mass)
64
+ speed += acc * env.tick
65
+ dist += speed * env.tick
66
+ mph = Imperial.mph(speed)
67
+
68
+ # rotational kinematics
69
+ alpha = acc / tire.radius
70
+ omega += alpha * env.tick
71
+ theta += omega * env.tick
72
+
73
+ if i < 10 or
74
+ (i < 20_000 and i%1000 == 0) or
75
+ (i % 10_000 == 0)
76
+
77
+ puts DrivingPhysics.elapsed_display(i)
78
+ puts format(" Tire: %.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 (%d mph)",
80
+ dist, speed, acc, mph)
81
+ puts format("Torque: %.1f Nm (%d N) Loss: %.1f%%",
82
+ torque, force, (1.0 - torque / axle_torque) * 100)
165
83
  puts
166
- puts sector_map[:label]
167
- puts '---'
168
84
  end
169
85
  }
86
+
87
+ puts Timer.summary(start, num_ticks, paused)
data/demo/vector_force.rb CHANGED
@@ -1,26 +1,56 @@
1
1
  require 'driving_physics/vector_force'
2
+ require 'driving_physics/environment'
3
+ require 'driving_physics/cli'
2
4
 
3
- DP = DrivingPhysics
5
+ include DrivingPhysics
6
+
7
+ env = Environment.new
8
+ puts env
4
9
 
5
- p = Vector[0, 0] # m
6
- v = Vector[0, 0] # m/s
7
10
  mass = 1000 # kg
8
- weight = mass * DP::G # N
11
+ weight = mass * env.g # N
12
+ drive_force = DrivingPhysics.random_unit_vector * 7000 # N
9
13
  duration = 100 # seconds
10
- drive_force = DP.random_unit_vector * 7000 # N
11
- tick = 1.0 / DP::HZ
12
14
 
13
- (duration * DP::HZ).times { |i|
14
- nf = drive_force + DP::VectorForce.all_resistance(v, dir: v, nf_mag: weight)
15
+ puts format("Force: %d N Dir: %s",
16
+ drive_force.magnitude,
17
+ DrivingPhysics.compass_dir(drive_force.normalize))
18
+ puts format("Mass: %d kg for %d seconds", mass, duration)
19
+ CLI.pause
20
+
21
+ acc = Vector[0, 0] # m/s/s
22
+ vel = Vector[0, 0] # m/s
23
+ pos = Vector[0, 0] # m
24
+
25
+ num_ticks = duration * env.hz + 1
26
+
27
+ flag = false
28
+ phase = :accelerate
29
+ paused = 0.0
30
+ start = Timer.now
15
31
 
16
- a = DP.a(nf, mass)
17
- v = DP.v(a, v, dt: tick)
18
- p = DP.p(v, p, dt: tick)
32
+ num_ticks.times { |i|
33
+ net_force = drive_force +
34
+ VectorForce.all_resistance(vel, dir: vel, nf_mag: weight)
19
35
 
20
- if i % DP::HZ == 0
21
- puts [i / DP::HZ,
22
- format("%.2f m/s", v.magnitude),
23
- format("%.2f m", p.magnitude),
24
- ].join("\t")
36
+ acc = DrivingPhysics.acc(net_force, mass)
37
+ vel += acc * env.tick
38
+ pos += vel * env.tick
39
+
40
+ if phase == :accelerate and vel.magnitude > 100
41
+ flag = true
42
+ phase = :coast
43
+ drive_force = Vector[0, 0]
44
+ end
45
+
46
+ if flag or (i % 1000 == 0)
47
+ puts format("%d %.3f m/s/s %.2f m/s %.1f m",
48
+ i.to_f / env.hz, acc.magnitude, vel.magnitude, pos.magnitude)
49
+ if flag
50
+ paused = CLI.pause
51
+ flag = false
52
+ end
25
53
  end
26
54
  }
55
+
56
+ puts Timer.summary(start, num_ticks, paused)