driving_physics 0.0.1.2 → 0.0.2.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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: ef486c39982798a7331f49f601e648d520a65977161180631cc4c5d3340a9011
4
- data.tar.gz: 9e30b74ae34cb77a711d5d9631983b0c070872900123a1dde2d3dd03bdab854b
3
+ metadata.gz: 89628c4f71193088741435eb426419edf7c9ac78cad2169c4718f2a365db0881
4
+ data.tar.gz: a229fa52ed29f6dcb8eab0513e845732e0735dd272e516e4f26603aeabe7f312
5
5
  SHA512:
6
- metadata.gz: 46eaf512dad64ca39ec20451d7acce1b1819ecd64125ee858f92c26e5edd7b0ec0e7ef01af8f73daa0d37868e0ffd76bd3b29478093219cd906d8d9e38026bcd
7
- data.tar.gz: ae2e01ae8e7986c9d2e659ff5a0934ed5359e20cf2757d7724118bb59e0fb0199180249815f2f28f55f48a2336511a0b43f0d6fab042ed5d20a5ab6a897baef1
6
+ metadata.gz: 42a6061d1ef6e2c989e5fb88141e8a84daaafc3c9a2f31e057d2fb7a384fbaa1c195d05deab18b2703fd5e79b539557543ec8eaf8632cd2a5f776359c7c3e340
7
+ data.tar.gz: 2860d2b6c17c5d4e9d73868de36bdcf48341d74712666c109d422c97975899b0fde2248b58c060d02e02561ba3dff471d7b66c694a44e5d143108a20f828e477
data/README.md CHANGED
@@ -10,8 +10,27 @@ using `Vector` class from `matrix.rb` in stdlib.
10
10
 
11
11
  This is very much a **Work In Progress**. Implemented so far:
12
12
 
13
+ ### mruby Support
14
+
15
+ This is big. This library is primarily a Ruby library, intended for use with
16
+ CRuby (MRI), JRuby, etc. **mruby** is the redheaded stepchild, with severe
17
+ implications for a codebase that wants to support both sides. Supporting
18
+ mruby here meant:
19
+
20
+ 1. Editing the codebase (minimally) to remove *mruby-deadends*.
21
+ 2. Creating `rake` tasks to process normal .rb files into *mruby-compatible*
22
+ .rb files
23
+ 3. Assembling all needed library .rb files into a master .rb file
24
+ 4. Compiling the library to bytecode
25
+ 5. Executing a toplevel/main script against the library
26
+ 6. Optionally executing toplevel/main bytecode against the library
27
+
28
+ See the main Rakefile near the top for mruby stuff.
29
+
30
+ ### Library Features
31
+
13
32
  * Spinning: rotational inertia and friction / hysteresis
14
- * Tires: traction force via friction and the normal force
33
+ * Tires: traction force via friction (static and kinetic) and the normal force
15
34
  * Vectors: 2D Vector forces and 3D Vector torques
16
35
  * Motor: torque curves, rotating mass with friction
17
36
  * Gearbox: gear ratios, final drive, rotating mass with friction
data/Rakefile CHANGED
@@ -16,6 +16,90 @@ end
16
16
 
17
17
  task default: :test
18
18
 
19
+ #
20
+ # mruby
21
+ #
22
+
23
+ MRBLIB_DIR = File.join %w[mruby mrblib]
24
+ MRBLIB_FILE = File.join(MRBLIB_DIR, 'driving_physics.rb')
25
+ MRBLIB_MRB = File.join(MRBLIB_DIR, 'driving_physics.mrb')
26
+ MRUBY_DEMO_DIR = File.join %w[demo mruby]
27
+
28
+ def write_mruby(input_file, output_file = MRBLIB_FILE, append: false)
29
+ file_obj = File.open(output_file, append ? 'a' : 'w')
30
+ line_count = 0
31
+
32
+ File.readlines(input_file).each { |line|
33
+ next if line.match /\A *(?:require|autoload)/
34
+ file_obj.write(line)
35
+ line_count += 1
36
+ }
37
+ file_obj.close
38
+ line_count
39
+ end
40
+
41
+ file MRBLIB_FILE do
42
+ line_count = write_mruby(File.join(%w[lib driving_physics.rb]), MRBLIB_FILE)
43
+ %w[mruby environment imperial power
44
+ disk tire motor gearbox powertrain car].each { |name|
45
+ file = File.join('lib', 'driving_physics', "#{name}.rb")
46
+ line_count += write_mruby(file, MRBLIB_FILE, append: true)
47
+ puts "wrote #{file} to #{MRBLIB_FILE}"
48
+ }
49
+ puts "wrote #{line_count} lines to #{MRBLIB_FILE}"
50
+ end
51
+
52
+ file MRBLIB_MRB => MRBLIB_FILE do
53
+ rb_file = File.join(%w[mruby mrblib driving_physics.rb])
54
+ mrb_file = File.join(%w[mruby mrblib driving_physics.mrb])
55
+ sh 'mrbc', rb_file
56
+ puts format("%s: %d bytes (created %s)",
57
+ mrb_file, File.size(mrb_file), File.birthtime(mrb_file))
58
+ end
59
+
60
+ file MRUBY_DEMO_DIR do
61
+ mkdir_p MRUBY_DEMO_DIR
62
+ end
63
+
64
+ desc "Map/Filter/Reduce lib/**/*.rb to mruby/mrblib/driving_physics.rb"
65
+ task mrblib: MRBLIB_FILE
66
+
67
+ desc "Compile mruby/mrblib/driving_physics.rb to .mrb bytecode"
68
+ task mrbc: MRBLIB_MRB
69
+
70
+ %w[disk tire motor gearbox powertrain car].each { |name|
71
+ demo_file = File.join(MRUBY_DEMO_DIR, "#{name}.rb")
72
+ demo_mrb = File.join(MRUBY_DEMO_DIR, "#{name}.mrb")
73
+
74
+ file demo_file => MRUBY_DEMO_DIR do
75
+ write_mruby(File.join('demo', "#{name}.rb"), demo_file)
76
+ end
77
+
78
+ file demo_mrb => demo_file do
79
+ sh 'mrbc', demo_file
80
+ end
81
+
82
+ desc "run demo/#{name}.rb via mruby"
83
+ task "demo_#{name}" => [demo_file, MRBLIB_MRB] do
84
+ sh 'mruby', '-r', MRBLIB_MRB, demo_file
85
+ end
86
+
87
+ desc "run demo/#{name}.rb via mruby bytecode"
88
+ task "mrb_#{name}" => [demo_mrb, MRBLIB_MRB] do
89
+ sh 'mruby', '-r', MRBLIB_MRB, '-b', demo_mrb
90
+ end
91
+ }
92
+
93
+ desc "Remove generated .rb and .mrb files"
94
+ task :clean do
95
+ [MRBLIB_DIR, MRUBY_DEMO_DIR].each { |dir|
96
+ Dir[File.join(dir, '*rb')].each { |file|
97
+ rm file unless File.directory?(file)
98
+ }
99
+ }
100
+ end
101
+
102
+
19
103
  #
20
104
  # METRICS
21
105
  #
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1.2
1
+ 0.0.2.1
data/demo/car.rb CHANGED
@@ -41,6 +41,9 @@ clutch = :ok
41
41
  phase = :ignition
42
42
  flag = false
43
43
  rpm = 0
44
+
45
+
46
+
44
47
  puts <<EOF
45
48
 
46
49
  #
@@ -59,7 +62,7 @@ num_ticks.times { |i|
59
62
  rpm = DrivingPhysics.rpm(crank_omega)
60
63
 
61
64
  if i % 100 == 0 or rpm > motor.idle_rpm
62
- puts Timer.display(i)
65
+ puts Timer.display(ms: i)
63
66
  puts format("%d rad %d rad/s %d rad/s/s",
64
67
  crank_theta, crank_omega, crank_alpha)
65
68
  puts format("%d RPM %d Nm starter torque", rpm, motor.starter_torque)
@@ -111,7 +114,7 @@ EOF
111
114
  crank_torque = car.powertrain.motor.torque(rpm)
112
115
 
113
116
  if flag or (i < 5000 and i % 100 == 0) or (i % 1000 == 0)
114
- puts Timer.display(i)
117
+ puts Timer.display(ms: i)
115
118
  puts format(" Tire: %.1f r/s/s %.2f r/s %.3f r",
116
119
  tire_alpha, tire_omega, tire_theta)
117
120
  puts format(" Car: %.1f m/s/s %.2f m/s %.3f m (%.1f MPH)",
data/demo/gearbox.rb CHANGED
@@ -21,10 +21,11 @@ alpha = 0.0
21
21
  omega = 0.0
22
22
  theta = 0.0
23
23
 
24
+ num_ticks = duration * env.hz + 1
24
25
  start = Timer.now
25
- paused = 0
26
+ paused = 0.0
26
27
 
27
- (duration * env.hz + 1).times { |i|
28
+ num_ticks.times { |i|
28
29
  # just for info, not used in the simulation
29
30
  friction = gearbox.spinner.rotating_friction(omega)
30
31
 
@@ -41,7 +42,7 @@ paused = 0
41
42
  (i < 100 and i % 10 == 0) or
42
43
  (i < 1000 and i % 100 == 0) or
43
44
  i % 1000 == 0
44
- puts Timer.display(i)
45
+ puts Timer.display(ms: i)
45
46
  puts format("RPM %d Torque: %.3f Nm (%d Nm) Friction: %.3f Nm",
46
47
  DrivingPhysics.rpm(omega), net_torque, torque, friction)
47
48
  puts format("%.1f rad %.1f rad/s %.1f rad/s/s", theta, omega, alpha)
data/demo/motor.rb CHANGED
@@ -82,12 +82,11 @@ num_ticks.times { |i|
82
82
  omega += alpha * env.tick
83
83
  theta += omega * env.tick
84
84
 
85
- net_torque = motor.implied_torque(alpha)
85
+ # prevent silly oscillations due to ticks or tiny floating point errors
86
+ omega = 0 if omega < 0.0001
86
87
 
87
- # prevent silly oscillations due to tiny floating point errors
88
- omega = 0 if omega < 0.00001
88
+ net_torque = motor.implied_torque(alpha)
89
89
  rpm = DrivingPhysics.rpm(omega)
90
-
91
90
  power = DrivingPhysics.power(net_torque, omega)
92
91
 
93
92
  if rpm > motor.idle_rpm and status == :ignition
@@ -122,7 +121,7 @@ num_ticks.times { |i|
122
121
  (i < 10_000 and i % 500 == 0) or
123
122
  (i % 5000 == 0) or
124
123
  (status == :idling and i % 100 == 0)
125
- puts Timer.display(i)
124
+ puts Timer.display(ms: i)
126
125
  puts format("Throttle: %.1f%%", motor.throttle * 100)
127
126
  puts format("%d RPM %.1f Nm (%d Nm) %.1f kW Friction: %.1f Nm",
128
127
  DrivingPhysics.rpm(omega),
@@ -130,7 +129,7 @@ num_ticks.times { |i|
130
129
  torque,
131
130
  power / 1000,
132
131
  motor.spinner.rotating_friction(omega))
133
- puts format("%d rad %.1f rad/s %.1f rad/s/s", theta, omega, alpha)
132
+ puts format("%.3f rad/s/s %.2f rad/s %.1f rad", alpha, omega, theta)
134
133
  puts
135
134
 
136
135
  paused += CLI.pause if flag
@@ -0,0 +1,81 @@
1
+
2
+ include DrivingPhysics
3
+
4
+ env = Environment.new
5
+ disk = Disk.new(env)
6
+
7
+ puts env
8
+ puts disk
9
+ puts
10
+
11
+ axle_torque = 50
12
+ alpha = disk.alpha(axle_torque)
13
+ drive_force = disk.force(axle_torque)
14
+
15
+ puts [format("Axle torque: %.1f Nm", axle_torque),
16
+ format(" Alpha: %.1f rad/s/s", alpha),
17
+ format("Drive force: %.1f N", drive_force),
18
+ ].join("\n")
19
+ puts
20
+
21
+ puts "* Spin up the disk with #{axle_torque} Nm of torque"
22
+ puts "* Cut the power at some point"
23
+ puts "* Observe"
24
+ CLI.pause
25
+
26
+ duration = 750 # sec
27
+
28
+ dist = 0.0 # meters
29
+ speed = 0.0 # meters/s
30
+
31
+ theta = 0.0 # radians
32
+ omega = 0.0 # radians/s
33
+
34
+ paused = 0.0 # seconds
35
+ num_ticks = duration * env.hz + 1
36
+ flag = false # to display current stats
37
+ start = Timer.now
38
+
39
+ num_ticks.times { |i|
40
+ # shut off the powah!
41
+ if i == 18_950
42
+ flag = true
43
+ puts '#'
44
+ puts '# CUT POWER'
45
+ puts '#'
46
+ puts
47
+ axle_torque = 0
48
+ end
49
+
50
+ rotating_friction = disk.rotating_friction(omega)
51
+ net_torque = axle_torque + rotating_friction
52
+ net_force = disk.force(net_torque)
53
+
54
+ # rotational kinematics
55
+ alpha = disk.alpha(net_torque)
56
+ omega += alpha * env.tick
57
+ omega = 0.0 if omega.abs < 0.0001
58
+ theta += omega * env.tick
59
+
60
+ if flag or i < 10 or
61
+ (i < 20_000 and i%1000 == 0) or
62
+ (i % 10_000 == 0) or
63
+ i == duration * env.hz - 1
64
+
65
+ puts Timer.display(ms: i)
66
+ puts format(" Torque: %.1f Nm (%d Nm) Friction: %.1f Nm",
67
+ net_torque, axle_torque, rotating_friction)
68
+ puts format("Radians: %.1f r %.2f r/s %.3f r/s^2", theta, omega, alpha)
69
+ puts format(" Revs: %d revs %d revs/s %d rpm",
70
+ DrivingPhysics.revs(theta),
71
+ DrivingPhysics.revs(omega),
72
+ DrivingPhysics.rpm(omega))
73
+ puts
74
+ if flag
75
+ paused += CLI.pause
76
+ flag = false
77
+ end
78
+ end
79
+ }
80
+
81
+ puts Timer.summary(start, num_ticks, paused)
@@ -0,0 +1,137 @@
1
+
2
+ # next fun idea: PID controller
3
+
4
+ include DrivingPhysics
5
+
6
+ env = Environment.new
7
+ motor = Motor.new(env)
8
+ puts env
9
+ puts motor
10
+ puts
11
+
12
+ puts "Rev it up!"
13
+ motor.throttle = 1.0
14
+ [:torque, :power].each { |run|
15
+ puts
16
+ puts run.to_s.upcase + ':'
17
+
18
+ 800.upto(7000) { |rpm|
19
+ next unless rpm % 200 == 0
20
+ omega = DrivingPhysics.omega(rpm)
21
+ torque = motor.torque(rpm)
22
+ case run
23
+ when :torque
24
+ count = (torque.to_f / 10).round
25
+ char = 'T'
26
+ val = torque.round.to_s.rjust(5, ' ')
27
+ fmt = "%s Nm"
28
+ when :power
29
+ power = DrivingPhysics.power(torque, omega)
30
+ kw = power.to_f / 1000
31
+ count = (kw / 5).round
32
+ char = 'P'
33
+ val = kw.round(1).to_s.rjust(5, ' ')
34
+ fmt = "%s kW"
35
+ else
36
+ raise "unknown"
37
+ end
38
+ puts format("%s RPM: #{fmt}\t%s",
39
+ rpm.to_s.rjust(4, ' '),
40
+ val,
41
+ char * count)
42
+ }
43
+ }
44
+
45
+ puts
46
+ puts "Now, the simulation begins..."
47
+ puts "---"
48
+ puts "* Spin the motor up to #{motor.idle_rpm} RPM with the starter motor."
49
+ puts "* Rev it up with the throttle."
50
+ puts "* Let it die."
51
+ CLI.pause
52
+
53
+ alpha = 0.0
54
+ omega = 0.0
55
+ theta = 0.0
56
+
57
+ duration = 60
58
+
59
+ status = :ignition
60
+ rpm = 0
61
+
62
+ paused = 0.0
63
+ num_ticks = duration * env.hz + 1
64
+ start = Timer.now
65
+
66
+ num_ticks.times { |i|
67
+ # this is an input torque; alpha is determined after inertia and friction
68
+ torque = case status
69
+ when :ignition
70
+ motor.starter_torque
71
+ when :running, :off_throttle, :idling
72
+ motor.torque(rpm)
73
+ else
74
+ 0
75
+ end
76
+
77
+ # Motor#alpha incorporates inertia and friction
78
+ alpha = motor.alpha(torque, omega: omega)
79
+ omega += alpha * env.tick
80
+ theta += omega * env.tick
81
+
82
+ # prevent silly oscillations due to ticks or tiny floating point errors
83
+ omega = 0 if omega < 0.0001
84
+
85
+ net_torque = motor.implied_torque(alpha)
86
+ rpm = DrivingPhysics.rpm(omega)
87
+ power = DrivingPhysics.power(net_torque, omega)
88
+
89
+ if rpm > motor.idle_rpm and status == :ignition
90
+ status = :running
91
+ flag = true
92
+ end
93
+
94
+ if rpm > 7000 and status == :running
95
+ status = :off_throttle
96
+ motor.throttle = 0.0
97
+ flag = true
98
+ end
99
+
100
+ if rpm < 1000 and status == :off_throttle
101
+ status = :idling
102
+ motor.throttle = 0.06
103
+ flag = true
104
+ end
105
+
106
+ if status == :idling
107
+ if rpm < 999
108
+ motor.throttle += (1.0 - motor.throttle) * 0.005
109
+ elsif rpm > 1001
110
+ motor.throttle -= motor.throttle * 0.005
111
+ end
112
+ end
113
+
114
+ if flag or
115
+ (i < 10) or
116
+ (i < 100 and i % 10 == 0) or
117
+ (i < 1000 and i % 100 == 0) or
118
+ (i < 10_000 and i % 500 == 0) or
119
+ (i % 5000 == 0) or
120
+ (status == :idling and i % 100 == 0)
121
+ puts Timer.display(ms: i)
122
+ puts format("Throttle: %.1f%%", motor.throttle * 100)
123
+ puts format("%d RPM %.1f Nm (%d Nm) %.1f kW Friction: %.1f Nm",
124
+ DrivingPhysics.rpm(omega),
125
+ net_torque,
126
+ torque,
127
+ power / 1000,
128
+ motor.spinner.rotating_friction(omega))
129
+ puts format("%.3f rad/s/s %.2f rad/s %.1f rad", alpha, omega, theta)
130
+ puts
131
+
132
+ paused += CLI.pause if flag
133
+ flag = false
134
+ end
135
+ }
136
+
137
+ puts Timer.summary(start, num_ticks, paused)
@@ -103,7 +103,7 @@ module DrivingPhysics
103
103
  end
104
104
 
105
105
  def corner_mass
106
- Rational(self.total_mass) / @num_tires
106
+ self.total_mass / @num_tires
107
107
  end
108
108
 
109
109
  # per tire
@@ -1,5 +1,5 @@
1
1
  require 'driving_physics/environment'
2
- require 'driving_physics/vector_force'
2
+ #require 'driving_physics/vector_force'
3
3
 
4
4
  module DrivingPhysics
5
5
  # radius is always in meters
@@ -110,11 +110,11 @@ module DrivingPhysics
110
110
 
111
111
  def initialize(env)
112
112
  @env = env
113
- @radius = 350/1000r # m
114
- @width = 200/1000r # m
113
+ @radius = 0.35
114
+ @width = 0.2
115
115
  @density = DENSITY
116
- @base_friction = 5/100_000r # constant resistance to rotation
117
- @omega_friction = 5/100_000r # scales with omega
116
+ @base_friction = 5.0/100_000 # constant resistance to rotation
117
+ @omega_friction = 5.0/100_000 # scales with omega
118
118
  yield self if block_given?
119
119
  end
120
120
 
@@ -27,8 +27,8 @@ module DrivingPhysics
27
27
  # represent all rotating mass
28
28
  @spinner = Disk.new(env) { |m|
29
29
  m.radius = 0.15
30
- m.base_friction = 5/1000r
31
- m.omega_friction = 15/100_000r
30
+ m.base_friction = 5.0/1000
31
+ m.omega_friction = 15.0/100_000
32
32
  m.mass = 15
33
33
  }
34
34
  @fixed_mass = 30 # kg
@@ -25,8 +25,8 @@ module DrivingPhysics
25
25
  @spinner = Disk.new(@env) { |fly|
26
26
  fly.radius = 0.25 # m
27
27
  fly.mass = 75 # kg
28
- fly.base_friction = 5/1000r
29
- fly.omega_friction = 2/10_000r
28
+ fly.base_friction = 5.0/1000
29
+ fly.omega_friction = 2.0/10_000
30
30
  }
31
31
  @starter_torque = 500 # Nm
32
32
  @idle_rpm = 1000 # RPM
@@ -75,11 +75,8 @@ module DrivingPhysics
75
75
  raise(Stall, "RPM #{rpm}") if rpm < @rpms.first
76
76
  raise(OverRev, "RPM #{rpm}") if rpm > @rpms.last
77
77
 
78
- last_rpm = 99999
79
- last_tq = -1
80
- torque = nil
78
+ last_rpm, last_tq, torque = 99999, -1, nil
81
79
 
82
- # ew; there must be a better way
83
80
  @rpms.each_with_index { |r, i|
84
81
  tq = @torques[i]
85
82
  if last_rpm <= rpm and rpm <= r
@@ -87,8 +84,7 @@ module DrivingPhysics
87
84
  torque = last_tq + (tq - last_tq) * proportion
88
85
  break
89
86
  end
90
- last_rpm = r
91
- last_tq = tq
87
+ last_rpm, last_tq = r, tq
92
88
  }
93
89
  raise(SanityCheck, "RPM #{rpm}") if torque.nil?
94
90
 
@@ -101,3 +97,54 @@ module DrivingPhysics
101
97
  end
102
98
  end
103
99
  end
100
+
101
+
102
+ # TODO: starter motor
103
+ # Starter motor is power limited, not torque limited
104
+ # Consider:
105
+ # * 2.2 kW (3.75:1 gear reduction)
106
+ # * 1.8 kW (4.4:1 gear reduction)
107
+ # On a workbench, a starter will draw 80 to 90 amps. However, during actual
108
+ # start-up of an engine, a starter will draw 250 to 350 amps.
109
+ # from https://www.motortrend.com/how-to/because-theres-more-to-a-starter-than-you-realize/
110
+
111
+ # V - Potential, voltage
112
+ # I - Current, amperage
113
+ # R - Resistance, ohms
114
+ # P - Power, wattage
115
+
116
+ # Ohm's law: I = V / R (where R is held constant)
117
+ # P = I * V
118
+ # For resistors (where R is helod constant)
119
+ # = I^2 * R
120
+ # = V^2 / R
121
+
122
+
123
+ # torque proportional to A
124
+ # speed proportional to V
125
+
126
+
127
+ # batteries are rated in terms of CCA - cold cranking amps
128
+
129
+ # P = I * V
130
+ # V = 12 (up to 14ish)
131
+ # I = 300
132
+ # P = 3600 or 3.6 kW
133
+
134
+
135
+ # A starter rated at e.g. 2.2 kW will use more power on initial cranking
136
+ # Sometimes up to 500 amperes are required, and some batteries will provide
137
+ # over 600 cold cranking amps
138
+
139
+
140
+ # Consider:
141
+
142
+ # V = 12
143
+ # R = resistance of battery, wiring, starter motor
144
+ # L = inductance (approx 0)
145
+ # I = current through motor
146
+ # Vc = proportional to omega
147
+
148
+ # rated power - Vc * I
149
+ # input power - V * I
150
+ # input power that is unable to be converted to output power is wasted as heat
@@ -0,0 +1,45 @@
1
+ module DrivingPhysics
2
+ module CLI
3
+ # returns user input as a string
4
+ def self.prompt(msg = '')
5
+ print msg + ' ' unless msg.empty?
6
+ print '> '
7
+ $stdin.gets.chomp
8
+ end
9
+
10
+ # press Enter to continue, ignore input, return elapsed time
11
+ def self.pause(msg = '')
12
+ t = Timer.now
13
+ puts msg unless msg.empty?
14
+ puts ' [ Press Enter ]'
15
+ $stdin.gets
16
+ Timer.since(t)
17
+ end
18
+ end
19
+
20
+ module Timer
21
+ def self.now
22
+ Time.now
23
+ end
24
+
25
+ def self.since(t)
26
+ self.now - t
27
+ end
28
+
29
+ def self.elapsed(&work)
30
+ t = self.now
31
+ return yield, self.since(t)
32
+ end
33
+
34
+ # HH:MM:SS.mmm
35
+ def self.display(seconds: 0, ms: 0)
36
+ ms += (seconds * 1000).round if seconds > 0
37
+ DrivingPhysics.elapsed_display(ms)
38
+ end
39
+
40
+ def self.summary(start, num_ticks, paused = 0)
41
+ elapsed = self.since(start) - paused
42
+ format("%.3f s (%d ticks/s)", elapsed, num_ticks.to_f / elapsed)
43
+ end
44
+ end
45
+ end
@@ -26,14 +26,14 @@ module DrivingPhysics
26
26
 
27
27
  def initialize(env)
28
28
  @env = env
29
- @radius = 350/1000r # m
30
- @width = 200/1000r # m
29
+ @radius = 0.35
30
+ @width = 0.2
31
31
  @density = DENSITY
32
32
  @temp = @env.air_temp
33
- @mu_s = 11/10r # static friction
34
- @mu_k = 7/10r # kinetic friction
35
- @base_friction = 5/10_000r
36
- @omega_friction = 5/100_000r # scales with speed
33
+ @mu_s = 1.1 # static friction
34
+ @mu_k = 0.7 # kinetic friction
35
+ @base_friction = 5.0/10_000
36
+ @omega_friction = 5.0/100_000
37
37
  @roll_cof = DrivingPhysics::ROLL_COF
38
38
 
39
39
  yield self if block_given?
data/test/disk.rb CHANGED
@@ -50,6 +50,7 @@ describe D do
50
50
  inertia = D.rotational_inertia(0.35, 25.0)
51
51
  expect(D.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
52
52
 
53
+ skip # Vector
53
54
  vector_torque = Vector[0, 0, 1000]
54
55
  vector_alpha = D.alpha vector_torque, inertia
55
56
  expect(vector_alpha).must_be_instance_of Vector
@@ -60,6 +61,7 @@ describe D do
60
61
 
61
62
  describe "Disk.torque_vector" do
62
63
  it "calculates a torque in the 3rd dimension given 2D force and radius" do
64
+ skip # Vector
63
65
  force = Vector[1000, 0]
64
66
  radius = Vector[0, 5]
65
67
  torque = D.torque_vector(force, radius)
@@ -71,6 +73,7 @@ describe D do
71
73
 
72
74
  describe "Disk.force_vector" do
73
75
  it "calculates a (3D) force given 3D torque and 2D radius" do
76
+ skip # Vector
74
77
  # let's invert the Disk.torque_vector case from above:
75
78
  torque = Vector[0, 0, 5000]
76
79
  radius = Vector[0, 5]
data/test/tire.rb CHANGED
@@ -11,6 +11,7 @@ describe T do
11
11
  scalar_t = T.traction(scalar_nf, cof)
12
12
  expect(scalar_t).must_equal 10780.0
13
13
 
14
+ skip # Vector
14
15
  vector_nf = Vector[9800, 0]
15
16
  vector_t = T.traction(vector_nf, cof)
16
17
  expect(vector_t).must_equal Vector[10780.0, 0.0]
@@ -62,6 +63,7 @@ describe T do
62
63
  inertia = T.rotational_inertia(0.35, 25.0)
63
64
  expect(T.alpha scalar_torque, inertia).must_be_within_epsilon 653.061
64
65
 
66
+ skip # Vector
65
67
  vector_torque = Vector[0, 0, 1000]
66
68
  vector_alpha = T.alpha vector_torque, inertia
67
69
  expect(vector_alpha).must_be_instance_of Vector
@@ -72,6 +74,7 @@ describe T do
72
74
 
73
75
  describe "Tire.torque_vector" do
74
76
  it "calculates a torque in the 3rd dimension given 2D force and radius" do
77
+ skip # Vector
75
78
  force = Vector[1000, 0]
76
79
  radius = Vector[0, 5]
77
80
  torque = T.torque_vector(force, radius)
@@ -84,6 +87,7 @@ describe T do
84
87
  describe "Tire.force_vector" do
85
88
  it "calculates a (3D) force given 3D torque and 2D radius" do
86
89
  # let's invert the Tire.torque_vector case from above:
90
+ skip # Vector
87
91
  torque = Vector[0, 0, 5000]
88
92
  radius = Vector[0, 5]
89
93
  force = T.force_vector(torque, radius)
@@ -154,6 +158,7 @@ describe T do
154
158
  expect(@tire.traction scalar_nf).must_equal 10780.0
155
159
  expect(@tire.traction scalar_nf, static: false).must_equal 6860.0
156
160
 
161
+ skip # Vector
157
162
  vector_nf = Vector[9800, 0]
158
163
  expect(@tire.traction vector_nf).must_equal Vector[10780.0, 0.0]
159
164
  expect(@tire.traction vector_nf, static: false).
@@ -173,6 +178,7 @@ describe T do
173
178
  expect(kin_tq).must_be_within_epsilon 2401.0
174
179
 
175
180
  # not sure about how torque vectors work, but the "math" "works":
181
+ skip # Vector
176
182
  vector_nf = Vector[9800, 0]
177
183
  expect(@tire.tractable_torque(vector_nf)[0]).
178
184
  must_be_within_epsilon 3773.0
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.1.2
4
+ version: 0.0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Rick Hull
@@ -23,6 +23,8 @@ files:
23
23
  - demo/disk.rb
24
24
  - demo/gearbox.rb
25
25
  - demo/motor.rb
26
+ - demo/mruby/disk.rb
27
+ - demo/mruby/motor.rb
26
28
  - demo/powertrain.rb
27
29
  - demo/scalar_force.rb
28
30
  - demo/tire.rb
@@ -36,6 +38,7 @@ files:
36
38
  - lib/driving_physics/gearbox.rb
37
39
  - lib/driving_physics/imperial.rb
38
40
  - lib/driving_physics/motor.rb
41
+ - lib/driving_physics/mruby.rb
39
42
  - lib/driving_physics/power.rb
40
43
  - lib/driving_physics/powertrain.rb
41
44
  - lib/driving_physics/scalar_force.rb